Added Satisfactory to latest master

This commit is contained in:
Jarno Westhof
2025-01-19 13:38:26 +01:00
parent 9e353ebb8e
commit 08da8188e8
11 changed files with 3534 additions and 0 deletions

View File

@@ -0,0 +1,980 @@
from typing import Tuple, Optional, Dict, Set, List
from dataclasses import dataclass
from enum import IntEnum
class PowerInfrastructureLevel(IntEnum):
Basic = 1
Automated = 2
Advanced = 3
Complex = 4
def to_name(self):
return "Power level: " + self.name
liquids: Set[str] = {
"Water",
"Liquid Biofuel",
"Crude Oil",
"Fuel",
"Heavy Oil Residue",
"Turbofuel",
"Alumina Solution",
"Sulfuric Acid",
"Nitrogen Gas",
"Nitric Acid",
"Dissolved Silica",
"Rocket Fuel",
"Ionized Fuel",
"Excited Photonic Matter",
"Dark Matter Residue"
}
radio_actives: Set[str] = {
"Uranium",
"Encased Uranium Cell",
"Uranium Fuel Rod"
"Uranium Waste",
"Non-fissile Uranium",
"Plutonium Pellet",
"Encased Plutonium Cell",
"Plutonium Fuel Rod",
"Plutonium Waste",
"Ficsonium",
"Ficsonium Fuel Rod"
}
class Recipe():
"""
Relationship between components and what is required to produce them (input ingredients, production building, etc.)
Not all recipes are Satisfactory FGRecipes - for example, Water has a Recipe, but it's not an FGRecipe
"""
name: str
building: str
inputs: Tuple[str, ...]
minimal_belt_speed: int
handcraftable: bool
implicitly_unlocked: bool
"""No explicit location/item is needed to unlock this recipe, you have access as soon as dependencies are met (ex. Water, Leaves, tutorial starting items)"""
additional_outputs: Tuple[str, ...]
needs_pipes: bool
is_radio_active: bool
def __init__(self, name: str, building: Optional[str] = None, inputs: Optional[Tuple[str, ...]] = None,
minimal_belt_speed: int = 1, handcraftable: bool = False, implicitly_unlocked: bool = False,
additional_outputs: Optional[Tuple[str, ...]] = None):
self.name = "Recipe: " + name
self.building = building
self.inputs = inputs
self.minimal_belt_speed = minimal_belt_speed
self.handcraftable = handcraftable
self.implicitly_unlocked = implicitly_unlocked
self.additional_outputs = additional_outputs
all_parts: List[str] = [name]
if inputs:
all_parts += inputs
if additional_outputs:
all_parts += additional_outputs
self.needs_pipes = not liquids.isdisjoint(all_parts)
self.is_radio_active = not radio_actives.isdisjoint(all_parts)
class Building(Recipe):
power_requirement: Optional[PowerInfrastructureLevel]
can_produce: bool
def __init__(self, name: str, inputs: Optional[Tuple[str, ...]] = None,
power_requirement: Optional[PowerInfrastructureLevel] = None, can_produce: bool = True,
implicitly_unlocked: bool = False):
super().__init__(name, None, inputs, handcraftable=True, implicitly_unlocked=implicitly_unlocked)
self.name = "Building: " + name
self.power_requirement = power_requirement
self.can_produce = can_produce
self.implicitly_unlocked = implicitly_unlocked
class MamNode():
name: str
unlock_cost: Dict[str, int]
"""All game items must be submitted to purchase this MamNode"""
depends_on: Tuple[str, ...]
"""At least one of these prerequisite MamNodes must be unlocked to purchase this MamNode"""
def __init__(self, name: str, unlock_cost: Dict[str, int], depends_on: Tuple[str, ...]):
self.name = name
self.unlock_cost = unlock_cost
self.depends_on = depends_on
class MamTree():
access_items: Tuple[str, ...]
"""At least one of these game items must enter the player inventory for this MamTree to be available"""
nodes: Tuple[MamNode, ...]
def __init__(self, access_items: Tuple[str, ...], nodes: Tuple[MamNode, ...]):
self.access_items = access_items
self.nodes = nodes
@dataclass
class DropPodData:
x: int
y: int
z: int
item: Optional[str]
power: int
gassed: Optional[bool] = None
radioactive: Optional[bool] = None
class GameLogic:
recipes: Dict[str, Tuple[Recipe, ...]] = {
# This Dict should only contain items that are used somewhere in a logic chain
# Exploration Items
"Leaves": (
Recipe("Leaves", handcraftable=True, implicitly_unlocked=True), ),
"Wood": (
Recipe("Wood", handcraftable=True, implicitly_unlocked=True), ),
"Hatcher Remains": (
Recipe("Hatcher Remains", handcraftable=True, implicitly_unlocked=True), ),
"Hog Remains": (
Recipe("Hog Remains", handcraftable=True, implicitly_unlocked=True), ),
"Plasma Spitter Remains": (
Recipe("Plasma Spitter Remains", handcraftable=True, implicitly_unlocked=True), ),
"Stinger Remains": (
Recipe("Stinger Remains", handcraftable=True, implicitly_unlocked=True), ),
"Mycelia": (
Recipe("Mycelia", handcraftable=True, implicitly_unlocked=True), ),
"Beryl Nut": (
Recipe("Beryl Nut", handcraftable=True, implicitly_unlocked=True), ),
"Paleberry": (
Recipe("Paleberry", handcraftable=True, implicitly_unlocked=True), ),
"Bacon Agaric": (
Recipe("Bacon Agaric", handcraftable=True, implicitly_unlocked=True), ),
"Blue Power Slug": (
Recipe("Blue Power Slug", handcraftable=True, implicitly_unlocked=True), ),
"Yellow Power Slug": (
Recipe("Yellow Power Slug", handcraftable=True, implicitly_unlocked=True), ),
"Purple Power Slug": (
Recipe("Purple Power Slug", handcraftable=True, implicitly_unlocked=True), ),
"Hard Drive": (
Recipe("Hard Drive", handcraftable=True, implicitly_unlocked=True), ),
"Mercer Sphere": (
Recipe("Mercer Sphere", handcraftable=True, implicitly_unlocked=True), ),
"Somersloop": (
Recipe("Somersloop", handcraftable=True, implicitly_unlocked=True), ),
# Raw Resources
"Water": (
Recipe("Water", "Water Extractor", implicitly_unlocked=True), ),
"Limestone": (
Recipe("Limestone", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
"Raw Quartz": (
Recipe("Raw Quartz", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
"Iron Ore": (
Recipe("Iron Ore", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
"Copper Ore": (
Recipe("Copper Ore", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
"Coal": (
Recipe("Coal", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
"Sulfur": (
Recipe("Sulfur", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
"Caterium Ore": (
Recipe("Caterium Ore", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
"Crude Oil": (
Recipe("Crude Oil", "Oil Extractor", implicitly_unlocked=True), ),
"Bauxite": (
Recipe("Bauxite", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
"Nitrogen Gas": (
Recipe("Nitrogen Gas", "Resource Well Pressurizer", implicitly_unlocked=True), ),
"Uranium": (
Recipe("Uranium", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
# Special Items
"Uranium Waste": (
Recipe("Uranium Waste", "Nuclear Power Plant", ("Uranium Fuel Rod", "Water"), implicitly_unlocked=True), ),
#"Plutonium Waste": (
# Recipe("Plutonium Waste", "Nuclear Power Plant", ("Plutonium Fuel Rod", "Water"), implicitly_unlocked=True), ),
# Recipes
"Reinforced Iron Plate": (
Recipe("Reinforced Iron Plate", "Assembler", ("Iron Plate", "Screw"), handcraftable=True, implicitly_unlocked=True),
Recipe("Adhered Iron Plate", "Assembler", ("Iron Plate", "Rubber")),
Recipe("Bolted Iron Plate", "Assembler", ("Iron Plate", "Screw"), minimal_belt_speed=3),
Recipe("Stitched Iron Plate", "Assembler", ("Iron Plate", "Wire"))),
"Rotor": (
Recipe("Rotor", "Assembler", ("Iron Rod", "Screw"), minimal_belt_speed=2, handcraftable=True),
Recipe("Copper Rotor", "Assembler", ("Copper Sheet", "Screw"), minimal_belt_speed=3),
Recipe("Steel Rotor", "Assembler", ("Steel Pipe", "Wire"))),
"Stator": (
Recipe("Stator", "Assembler", ("Steel Pipe", "Wire"), handcraftable=True),
Recipe("Quickwire Stator", "Assembler", ("Steel Pipe", "Quickwire"))),
"Plastic": (
Recipe("Plastic", "Refinery", ("Crude Oil", ), additional_outputs=("Heavy Oil Residue", )),
Recipe("Residual Plastic", "Refinery", ("Polymer Resin", "Water")),
Recipe("Recycled Plastic", "Refinery", ("Rubber", "Fuel"))),
"Rubber": (
Recipe("Rubber", "Refinery", ("Crude Oil", ), additional_outputs=("Heavy Oil Residue", )),
Recipe("Residual Rubber", "Refinery", ("Polymer Resin", "Water")),
Recipe("Recycled Rubber", "Refinery", ("Plastic", "Fuel"))),
"Iron Plate": (
Recipe("Iron Plate", "Constructor", ("Iron Ingot", ), handcraftable=True, implicitly_unlocked=True),
Recipe("Coated Iron Plate", "Assembler", ("Iron Ingot", "Plastic"), minimal_belt_speed=2),
Recipe("Steel Cast Plate", "Foundry", ("Iron Ingot", "Steel Ingot"))),
"Iron Rod": (
Recipe("Iron Rod", "Constructor", ("Iron Ingot", ), handcraftable=True, implicitly_unlocked=True),
Recipe("Steel Rod", "Constructor", ("Steel Ingot", )),
Recipe("Aluminum Rod", "Constructor", ("Aluminum Ingot", ))),
"Screw": (
Recipe("Screw", "Constructor", ("Iron Rod", ), handcraftable=True, implicitly_unlocked=True),
Recipe("Cast Screw", "Constructor", ("Iron Ingot", )),
Recipe("Steel Screw", "Constructor", ("Steel Beam", ), minimal_belt_speed=3)),
"Wire": (
Recipe("Wire", "Constructor", ("Copper Ingot", ), handcraftable=True, implicitly_unlocked=True),
Recipe("Fused Wire", "Assembler", ("Copper Ingot", "Caterium Ingot"), minimal_belt_speed=2),
Recipe("Iron Wire", "Constructor", ("Iron Ingot", )),
Recipe("Caterium Wire", "Constructor", ("Caterium Ingot", ), minimal_belt_speed=2)),
"Cable": (
Recipe("Cable", "Constructor", ("Wire", ), handcraftable=True, implicitly_unlocked=True),
Recipe("Coated Cable", "Refinery", ("Wire", "Heavy Oil Residue"), minimal_belt_speed=2),
Recipe("Insulated Cable", "Assembler", ("Wire", "Rubber"), minimal_belt_speed=2),
Recipe("Quickwire Cable", "Assembler", ("Quickwire", "Rubber"))),
"Quickwire": (
Recipe("Quickwire", "Constructor", ("Caterium Ingot", ), handcraftable=True),
Recipe("Fused Quickwire", "Assembler", ("Caterium Ingot", "Copper Ingot"), minimal_belt_speed=2)),
"Copper Sheet": (
Recipe("Copper Sheet", "Constructor", ("Copper Ingot", ), handcraftable=True),
Recipe("Steamed Copper Sheet", "Refinery", ("Copper Ingot", "Water"))),
"Steel Pipe": (
Recipe("Steel Pipe", "Constructor", ("Steel Ingot", ), handcraftable=True),
Recipe("Iron Pipe", "Constructor", ("Iron Ingot", ), minimal_belt_speed=2),
Recipe("Molded Steel Pipe", "Foundry", ("Steel Ingot", "Concrete"))),
"Steel Beam": (
Recipe("Steel Beam", "Constructor", ("Steel Ingot", ), handcraftable=True),
Recipe("Aluminum Beam", "Constructor", ("Aluminum Ingot", )),
Recipe("Molded Beam", "Foundry", ("Steel Ingot", "Concrete"), minimal_belt_speed=2)),
"Heavy Oil Residue": (
Recipe("Heavy Oil Residue", "Refinery", ("Crude Oil", ), additional_outputs=("Polymer Resin", )),
Recipe("Plastic", "Refinery", ("Crude Oil", ), additional_outputs=("Plastic", )),
Recipe("Rubber", "Refinery", ("Crude Oil", ), additional_outputs=("Rubber", )),
Recipe("Polymer Resin", "Refinery", ("Crude Oil", ), additional_outputs=("Polymer Resin", ), minimal_belt_speed=3)),
"Polymer Resin": (
Recipe("Polymer Resin", "Refinery", ("Crude Oil", ), additional_outputs=("Heavy Oil Residue", ), minimal_belt_speed=2),
Recipe("Fuel", "Refinery", ("Crude Oil", ), additional_outputs=("Fuel", )),
Recipe("Heavy Oil Residue", "Refinery", ("Crude Oil", ), additional_outputs=("Heavy Oil Residue", ), minimal_belt_speed=3)),
"Fuel": (
Recipe("Fuel", "Refinery", ("Crude Oil", ), additional_outputs=("Polymer Resin", )),
Recipe("Diluted Fuel", "Blender", ("Heavy Oil Residue", "Water")),
Recipe("Residual Fuel", "Refinery", ("Heavy Oil Residue", ))),
"Concrete": (
Recipe("Concrete", "Constructor", ("Limestone", ), handcraftable=True, implicitly_unlocked=True),
Recipe("Fine Concrete", "Assembler", ("Limestone", "Silica")),
Recipe("Rubber Concrete", "Assembler", ("Limestone", "Rubber")),
Recipe("Wet Concrete", "Refinery", ("Limestone", "Water"), minimal_belt_speed=2)),
"Silica": (
Recipe("Silica", "Constructor", ("Raw Quartz", ), handcraftable=True),
Recipe("Alumina Solution", "Refinery", ("Bauxite", "Water"), additional_outputs=("Alumina Solution", ), minimal_belt_speed=2),
Recipe("Cheap Silica", "Assembler", ("Raw Quartz", "Limestone")),
Recipe("Distilled Silica", "Blender", ("Dissolved Silica", "Limestone", "Water"), additional_outputs=("Water", ))),
"Dissolved Silica": (
Recipe("Quartz Purification", "Refinery", ("Raw Quartz", "Nitric Acid"), additional_outputs=("Quartz Crystal", ), minimal_belt_speed=2), ),
"Quartz Crystal": (
Recipe("Quartz Crystal", "Constructor", ("Raw Quartz", ), handcraftable=True),
Recipe("Pure Quartz Crystal", "Refinery", ("Raw Quartz", "Water"), minimal_belt_speed=2),
Recipe("Fused Quartz Crystal", "Foundry", ("Raw Quartz", "Coal"), minimal_belt_speed=2),
Recipe("Quartz Purification", "Refinery", ("Raw Quartz", "Nitric Acid"), additional_outputs=("Dissolved Silica", ), minimal_belt_speed=2)),
"Iron Ingot": (
Recipe("Iron Ingot", "Smelter", ("Iron Ore", ), handcraftable=True, implicitly_unlocked=True),
Recipe("Pure Iron Ingot", "Refinery", ("Iron Ore", "Water"), minimal_belt_speed=2),
Recipe("Iron Alloy Ingot", "Foundry", ("Iron Ore", "Copper Ore")),
Recipe("Basic Iron Ingot", "Foundry", ("Iron Ore", "Limestone")),
Recipe("Leached Iron ingot", "Refinery", ("Iron Ore", "Sulfuric Acid"), minimal_belt_speed=2)),
"Steel Ingot": (
Recipe("Steel Ingot", "Foundry", ("Iron Ore", "Coal"), handcraftable=True),
Recipe("Coke Steel Ingot", "Foundry", ("Iron Ore", "Petroleum Coke"), minimal_belt_speed=2),
Recipe("Compacted Steel Ingot", "Foundry", ("Iron Ore", "Compacted Coal")),
Recipe("Solid Steel Ingot", "Foundry", ("Iron Ingot", "Coal"))),
"Copper Ingot": (
Recipe("Copper Ingot", "Smelter", ("Copper Ore", ), handcraftable=True, implicitly_unlocked=True),
Recipe("Copper Alloy Ingot", "Foundry", ("Copper Ore", "Iron Ore"), minimal_belt_speed=2),
Recipe("Pure Copper Ingot", "Refinery", ("Copper Ore", "Water")),
Recipe("Leached Copper Ingot", "Refinery", ("Copper Ore", "Sulfuric Acid"), minimal_belt_speed=2),
Recipe("Tempered Copper Ingot", "Foundry", ("Copper Ore", "Petroleum Coke"))),
"Caterium Ingot": (
Recipe("Caterium Ingot", "Smelter", ("Caterium Ore", ), handcraftable=True),
Recipe("Pure Caterium Ingot", "Refinery", ("Caterium Ore", "Water")),
Recipe("Leached Caterium Ingot", "Refinery", ("Caterium Ore", "Sulfuric Acid")),
Recipe("Tempered Caterium Ingot", "Foundry", ("Caterium Ore", "Petroleum Coke"))),
"Petroleum Coke": (
Recipe("Petroleum Coke", "Refinery", ("Heavy Oil Residue", ), minimal_belt_speed=2), ),
"Compacted Coal": (
Recipe("Compacted Coal", "Assembler", ("Coal", "Sulfur")), ),
"Motor": (
Recipe("Motor", "Assembler", ("Rotor", "Stator"), handcraftable=True),
Recipe("Rigor Motor", "Manufacturer", ("Rotor", "Stator", "Crystal Oscillator")),
Recipe("Electric Motor", "Assembler", ("Electromagnetic Control Rod", "Rotor"))),
"Modular Frame": (
Recipe("Modular Frame", "Assembler", ("Reinforced Iron Plate", "Iron Rod"), handcraftable=True),
Recipe("Bolted Frame", "Assembler", ("Reinforced Iron Plate", "Screw"), minimal_belt_speed=3),
Recipe("Steeled Frame", "Assembler", ("Reinforced Iron Plate", "Steel Pipe"))),
"Heavy Modular Frame": (
Recipe("Heavy Modular Frame", "Manufacturer", ("Modular Frame", "Steel Pipe", "Encased Industrial Beam", "Screw"), minimal_belt_speed=3, handcraftable=True),
Recipe("Heavy Flexible Frame", "Manufacturer", ("Modular Frame", "Encased Industrial Beam", "Rubber", "Screw"), minimal_belt_speed=4),
Recipe("Heavy Encased Frame", "Manufacturer", ("Modular Frame", "Encased Industrial Beam", "Steel Pipe", "Concrete"))),
"Encased Industrial Beam": (
Recipe("Encased Industrial Beam", "Assembler", ("Steel Beam", "Concrete"), handcraftable=True),
Recipe("Encased Industrial Pipe", "Assembler", ("Steel Pipe", "Concrete"))),
"Computer": (
Recipe("Computer", "Manufacturer", ("Circuit Board", "Cable", "Plastic"), minimal_belt_speed=3, handcraftable=True),
Recipe("Crystal Computer", "Assembler", ("Circuit Board", "Crystal Oscillator")),
Recipe("Caterium Computer", "Manufacturer", ("Circuit Board", "Quickwire", "Rubber"), minimal_belt_speed=2)),
"Circuit Board": (
Recipe("Circuit Board", "Assembler", ("Copper Sheet", "Plastic"), handcraftable=True),
Recipe("Electrode Circuit Board", "Assembler", ("Rubber", "Petroleum Coke")),
Recipe("Silicon Circuit Board", "Assembler", ("Copper Sheet", "Silica")),
Recipe("Caterium Circuit Board", "Assembler", ("Plastic", "Quickwire"))),
"Crystal Oscillator": (
Recipe("Crystal Oscillator", "Manufacturer", ("Quartz Crystal", "Cable", "Reinforced Iron Plate"), handcraftable=True),
Recipe("Insulated Crystal Oscillator", "Manufacturer", ("Quartz Crystal", "Rubber", "AI Limiter"))),
"AI Limiter": (
Recipe("AI Limiter", "Assembler", ("Copper Sheet", "Quickwire"), minimal_belt_speed=2, handcraftable=True),
Recipe("Plastic AI Limiter", "Assembler", ("Quickwire", "Plastic"), minimal_belt_speed=2)),
"Electromagnetic Control Rod": (
Recipe("Electromagnetic Control Rod", "Assembler", ("Stator", "AI Limiter"), handcraftable=True),
Recipe("Electromagnetic Connection Rod", "Assembler", ("Stator", "High-Speed Connector"))),
"High-Speed Connector": (
Recipe("High-Speed Connector", "Manufacturer", ("Quickwire", "Cable", "Circuit Board"), minimal_belt_speed=3, handcraftable=True),
Recipe("Silicon High-Speed Connector", "Manufacturer", ("Quickwire", "Silica", "Circuit Board"), minimal_belt_speed=2)),
"Smart Plating": (
Recipe("Smart Plating", "Assembler", ("Reinforced Iron Plate", "Rotor")),
Recipe("Plastic Smart Plating", "Manufacturer", ("Reinforced Iron Plate", "Rotor", "Plastic"))),
"Versatile Framework": (
Recipe("Versatile Framework", "Assembler", ("Modular Frame", "Steel Beam")),
Recipe("Flexible Framework", "Manufacturer", ("Modular Frame", "Steel Beam", "Rubber"))),
"Automated Wiring": (
Recipe("Automated Wiring", "Assembler", ("Stator", "Cable")),
Recipe("Automated Speed Wiring", "Manufacturer", ("Stator", "Wire", "High-Speed Connector"), minimal_belt_speed=2)),
"Modular Engine": (
Recipe("Modular Engine", "Manufacturer", ("Motor", "Rubber", "Smart Plating")), ),
"Adaptive Control Unit": (
Recipe("Adaptive Control Unit", "Manufacturer", ("Automated Wiring", "Circuit Board", "Heavy Modular Frame", "Computer")), ),
"Portable Miner": (
Recipe("Portable Miner", "Equipment Workshop", ("Iron Rod", "Iron Plate"), handcraftable=True, minimal_belt_speed=0, implicitly_unlocked=True),
Recipe("Automated Miner", "Manufacturer", ("Steel Pipe", "Iron Plate")), ),
"Alumina Solution": (
Recipe("Alumina Solution", "Refinery", ("Bauxite", "Water"), additional_outputs=("Silica", ), minimal_belt_speed=2),
Recipe("Sloppy Alumina", "Refinery", ("Bauxite", "Water"), minimal_belt_speed=3)),
"Aluminum Scrap": (
Recipe("Aluminum Scrap", "Refinery", ("Alumina Solution", "Coal"), additional_outputs=("Water", ), minimal_belt_speed=4),
Recipe("Electrode Aluminum Scrap", "Refinery", ("Alumina Solution", "Petroleum Coke"), additional_outputs=("Water", ), minimal_belt_speed=4),
Recipe("Instant Scrap", "Blender", ("Bauxite", "Coal", "Sulfuric Acid", "Water"), additional_outputs=("Water", ), minimal_belt_speed=3)),
"Aluminum Ingot": (
Recipe("Aluminum Ingot", "Foundry", ("Aluminum Scrap", "Silica"), minimal_belt_speed=2, handcraftable=True),
Recipe("Pure Aluminum Ingot", "Smelter", ("Aluminum Scrap", ))),
"Alclad Aluminum Sheet": (
Recipe("Alclad Aluminum Sheet", "Assembler", ("Aluminum Ingot", "Copper Ingot"), handcraftable=True), ),
"Aluminum Casing": (
Recipe("Aluminum Casing", "Constructor", ("Alclad Aluminum Sheet", ), handcraftable=True),
Recipe("Alclad Casing", "Assembler", ("Aluminum Ingot", "Copper Ingot"))),
"Heat Sink": (
Recipe("Heat Sink", "Assembler", ("Alclad Aluminum Sheet", "Silica"), minimal_belt_speed=2, handcraftable=True),
Recipe("Heat Exchanger", "Assembler", ("Aluminum Casing", "Rubber"), minimal_belt_speed=3)),
"Nitric Acid": (
Recipe("Nitric Acid", "Blender", ("Nitrogen Gas", "Water", "Iron Plate")), ),
"Fused Modular Frame": (
Recipe("Fused Modular Frame", "Blender", ("Heavy Modular Frame", "Aluminum Casing", "Nitrogen Gas"), minimal_belt_speed=2),
Recipe("Heat-Fused Frame", "Blender", ("Heavy Modular Frame", "Aluminum Ingot", "Nitric Acid", "Fuel"), minimal_belt_speed=3)),
"Radio Control Unit": (
Recipe("Radio Control Unit", "Manufacturer", ("Aluminum Casing", "Crystal Oscillator", "Computer"), handcraftable=True),
Recipe("Radio Connection Unit", "Manufacturer", ("Heat Sink", "High-Speed Connector", "Quartz Crystal")),
Recipe("Radio Control System", "Manufacturer", ("Crystal Oscillator", "Circuit Board", "Aluminum Casing", "Rubber"), minimal_belt_speed=2)),
"Pressure Conversion Cube": (
Recipe("Pressure Conversion Cube", "Assembler", ("Fused Modular Frame", "Radio Control Unit"), handcraftable=True), ),
"Cooling System": (
Recipe("Cooling System", "Blender", ("Heat Sink", "Rubber", "Water", "Nitrogen Gas")),
Recipe("Cooling Device", "Blender", ("Heat Sink", "Motor", "Nitrogen Gas"))),
"Turbo Motor": (
Recipe("Turbo Motor", "Manufacturer", ("Cooling System", "Radio Control Unit", "Motor", "Rubber"), handcraftable=True),
Recipe("Turbo Electric Motor", "Manufacturer", ("Motor", "Radio Control Unit", "Electromagnetic Control Rod", "Rotor")),
Recipe("Turbo Pressure Motor", "Manufacturer", ("Motor", "Pressure Conversion Cube", "Packaged Nitrogen Gas", "Stator"))),
"Battery": (
Recipe("Battery", "Blender", ("Sulfuric Acid", "Alumina Solution", "Aluminum Casing"), additional_outputs=("Water", )),
Recipe("Classic Battery", "Manufacturer", ("Sulfur", "Alclad Aluminum Sheet", "Plastic", "Wire"), minimal_belt_speed=2)),
"Supercomputer": (
Recipe("Supercomputer", "Manufacturer", ("Computer", "AI Limiter", "High-Speed Connector", "Plastic"), handcraftable=True),
Recipe("OC Supercomputer", "Assembler", ("Radio Control Unit", "Cooling System")),
Recipe("Super-State Computer", "Manufacturer", ("Computer", "Electromagnetic Control Rod", "Battery", "Wire"))),
"Sulfuric Acid": (
Recipe("Sulfuric Acid", "Refinery", ("Sulfur", "Water")), ),
"Encased Uranium Cell": (
Recipe("Encased Uranium Cell", "Blender", ("Uranium", "Concrete", "Sulfuric Acid"), additional_outputs=("Sulfuric Acid", )),
Recipe("Infused Uranium Cell", "Manufacturer", ("Uranium", "Silica", "Sulfur", "Quickwire"), minimal_belt_speed=2)),
"Uranium Fuel Rod": (
Recipe("Uranium Fuel Rod", "Manufacturer", ("Encased Uranium Cell", "Encased Industrial Beam", "Electromagnetic Control Rod")),
Recipe("Uranium Fuel Unit", "Manufacturer", ("Encased Uranium Cell", "Electromagnetic Control Rod", "Crystal Oscillator", "Rotor"))),
#"Non-fissile Uranium": (
# Recipe("Non-fissile Uranium", "Blender", ("Uranium Waste", "Silica", "Nitric Acid", "Sulfuric Acid"), additional_outputs=("Water", )),
# Recipe("Fertile Uranium", "Blender", ("Uranium", "Uranium Waste", "Nitric Acid", "Sulfuric Acid"), additional_outputs=("Water", ), minimal_belt_speed=2)),
#"Plutonium Pellet": (
# Recipe("Plutonium Pellet", "Particle Accelerator", ("Non-fissile Uranium", "Uranium Waste"), minimal_belt_speed=2), ),
#"Encased Plutonium Cell": (
# Recipe("Encased Plutonium Cell", "Assembler", ("Plutonium Pellet", "Concrete")),
# Recipe("Instant Plutonium Cell", "Particle Accelerator", ("Non-fissile Uranium", "Aluminum Casing"), minimal_belt_speed=2)),
#"Plutonium Fuel Rod": (
# Recipe("Plutonium Fuel Rod", "Manufacturer", ("Encased Plutonium Cell", "Steel Beam", "Electromagnetic Control Rod", "Heat Sink")),
# Recipe("Plutonium Fuel Unit", "Assembler", ("Encased Plutonium Cell", "Pressure Conversion Cube"))),
"Gas Filter": (
Recipe("Gas Filter", "Manufacturer", ("Coal", "Rubber", "Fabric"), handcraftable=True), ),
"Iodine Infused Filter": (
Recipe("Iodine Infused Filter", "Manufacturer", ("Gas Filter", "Quickwire", "Aluminum Casing"), handcraftable=True), ),
"Hazmat Suit": (
Recipe("Hazmat Suit", "Equipment Workshop", ("Rubber", "Plastic", "Fabric", "Alclad Aluminum Sheet"), handcraftable=True, minimal_belt_speed=0), ),
"Assembly Director System": (
Recipe("Assembly Director System", "Assembler", ("Adaptive Control Unit", "Supercomputer")), ),
"Magnetic Field Generator": (
Recipe("Magnetic Field Generator", "Assembler", ("Versatile Framework", "Electromagnetic Control Rod")), ),
"Copper Powder": (
Recipe("Copper Powder", "Constructor", ("Copper Ingot", ), handcraftable=True), ),
"Nuclear Pasta": (
Recipe("Nuclear Pasta", "Particle Accelerator", ("Copper Powder", "Pressure Conversion Cube")), ),
"Thermal Propulsion Rocket": (
Recipe("Thermal Propulsion Rocket", "Manufacturer", ("Modular Engine", "Turbo Motor", "Cooling System", "Fused Modular Frame")), ),
"Alien Protein": (
Recipe("Hatcher Protein", "Constructor", ("Hatcher Remains", ), handcraftable=True),
Recipe("Hog Protein", "Constructor", ("Hog Remains", ), handcraftable=True),
Recipe("Spitter Protein", "Constructor", ("Plasma Spitter Remains", ), handcraftable=True),
Recipe("Stinger Protein", "Constructor", ("Stinger Remains", ), handcraftable=True)),
"Biomass": (
Recipe("Biomass (Leaves)", "Constructor", ("Leaves", ), minimal_belt_speed=2, handcraftable=True, implicitly_unlocked=True),
Recipe("Biomass (Wood)", "Constructor", ("Wood", ), minimal_belt_speed=4, handcraftable=True, implicitly_unlocked=True),
Recipe("Biomass (Mycelia)", "Constructor", ("Mycelia", ), minimal_belt_speed=3, handcraftable=True),
Recipe("Biomass (Alien Protein)", "Constructor", ("Alien Protein", ), minimal_belt_speed=5, handcraftable=True)),
"Fabric": (
Recipe("Fabric", "Assembler", ("Biomass", "Mycelia"), handcraftable=True, minimal_belt_speed=2),
Recipe("Polyester Fabric", "Refinery", ("Polymer Resin", "Water"))),
"Solid Biofuel": (
Recipe("Solid Biofuel", "Constructor", ("Biomass", ), minimal_belt_speed=2, handcraftable=True), ),
"Liquid Biofuel": (
Recipe("Liquid Biofuel", "Refinery", ("Solid Biofuel", "Water"), minimal_belt_speed=2), ),
"Empty Canister": (
Recipe("Empty Canister", "Constructor", ("Plastic", ), handcraftable=True),
Recipe("Coated Iron Canister", "Assembler", ("Iron Plate", "Copper Sheet")),
Recipe("Steel Canister", "Constructor", ("Steel Ingot", ))),
"Empty Fluid Tank": (
Recipe("Empty Fluid Tank", "Constructor", ("Aluminum Ingot", ), handcraftable=True), ),
"Packaged Alumina Solution": (
Recipe("Packaged Alumina Solution", "Packager", ("Alumina Solution", "Empty Canister"), minimal_belt_speed=2), ),
"Packaged Fuel": (
Recipe("Packaged Fuel", "Packager", ("Fuel", "Empty Canister")),
Recipe("Diluted Packaged Fuel", "Refinery", ("Heavy Oil Residue", "Packaged Water"))),
"Packaged Heavy Oil Residue": (
Recipe("Packaged Heavy Oil Residue", "Packager", ("Heavy Oil Residue", "Empty Canister")), ),
"Packaged Liquid Biofuel": (
Recipe("Packaged Liquid Biofuel", "Packager", ("Liquid Biofuel", "Empty Canister")), ),
"Packaged Nitric Acid": (
Recipe("Packaged Nitric Acid", "Packager", ("Nitric Acid", "Empty Fluid Tank")), ),
"Packaged Nitrogen Gas": (
Recipe("Packaged Nitrogen Gas", "Packager", ("Nitrogen Gas", "Empty Fluid Tank")), ),
"Packaged Oil": (
Recipe("Packaged Oil", "Packager", ("Crude Oil", "Empty Fluid Tank")), ),
"Packaged Sulfuric Acid": (
Recipe("Packaged Sulfuric Acid", "Packager", ("Sulfuric Acid", "Empty Fluid Tank")), ),
"Packaged Turbofuel": (
Recipe("Packaged Turbofuel", "Packager", ("Turbofuel", "Empty Fluid Tank")), ),
"Packaged Water": (
Recipe("Packaged Water", "Packager", ("Water", "Empty Fluid Tank")), ),
"Turbofuel": (
Recipe("Turbofuel", "Refinery", ("Fuel", "Compacted Coal")),
Recipe("Turbo Heavy Fuel", "Refinery", ("Heavy Oil Residue", "Compacted Coal")),
Recipe("Turbo Blend Fuel", "Blender", ("Fuel", "Heavy Oil Residue", "Sulfur", "Petroleum Coke"))),
"Gas Mask": (
Recipe("Gas Mask", "Equipment Workshop", ("Rubber", "Plastic", "Fabric"), handcraftable=True, minimal_belt_speed=0), ),
"Alien DNA Capsule": (
Recipe("Alien DNA Capsule", "Constructor", ("Alien Protein", ), handcraftable=True), ),
"Black Powder": (
Recipe("Black Powder", "Assembler", ("Coal", "Sulfur"), handcraftable=True),
Recipe("Fine Black Powder", "Assembler", ("Sulfur", "Compacted Coal"))),
"Smokeless Powder": (
Recipe("Smokeless Powder", "Refinery", ("Black Powder", "Heavy Oil Residue")), ),
"Rifle Ammo": (
Recipe("Rifle Ammo", "Assembler", ("Copper Sheet", "Smokeless Powder"), handcraftable=True, minimal_belt_speed=2), ),
"Iron Rebar": (
Recipe("Iron Rebar", "Constructor", ("Iron Rod", ), handcraftable=True), ),
"Nobelisk": (
Recipe("Nobelisk", "Assembler", ("Black Powder", "Steel Pipe"), handcraftable=True), ),
"Power Shard": (
Recipe("Power Shard (1)", "Constructor", ("Blue Power Slug", ), handcraftable=True),
Recipe("Power Shard (2)", "Constructor", ("Yellow Power Slug", ), handcraftable=True),
Recipe("Power Shard (5)", "Constructor", ("Purple Power Slug", ), handcraftable=True),
Recipe("Synthetic Power Shard", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Time Crystal", "Dark Matter Crystal", "Quartz Crystal"))), # 1.0
"Object Scanner": (
Recipe("Object Scanner", "Equipment Workshop", ("Reinforced Iron Plate", "Wire", "Screw"), handcraftable=True), ),
"Xeno-Zapper": (
Recipe("Xeno-Zapper", "Equipment Workshop", ("Iron Rod", "Reinforced Iron Plate", "Cable", "Wire"), handcraftable=True, implicitly_unlocked=True), ),
#1.0
"Rocket Fuel": (
Recipe("Rocket Fuel", "Blender", ("Turbofuel", "Nitric Acid"), additional_outputs=("Compacted Coal", )),
Recipe("Nitro Rocket Fuel", "Blender", ("Fuel", "Nitrogen Gas", "Sulfur", "Coal"), minimal_belt_speed=2, additional_outputs=("Compacted Coal", ))),
#"Ionized Fuel": (
# Recipe("Ionized Fuel", "Refinery", ("Rocket Fuel", "Power Shard"), additional_outputs=("Compacted Coal", )), ),
"Packaged Rocket Fuel": (
Recipe("Packaged Rocket Fuel", "Packager", ("Rocket Fuel", "Empty Fluid Tank")), ),
#"Packaged Ionized Fuel": (
# Recipe("Packaged Ionized Fuel", "Packager", ("Ionized Fuel", "Empty Fluid Tank")), ),
"Diamonds": (
Recipe("Diamonds", "Particle Accelerator", ("Coal", ), minimal_belt_speed=5),
Recipe("Cloudy Diamonds", "Particle Accelerator", ("Coal", "Limestone"), minimal_belt_speed=4),
Recipe("Oil-Based Diamonds", "Particle Accelerator", ("Crude Oil", )),
Recipe("Petroleum Diamonds", "Particle Accelerator", ("Petroleum Coke", ), minimal_belt_speed=5),
Recipe("Pink Diamonds", "Converter", ("Coal", "Quartz Crystal"), minimal_belt_speed=2),
Recipe("Turbo Diamonds", "Particle Accelerator", ("Coal", "Packaged Turbofuel"), minimal_belt_speed=5)),
"Time Crystal": (
Recipe("Time Crystal", "Converter", ("Diamonds", )), ),
"Ficsite Ingot": (
Recipe("Ficsite Ingot (Aluminum)", "Converter", ("Reanimated SAM", "Aluminum Ingot"), minimal_belt_speed=2),
Recipe("Ficsite Ingot (Caterium)", "Converter", ("Reanimated SAM", "Caterium Ingot")),
Recipe("Ficsite Ingot (Iron)", "Converter", ("Reanimated SAM", "Iron Ingot"), minimal_belt_speed=3)),
"Ficsite Trigon": (
Recipe("Ficsite Trigon", "Constructor", ("Ficsite Ingot", ), handcraftable=True), ),
"SAM": (
Recipe("SAM", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
"Reanimated SAM": (
Recipe("Reanimated SAM", "Constructor", ("SAM", ), handcraftable=True, minimal_belt_speed=2), ),
"SAM Fluctuator": (
Recipe("SAM Fluctuator", "Manufacturer", ("Reanimated SAM", "Steel Pipe", "Wire"), handcraftable=True), ),
"Excited Photonic Matter": (
Recipe("Excited Photonic Matter", "Converter", implicitly_unlocked=True), ),
"Dark Matter Crystal": (
Recipe("Dark Matter Crystal", "Particle Accelerator", ("Diamonds", ), additional_outputs=("Dark Matter Residue", )),
Recipe("Dark Matter Crystallization", "Particle Accelerator", additional_outputs=("Dark Matter Residue", )),
Recipe("Dark Matter Trap", "Particle Accelerator", ("Time Crystal", ), additional_outputs=("Dark Matter Residue", ))),
"Singularity Cell": (
Recipe("Singularity Cell", "Manufacturer", ("Nuclear Pasta", "Dark Matter Crystal", "Iron Plate", "Concrete"), minimal_belt_speed=3), ),
"Biochemical Sculptor": (
Recipe("Biochemical Sculptor", "Blender", ("Assembly Director System", "Ficsite Trigon", "Water")), ),
"Ballistic Warp Drive": (
Recipe("Ballistic Warp Drive", "Manufacturer", ("Thermal Propulsion Rocket", "Singularity Cell", "Superposition Oscillator", "Dark Matter Crystal")), ),
# All Quantum Encoder recipes have `Dark Matter Residue` set as an input, this hack makes the logic make sure you can get rid of it
"Dark Matter Residue": (
#Recipe("Ficsonium", "Particle Accelerator", ("Plutonium Waste", "Singularity Cell"), additional_outputs=("Ficsonium", )),
Recipe("Dark Matter Crystal", "Particle Accelerator", ("Diamonds", ), additional_outputs=("Dark Matter Crystal", )),
Recipe("Dark Matter Crystallization", "Particle Accelerator", additional_outputs=("Dark Matter Crystal", )),
Recipe("Dark Matter Trap", "Particle Accelerator", ("Time Crystal", ), additional_outputs=("Dark Matter Crystal", ))),
"Superposition Oscillator": (
Recipe("Superposition Oscillator", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Dark Matter Crystal", "Crystal Oscillator", "Alclad Aluminum Sheet")), ),
"Neural-Quantum Processor": (
Recipe("Neural-Quantum Processor", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Time Crystal", "Supercomputer", "Ficsite Trigon")), ),
"AI Expansion Server": (
Recipe("AI Expansion Server", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Magnetic Field Generator", "Neural-Quantum Processor", "Superposition Oscillator")), ),
###
#1.0
# TODO transport types aren't currently in logic
}
buildings: Dict[str, Building] = {
"Constructor": Building("Constructor", ("Reinforced Iron Plate", "Cable"), PowerInfrastructureLevel.Basic, implicitly_unlocked=True),
"Assembler": Building("Assembler", ("Reinforced Iron Plate", "Rotor", "Cable"), PowerInfrastructureLevel.Basic),
"Manufacturer": Building("Manufacturer", ("Motor", "Heavy Modular Frame", "Cable", "Plastic"), PowerInfrastructureLevel.Advanced),
"Packager": Building("Packager", ("Steel Beam", "Rubber", "Plastic"), PowerInfrastructureLevel.Basic),
"Refinery": Building("Refinery", ("Motor", "Encased Industrial Beam", "Steel Pipe", "Copper Sheet"), PowerInfrastructureLevel.Automated),
"Blender": Building("Blender", ("Motor", "Heavy Modular Frame", "Aluminum Casing", "Radio Control Unit"), PowerInfrastructureLevel.Complex),
"Particle Accelerator": Building("Particle Accelerator", ("Radio Control Unit", "Electromagnetic Control Rod", "Supercomputer", "Cooling System", "Fused Modular Frame", "Turbo Motor"), PowerInfrastructureLevel.Complex),
"Biomass Burner": Building("Biomass Burner", ("Iron Plate", "Iron Rod", "Wire"), implicitly_unlocked=True),
"Coal Generator": Building("Coal Generator", ("Reinforced Iron Plate", "Rotor", "Cable")),
"Fuel Generator": Building("Fuel Generator", ("Computer", "Heavy Modular Frame", "Motor", "Rubber", "Quickwire")),
"Geothermal Generator": Building("Geothermal Generator", ("Motor", "Modular Frame", "High-Speed Connector", "Copper Sheet", "Wire")),
"Nuclear Power Plant": Building("Nuclear Power Plant", ("Concrete", "Heavy Modular Frame", "Supercomputer", "Cable", "Alclad Aluminum Sheet")),
"Miner Mk.1": Building("Miner Mk.1", ("Iron Plate", "Concrete"), PowerInfrastructureLevel.Basic, implicitly_unlocked=True),
"Miner Mk.2": Building("Miner Mk.2", ("Encased Industrial Beam", "Steel Pipe", "Modular Frame"), PowerInfrastructureLevel.Automated),
"Miner Mk.3": Building("Miner Mk.3", ("Steel Pipe", "Supercomputer", "Fused Modular Frame", "Turbo Motor"), PowerInfrastructureLevel.Advanced),
"Oil Extractor": Building("Oil Extractor", ("Motor", "Encased Industrial Beam", "Cable")),
"Water Extractor": Building("Water Extractor", ("Copper Sheet", "Reinforced Iron Plate", "Rotor")),
"Smelter": Building("Smelter", ("Iron Rod", "Wire"), PowerInfrastructureLevel.Basic, implicitly_unlocked=True),
"Foundry": Building("Foundry", ("Modular Frame", "Rotor", "Concrete"), PowerInfrastructureLevel.Basic),
"Resource Well Pressurizer": Building("Resource Well Pressurizer", ("Wire", "Rubber", "Encased Industrial Beam", "Motor", "Steel Beam", "Plastic"), PowerInfrastructureLevel.Advanced),
"Equipment Workshop": Building("Equipment Workshop", ("Iron Plate", "Iron Rod"), implicitly_unlocked=True),
"AWESOME Sink": Building("AWESOME Sink", ("Reinforced Iron Plate", "Cable", "Concrete"), can_produce=False),
"AWESOME Shop": Building("AWESOME Shop", ("Screw", "Iron Plate", "Cable"), can_produce=False),
"MAM": Building("MAM", ("Reinforced Iron Plate", "Wire", "Cable"), can_produce=False),
"Pipes Mk.1": Building("Pipes Mk.1", ("Copper Sheet", "Iron Plate", "Concrete"), can_produce=False),
"Pipes Mk.2": Building("Pipes Mk.2", ("Copper Sheet", "Plastic", "Iron Plate", "Concrete"), can_produce=False),
"Pipeline Pump Mk.1": Building("Pipeline Pump Mk.1", ("Copper Sheet", "Rotor"), can_produce=False),
"Pipeline Pump Mk.2": Building("Pipeline Pump Mk.2", ("Motor", "Encased Industrial Beam", "Plastic"), can_produce=False),
"Conveyor Merger": Building("Conveyor Merger", ("Iron Plate", "Iron Rod"), can_produce=False),
"Conveyor Splitter": Building("Conveyor Splitter", ("Iron Plate", "Cable"), can_produce=False),
"Conveyor Mk.1": Building("Conveyor Mk.1", ("Iron Plate", "Iron Rod", "Concrete"), can_produce=False, implicitly_unlocked=True),
"Conveyor Mk.2": Building("Conveyor Mk.2", ("Reinforced Iron Plate", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
"Conveyor Mk.3": Building("Conveyor Mk.3", ("Steel Beam", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
"Conveyor Mk.4": Building("Conveyor Mk.4", ("Encased Industrial Beam", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
"Conveyor Mk.5": Building("Conveyor Mk.5", ("Alclad Aluminum Sheet", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
"Conveyor Mk.6": Building("Conveyor Mk.6", ("Ficsite Trigon", "Time Crystal", "Iron Plate", "Iron Rod", "Concrete"), can_produce=False),
"Power Pole Mk.1": Building("Power Pole Mk.1", ("Iron Plate", "Iron Rod", "Concrete"), can_produce=False, implicitly_unlocked=True),
# higher level power poles arent in logic (yet)
#"Power Pole Mk.2": Building("Power Pole Mk.2", ("Quickwire", "Iron Rod", "Concrete"), False),
#"Power Pole Mk.3": Building("Power Pole Mk.3", ("High-Speed Connector", "Steel Pipe", "Rubber"), False),
"Power Storage": Building("Power Storage", ("Wire", "Modular Frame", "Stator"), can_produce=False),
"Foundation": Building("Foundation", ("Iron Plate", "Concrete"), can_produce=False),
"Walls Orange": Building("Walls Orange", ("Iron Plate", "Concrete"), can_produce=False),
"Space Elevator": Building("Space Elevator", ("Concrete", "Iron Plate", "Iron Rod", "Wire"), can_produce=False, implicitly_unlocked=True),
#1.0
"Converter": Building("Converter", ("Fused Modular Frame", "Cooling System", "Radio Control Unit", "SAM Fluctuator"), PowerInfrastructureLevel.Complex),
"Quantum Encoder": Building("Quantum Encoder", ("Turbo Motor", "Supercomputer", "Cooling System", "Time Crystal", "Ficsite Trigon"), PowerInfrastructureLevel.Complex),
"Alien Power Augmenter": Building("Alien Power Augmenter", ("SAM Fluctuator", "Cable", "Encased Industrial Beam", "Motor", "Computer")),
#1.0
}
handcraftable_recipes: Dict[str, List[Recipe]] = {}
for part, recipes_per_part in recipes.items():
for recipe in recipes_per_part:
if recipe.handcraftable:
handcraftable_recipes.setdefault(part, list()).append(recipe)
implicitly_unlocked_recipes: Dict[str, Recipe] = {
recipe.name: recipe
for recipes_per_part in recipes.values()
for recipe in recipes_per_part if recipe.implicitly_unlocked
}
implicitly_unlocked_recipes.update({
building.name: building
for building in buildings.values() if building.implicitly_unlocked
})
requirement_per_powerlevel: Dict[PowerInfrastructureLevel, Tuple[Recipe, ...]] = {
# no need to polute the logic by including higher level recipes based on previus recipes
PowerInfrastructureLevel.Basic: (
Recipe("Biomass Power (Biomass)", "Biomass Burner", ("Biomass", ), implicitly_unlocked=True),
),
PowerInfrastructureLevel.Automated: (
Recipe("Biomass Power (Solid Biofuel)", "Biomass Burner", ("Solid Biofuel", ), implicitly_unlocked=True),
#Recipe("Coal Generator Power (Petroleum Coke)", "Coal Generator", ("Petroleum Coke", "Water"), implicitly_unlocked=True),
Recipe("Coal Generator Power (Coal)", "Coal Generator", ("Coal", "Water"), implicitly_unlocked=True),
),
PowerInfrastructureLevel.Advanced: (
Recipe("Coal Generator Power (Compacted Coal)", "Coal Generator", ("Compacted Coal", "Water"), implicitly_unlocked=True),
Recipe("Geothermal Generator Power", "Geothermal Generator", implicitly_unlocked=True),
Recipe("Fuel Generator Power (Liquid Biofuel)","Fuel Generator", ("Liquid Biofuel", ), implicitly_unlocked=True),
Recipe("Fuel Generator Power (Fuel)","Fuel Generator", ("Fuel", ), implicitly_unlocked=True),
Recipe("Alien Power Augmenter Power","Alien Power Augmenter", implicitly_unlocked=True),
),
PowerInfrastructureLevel.Complex: (
Recipe("Fuel Generator Power (Turbofuel)","Fuel Generator", ("Turbofuel", ), implicitly_unlocked=True),
#Recipe("Fuel Generator Power (Rocket Fuel)","Fuel Generator", ("Rocket Fuel", ), implicitly_unlocked=True),
#Recipe("Fuel Generator Power (Ionized Fuel)","Fuel Generator", ("Ionized Fuel", ), implicitly_unlocked=True),
Recipe("Nuclear Power Plant Power (Uranium)","Nuclear Power Plant", ("Uranium Fuel Rod", "Water"), implicitly_unlocked=True),
#Recipe("Nuclear Power Plant Power (Plutonium)","Nuclear Power Plant", ("Plutonium Fuel Rod", "Water"), implicitly_unlocked=True),
#Recipe("Nuclear Power Plant Power (Ficsonium)","Nuclear Power Plant", ("Ficsonium Fuel Rod", "Water"), implicitly_unlocked=True),
#Recipe("Alien Power Augmenter Power (Alien Power Matrix)","Alien Power Augmenter", ("Alien Power Matrix"), implicitly_unlocked=True),
)
}
slots_per_milestone: int = 8
hub_layout: Tuple[Tuple[Dict[str, int], ...], ...] = (
# Regenerate via /Script/Engine.Blueprint'/Archipelago/Debug/CC_BuildHubData.CC_BuildHubData'
( # Tier 1
{"Concrete":200, "Iron Plate":100, "Iron Rod":100, }, # Schematic: Base Building (Schematic_1-1_C)
{"Iron Plate":150, "Iron Rod":150, "Wire":300, }, # Schematic: Logistics (Schematic_1-2_C)
{"Wire":300, "Screw":300, "Iron Plate":100, }, # Schematic: Field Research (Schematic_1-3_C)
{"Wire":100, "Screw":200, "Concrete":200, }, # Schematic: Archipelago Additional Tier1 (Schem_ApExtraTier1_C)
),
( # Tier 2
{"Cable":200, "Iron Rod":200, "Screw":500, "Iron Plate":300, }, # Schematic: Part Assembly (Schematic_2-1_C)
{"Screw":500, "Cable":100, "Concrete":100, }, # Schematic: Obstacle Clearing (Schematic_2-2_C)
{"Rotor":50, "Iron Plate":300, "Cable":150, }, # Schematic: Jump Pads (Schematic_2-3_C)
{"Concrete":400, "Wire":500, "Iron Rod":200, "Iron Plate":200, }, # Schematic: Resource Sink Bonus Program (Schematic_2-5_C)
{"Reinforced Iron Plate":50, "Concrete":200, "Iron Rod":300, "Iron Plate":300, }, # Schematic: Logistics Mk.2 (Schematic_3-2_C)
),
( # Tier 3
{"Reinforced Iron Plate":150, "Rotor":50, "Cable":500, }, # Schematic: Coal Power (Schematic_3-1_C)
{"Modular Frame":25, "Rotor":100, "Cable":100, "Iron Plate":400, }, # Schematic: Vehicular Transport (Schematic_3-3_C)
{"Modular Frame":50, "Rotor":150, "Concrete":500, "Wire":1000, }, # Schematic: Basic Steel Production (Schematic_3-4_C)
{"Reinforced Iron Plate":100, "Iron Rod":600, "Wire":1500, }, # Schematic: Improved Melee Combat (Schematic_4-2_C)
),
( # Tier 4
{"Modular Frame":100, "Steel Beam":200, "Cable":500, "Concrete":1000, }, # Schematic: FICSIT Blueprints (Schematic_4-5_C)
{"Steel Beam":200, "Steel Pipe":200, "Reinforced Iron Plate":400, }, # Schematic: Logistics Mk.3 (Schematic_5-3_C)
{"Steel Pipe":100, "Modular Frame":100, "Rotor":200, "Concrete":500, }, # Schematic: Advanced Steel Production (Schematic_4-1_C)
{"Encased Industrial Beam":50, "Steel Beam":100, "Modular Frame":200, "Wire":2000 }, # Schematic: Expanded Power Infrastructure (Schematic_4-3_C)
{"Copper Sheet":500, "Steel Pipe":300, "Encased Industrial Beam":50, }, # Schematic: Hypertubes (Schematic_4-4_C)
),
( # Tier 5
{"Motor":50, "Cable":100, "Iron Plate":500 }, # Something jetpack
{"Motor":50, "Encased Industrial Beam":100, "Steel Pipe":500, "Copper Sheet":500, }, # Schematic: Oil Processing (Schematic_5-1_C)
{"Rubber":200, "Encased Industrial Beam":300, "Modular Frame":400, },
{"Plastic":200, "Steel Beam":400, "Copper Sheet":1000, }, # Schematic: Alternative Fluid Transport (Schematic_5-4_C)
{"Motor":100, "Encased Industrial Beam":100, "Plastic":200, "Rubber":200, }, # Schematic: Industrial Manufacturing (Schematic_5-2_C)
),
( # Tier 6
{"Motor":200, "Modular Frame":200, "Plastic":400, "Cable":1000, }, # Schematic: Industrial Manufacturing (Schematic_5-2_C)
{"Motor":250, "Encased Industrial Beam":500, "Steel Pipe":1000, "Steel Beam":1000, }, # Schematic: Monorail Train Technology (Schematic_6-3_C)
{"Computer":50, "Steel Pipe":4000, "Copper Sheet":1000, },
{"Heavy Modular Frame":50, "Plastic":1000, "Rubber":1000, }, # Schematic: Pipeline Engineering Mk.2 (Schematic_6-5_C)
{"Heavy Modular Frame":50, "Computer":100, "Rubber":400, "Concrete": 1000, },
),
( # Tier 7
{"Computer":100, "Heavy Modular Frame":100, "Motor":250, "Rubber":500, }, # Schematic: Bauxite Refinement (Schematic_7-1_C)
{"Alclad Aluminum Sheet":100, "Heavy Modular Frame":100, "Computer":100, "Motor":250, }, # Schematic: Hover Pack (Schematic_8-3_C)
{"Alclad Aluminum Sheet":200, "Encased Industrial Beam":400, "Reinforced Iron Plate":600, }, # Schematic: Logistics Mk.5 (Schematic_7-2_C)
{"Gas Filter":50, "Aluminum Casing":100, "Quickwire":500, }, # Schematic: Hazmat Suit (Schematic_7-3_C)
{"Alclad Aluminum Sheet":200, "Aluminum Casing":400, "Computer":200, "Plastic": 1000, }, # Schematic: Aeronautical Engineering (Schematic_7-4_C)
),
( # Tier 8
{"Radio Control Unit": 50, "Alclad Aluminum Sheet":100, "Aluminum Casing":200, "Motor": 300, }, # Schematic: Aeronautical Engineering (Schematic_7-4_C)
{"Supercomputer":50, "Heavy Modular Frame":200, "Cable":1000, "Concrete":2000, }, # Schematic: Nuclear Power (Schematic_8-1_C)
{"Radio Control Unit":50, "Aluminum Casing":200, "Alclad Aluminum Sheet":400, "Wire":3000, }, # Schematic: Advanced Aluminum Production (Schematic_8-2_C)
{"Fused Modular Frame":50, "Supercomputer":100, "Steel Pipe":1000, }, # Schematic: Leading-edge Production (Schematic_8-4_C)
{"Turbo Motor":50, "Fused Modular Frame":100, "Cooling System":200, "Quickwire":2500, }, # Schematic: Particle Enrichment (Schematic_8-5_C)
),
( # Tier 9
{"Fused Modular Frame":100, "Radio Control Unit":250, "Cooling System":500, },
{"Time Crystal":50, "Ficsite Trigon":100, "Turbo Motor":200, "Supercomputer":400, },
{"Neural-Quantum Processor":100, "Time Crystal":250, "Ficsite Trigon":500, "Fused Modular Frame":500, },
{"Superposition Oscillator":100, "Turbo Motor":250, "Radio Control Unit":500, "SAM Fluctuator":1000, },
{"Time Crystal":250, "Ficsite Trigon":250, "Alclad Aluminum Sheet":500, "Iron Plate":10000, },
),
)
# Values from /Game/FactoryGame/Schematics/Progression/BP_GamePhaseManager.BP_GamePhaseManager
space_elevator_tiers: Tuple[Dict[str, int], ...] = (
{ "Smart Plating": 50 },
{ "Smart Plating": 500, "Versatile Framework": 500, "Automated Wiring": 100 },
{ "Versatile Framework": 2500, "Modular Engine": 500, "Adaptive Control Unit": 100 },
{ "Assembly Director System": 4000, "Magnetic Field Generator": 4000, "Nuclear Pasta": 1000, "Thermal Propulsion Rocket": 1000 },
{ "Nuclear Pasta": 1000, "Biochemical Sculptor": 1000, "AI Expansion Server": 256, "Ballistic Warp Drive": 200 }
)
# Do not regenerate as format got changed
# Regenerate via /Script/Engine.Blueprint'/Archipelago/Debug/CC_BuildMamData.CC_BuildMamData'
man_trees: Dict[str, MamTree] = {
"Alien Organisms": MamTree(("Hog Remains", "Plasma Spitter Remains", "Stinger Remains", "Hatcher Remains"), ( # Alien Organisms (BPD_ResearchTree_AlienOrganisms_C)
MamNode("Inflated Pocket Dimension", {"Alien Protein":3,"Cable":1000,}, depends_on=("Bio-Organic Properties", )), #(Research_AOrgans_3_C)
MamNode("Hostile Organism Detection", {"Alien DNA Capsule":10,"Crystal Oscillator":5,"High-Speed Connector":5,}, depends_on=("Bio-Organic Properties", )), #(Research_AOrganisms_2_C)
MamNode("Expanded Toolbelt", {"Alien DNA Capsule":5,"Steel Beam":500,}, depends_on=("Inflated Pocket Dimension", )), #(Research_ACarapace_3_C)
MamNode("Bio-Organic Properties", {"Alien Protein":5,}, depends_on=("Spitter Research", "Hog Research", "Hatcher Research", "Stinger Research")), #(Research_AO_DNACapsule_C)
MamNode("Stinger Research", {"Stinger Remains":1,}, depends_on=()), #(Research_AO_Stinger_C)
MamNode("Hatcher Research", {"Hatcher Remains":1,}, depends_on=()), #(Research_AO_Hatcher_C)
MamNode("Hog Research", {"Hog Remains":1,}, depends_on=()), #(Research_ACarapace_0_C)
MamNode("Spitter Research", {"Plasma Spitter Remains":1,}, depends_on=()), #(Research_AOrgans_0_C)
MamNode("Structural Analysis", {"Alien DNA Capsule":5,"Iron Rod":100,}, depends_on=("Bio-Organic Properties", )), #(Research_AO_Pre_Rebar_C)
MamNode("Protein Inhaler", {"Alien Protein":2,"Beryl Nut":20,"Rotor":50,}, depends_on=("Bio-Organic Properties", )), #(Research_AOrgans_2_C)
MamNode("The Rebar Gun", {"Rotor":25,"Reinforced Iron Plate":50,"Screw":500,}, depends_on=("Structural Analysis", )), #(Research_ACarapace_2_C)
)),
# 1.0
"Alien Technology": MamTree(("SAM", "Mercer Sphere", "Somersloop"), (
MamNode("SAM Analysis", {"SAM":10,}, depends_on=()),
MamNode("SAM Reanimation", {"SAM":20,}, depends_on=("SAM Analysis",)),
MamNode("SAM Fluctuator", {"Reanimated SAM":10,"Steel Pipe":100,"Wire":200,}, depends_on=("SAM Reanimation",)),
MamNode("Mercer Sphere Analysis", {"Mercer Sphere":1,}, depends_on=()),
MamNode("Dimensional Depot", {"Mercer Sphere":1,"SAM Fluctuator":11,}, depends_on=("Mercer Sphere Analysis", "SAM Fluctuator")),
MamNode("Manual Depot Uploader", {"Mercer Sphere":3,"Computer":17,"SAM Fluctuator":19,}, depends_on=("Dimensional Depot",)),
MamNode("Depot Expansion (200%)", {"Mercer Sphere":3,"SAM Fluctuator":47,}, depends_on=("Dimensional Depot",)),
MamNode("Depot Expansion (300%)", {"Mercer Sphere":7,"SAM Fluctuator":103,}, depends_on=("Depot Expansion (200%)",)),
MamNode("Depot Expansion (400%)", {"Mercer Sphere":13,"SAM Fluctuator":151,}, depends_on=("Depot Expansion (300%)",)),
MamNode("Depot Expansion (500%)", {"Mercer Sphere":23,"SAM Fluctuator":199,}, depends_on=("Depot Expansion (400%)",)),
MamNode("Upload Upgrade: 30/min", {"Mercer Sphere":3,"SAM Fluctuator":47,}, depends_on=("Dimensional Depot",)),
MamNode("Upload Upgrade: 60/min", {"Mercer Sphere":7,"SAM Fluctuator":103,}, depends_on=("Upload Upgrade: 30/min",)),
MamNode("Upload Upgrade: 120/min", {"Mercer Sphere":13,"SAM Fluctuator":151,}, depends_on=("Upload Upgrade: 60/min",)),
MamNode("Upload Upgrade: 240/min", {"Mercer Sphere":23,"SAM Fluctuator":199,}, depends_on=("Upload Upgrade: 120/min",)),
MamNode("Somersloop Analysis", {"Somersloop":1,}, depends_on=()),
MamNode("Alien Energy Harvesting", {"SAM Fluctuator":10,}, depends_on=("Somersloop Analysis", "SAM Fluctuator")),
MamNode("Production Amplifier", {"Somersloop":1,"SAM Fluctuator":100,"Circuit Board":50,}, depends_on=("Alien Energy Harvesting",)),
MamNode("Power Augmenter", {"Somersloop":1,"SAM Fluctuator":100,"Computer":50,}, depends_on=("Alien Energy Harvesting",)),
MamNode("Alien Power Matrix", {"Singularity Cell":50,"Power Shard":100,"SAM Fluctuator":500}, depends_on=("Power Augmenter",)),
)),
# 1.0
"Caterium": MamTree(("Caterium Ore", ), ( # Caterium (BPD_ResearchTree_Caterium_C)
MamNode("Caterium Electronics", {"Quickwire":100,}, depends_on=("Quickwire", )), #(Research_Caterium_3_C)
MamNode("Bullet Guidance System", {"High-Speed Connector":10,"Rifle Ammo":500,}, depends_on=("High-Speed Connector", )), #(Research_Caterium_6_3_C)
MamNode("High-Speed Connector", {"Quickwire":500,"Plastic":50,}, depends_on=("Caterium Electronics", )), #(Research_Caterium_5_C)
MamNode("Caterium", {"Caterium Ore":10,}, depends_on=()), #(Research_Caterium_0_C)
MamNode("Caterium Ingots", {"Caterium Ore":50,}, depends_on=("Caterium", )), #(Research_Caterium_1_C)
MamNode("Quickwire", {"Caterium Ingot":50,}, depends_on=("Caterium Ingots", )), #(Research_Caterium_2_C)
MamNode("Power Switch", {"Steel Beam":100,"AI Limiter":50,}, depends_on=("AI Limiter", )), #(Research_Caterium_4_1_2_C)
MamNode("Priority Power Switch", {"High-Speed Connector":25,"Quickwire":500,}, depends_on=("High-Speed Connector", )), # 1.0
MamNode("Power Poles Mk.2", {"Quickwire":300,}, depends_on=("Caterium Electronics", )), #(Research_Caterium_4_2_C)
MamNode("AI Limiter", {"Quickwire":200,"Copper Sheet":50,}, depends_on=("Caterium Electronics", )), #(Research_Caterium_4_1_C)
MamNode("Smart Splitter", {"AI Limiter":10,"Reinforced Iron Plate":50,}, depends_on=("AI Limiter", )), #(Research_Caterium_4_1_1_C)
MamNode("Programmable Splitter", {"AI Limiter":100, "Computer":50,"Heavy Modular Frame":50,}, depends_on=("AI Limiter", "High-Speed Connector")), #(Research_Caterium_7_1_C) # 1.0
MamNode("Zipline", {"Quickwire":100,"Cable":50,}, depends_on=("Quickwire", )), #(Research_Caterium_2_1_C)
MamNode("Geothermal Generator", {"Supercomputer":50,"Heavy Modular Frame":50,"Rubber":300,}, depends_on=("AI Limiter", "High-Speed Connector")), #(Research_Caterium_7_2_C) # 1.0
MamNode("Stun Rebar", {"Quickwire":50,"Iron Rebar":10,}, depends_on=("Quickwire", )), #(Research_Caterium_3_2_C)
MamNode("Power Poles Mk.3", {"High-Speed Connector":50,"Steel Pipe":200,}, depends_on=("Power Poles Mk.2", )), #(Research_Caterium_6_2_C) # 1.0
)),
"Mycelia": MamTree(("Mycelia", ), ( # Mycelia (BPD_ResearchTree_Mycelia_C)
MamNode("Therapeutic Inhaler", {"Mycelia":15,"Bacon Agaric":1,"Alien Protein":1,}, depends_on=("Medical Properties", )), #(Research_Mycelia_6_C)
MamNode("Expanded Toolbelt", {"Fabric":50,"Rotor":100,}, depends_on=("Fabric", )), #(Research_Mycelia_7_C)
MamNode("Mycelia", {"Mycelia":5,}, depends_on=tuple()), #(Research_Mycelia_1_C)
MamNode("Fabric", {"Mycelia":25,"Biomass":100,}, depends_on=("Mycelia", )), #(Research_Mycelia_2_C)
MamNode("Medical Properties", {"Mycelia":25,"Stator":10,}, depends_on=("Mycelia", )), #(Research_Mycelia_4_C)
MamNode("Toxic Cellular Modification", {"Nobelisk":10,"Mycelia":100,"Biomass":200,}, depends_on=("Mycelia", )), #(Research_Mycelia_8_C)
MamNode("Vitamin Inhaler", {"Mycelia":10,"Paleberry":5,}, depends_on=("Medical Properties", )), #(Research_Mycelia_5_C)
MamNode("Parachute", {"Fabric":10,"Cable":50,}, depends_on=("Fabric", )), #(Research_Mycelia_3_C)
MamNode("Synthethic Polyester Fabric", {"Fabric":25,"Polymer Resin":100,}, depends_on=("Fabric", )), #(Research_Mycelia_2_1_C)
MamNode("Gas Mask", {"Coal":10,"Fabric":50,"Steel Pipe":50}, depends_on=("Fabric", )), # 1.0
)),
"Nutrients": MamTree(("Paleberry", "Beryl Nut", "Bacon Agaric"), ( # Nutrients (BPD_ResearchTree_Nutrients_C)
MamNode("Bacon Agaric", {"Bacon Agaric":1,}, depends_on=()), #(Research_Nutrients_2_C)
MamNode("Beryl Nut", {"Beryl Nut":5,}, depends_on=()), #(Research_Nutrients_1_C)
MamNode("Paleberry", {"Paleberry":2,}, depends_on=()), #(Research_Nutrients_0_C)
MamNode("Nutritional Processor", {"Modular Frame":25,"Steel Pipe":50,"Wire":500,}, depends_on=("Beryl Nut", "Bacon Agaric", "Paleberry")), #(Research_Nutrients_3_C)
MamNode("Nutritional Inhaler", {"Bacon Agaric":2,"Paleberry":4,"Beryl Nut":10,}, depends_on=("Nutritional Processor", )), #(Research_Nutrients_4_C)
)),
"Power Slugs": MamTree(("Blue Power Slug", ), ( # Power Slugs (BPD_ResearchTree_PowerSlugs_C)
MamNode("Slug Scanning", {"Iron Rod":50,"Wire":100,"Screw":200,}, depends_on=("Blue Power Slugs", )), #(Research_PowerSlugs_3_C)
MamNode("Blue Power Slugs", {"Blue Power Slug":1,}, depends_on=()), #(Research_PowerSlugs_1_C)
MamNode("Yellow Power Shards", {"Yellow Power Slug":1,"Rotor":25,"Cable":100,}, depends_on=("Blue Power Slugs", )), #(Research_PowerSlugs_4_C)
MamNode("Purple Power Shards", {"Purple Power Slug":1,"Modular Frame":25,"Copper Sheet":100,}, depends_on=("Yellow Power Shards", )), #(Research_PowerSlugs_5_C)
MamNode("Overclock Production", {"Power Shard":1,"Iron Plate":50,"Wire":50,}, depends_on=("Blue Power Slugs", )), #(Research_PowerSlugs_2_C)
MamNode("Synthetic Power Shards", {"Power Shard":10,"Time Crystal":100,"Quartz Crystal":200,}, depends_on=("Purple Power Shards", )), # 1.0
)),
"Quartz": MamTree(("Raw Quartz", ), ( # Quartz (BPD_ResearchTree_Quartz_C)
MamNode("Crystal Oscillator", {"Quartz Crystal":100,"Reinforced Iron Plate":50,}, depends_on=("Quartz Crystals", )), #(Research_Quartz_2_C)
MamNode("Quartz Crystals", {"Raw Quartz":20,}, depends_on=("Quartz", )), #(Research_Quartz_1_1_C)
MamNode("Quartz", {"Raw Quartz":10,}, depends_on=()), #(Research_Quartz_0_C)
MamNode("Shatter Rebar", {"Quartz Crystal":30,"Iron Rebar":150,}, depends_on=("Quartz Crystals", )), #(Research_Quartz_2_1_C)
MamNode("Silica", {"Raw Quartz":20,}, depends_on=("Quartz", )), #(Research_Quartz_1_2_C)
MamNode("Explosive Resonance Application", {"Crystal Oscillator":5,"Nobelisk":100,}, depends_on=("Crystal Oscillator", )), #(Research_Quartz_3_4_C)
MamNode("Blade Runners", {"Silica":50,"Modular Frame":10,}, depends_on=("Silica", )), #(Research_Caterium_4_3_C)
MamNode("The Explorer", {"Crystal Oscillator":10,"Modular Frame":100,}, depends_on=("Crystal Oscillator", )), #(Research_Quartz_3_1_C)
MamNode("Radio Signal Scanning", {"Crystal Oscillator":100,"Motor":100,"Object Scanner":1,}, depends_on=("Crystal Oscillator", )), #(Research_Quartz_4_1_C)
MamNode("Inflated Pocket Dimension", {"Silica":200,}, depends_on=("Silica", )), #(Research_Caterium_3_1_C)
MamNode("Radar Technology", {"Crystal Oscillator":50,"Heavy Modular Frame":50,"Computer":50,}, depends_on=("Crystal Oscillator", )), #(Research_Quartz_4_C) # 1.0
)),
"Sulfur": MamTree(("Sulfur", ), ( # Sulfur (BPD_ResearchTree_Sulfur_C)
MamNode("The Nobelisk Detonator", {"Black Powder":50,"Steel Pipe":100,"Cable":200,}, depends_on=("Black Powder", )), #(Research_Sulfur_3_1_C)
MamNode("Smokeless Powder", {"Black Powder":100,"Plastic":50,}, depends_on=("Black Powder", )), #(Research_Sulfur_3_C)
MamNode("Sulfur", {"Sulfur":10,}, depends_on=()), #(Research_Sulfur_0_C)
MamNode("Inflated Pocket Dimension", {"Smokeless Powder":50,"Computer":50,}, depends_on=("Nuclear Deterrent Development", "Turbo Rifle Ammo", "Cluster Nobelisk", "The Rifle")), #(Research_Sulfur_6_C)
MamNode("The Rifle", {"Smokeless Powder":50,"Motor":100,"Rubber":200,}, depends_on=("Smokeless Powder", )), #(Research_Sulfur_4_1_C)
MamNode("Compacted Coal", {"Hard Drive":1,"Sulfur":25,"Coal":25,}, depends_on=("Experimental Power Generation", )), #(Research_Sulfur_CompactedCoal_C)
MamNode("Black Powder", {"Sulfur":50,"Coal":25,}, depends_on=("Sulfur", )), #(Research_Sulfur_1_C)
MamNode("Explosive Rebar", {"Smokeless Powder":200,"Iron Rebar":200,"Steel Beam":200,}, depends_on=("Smokeless Powder", )), #(Research_Sulfur_4_2_C)
MamNode("Cluster Nobelisk", {"Smokeless Powder":100,"Nobelisk":200,}, depends_on=("Smokeless Powder", )), #(Research_Sulfur_4_C)
MamNode("Experimental Power Generation", {"Sulfur":25,"Modular Frame":50,"Rotor":100,}, depends_on=("Sulfur", )), #(Research_Sulfur_ExperimentalPower_C)
MamNode("Turbo Rifle Ammo", {"Rifle Ammo":1000,"Packaged Turbofuel":50,"Aluminum Casing":100,}, depends_on=("The Rifle", )), #(Research_Sulfur_5_2_C) # 1.0
MamNode("Turbo Fuel", {"Hard Drive":1,"Compacted Coal":15,"Packaged Fuel":50,}, depends_on=("Experimental Power Generation", )), #(Research_Sulfur_TurboFuel_C)
MamNode("Expanded Toolbelt", {"Black Powder":100,"Encased Industrial Beam":50,}, depends_on=("Black Powder", )), #(Research_Sulfur_5_C)
MamNode("Nuclear Deterrent Development", {"Nobelisk":500,"Encased Uranium Cell":10,"AI Limiter":100,}, depends_on=("Cluster Nobelisk", )), #(Research_Sulfur_5_1_C) # 1.0
MamNode("Rocket Fuel", {"Hard Drive":1,"Empty Fluid Tank":10,"Packaged Turbofuel":100,}, depends_on=("Turbo Fuel", )), # 1.0
MamNode("Ionized Fuel", {"Hard Drive":1,"Power Shard":100,"Packaged Rocket Fuel":200,}, depends_on=("Turbo Fuel", )), # 1.0
))
}
drop_pods: List[DropPodData] = [
# Regenerate via /Script/Engine.Blueprint'/Archipelago/Debug/CC_BuildDropPodLocations.CC_BuildDropPodLocations'
DropPodData(-29068, -22640, 17384, "Encased Industrial Beam", 0), # Unlocks with: 4 x Desc_SteelPlateReinforced_C
DropPodData(-33340, 5176, 23519, "Crystal Oscillator", 0), # Unlocks with: 5 x Desc_CrystalOscillator_C
DropPodData(8680, -41777, 13053, "Steel Pipe", 0), # Unlocks with: 7 x Desc_SteelPipe_C
DropPodData(35082, 16211, 22759, "Supercomputer", 0), # Unlocks with: 7 x Desc_ComputerSuper_C
#DropPodData(-3511, 62314, 22109, "Quantum Computer", 0), # Unlocks with: 1 x Desc_ComputerQuantum_C
DropPodData(66652, -13642, 13420, "Encased Industrial Beam", 50), # Unlocks with: 3 x Desc_SteelPlateReinforced_C
DropPodData(55247, -51316, 14363, None, 25), # Unlocks with: (No Item)
DropPodData(-4706, -76301, 13618, "Black Powder", 0), # Unlocks with: 10 x Desc_Gunpowder_C
DropPodData(-40194,62956, 26261, "Superposition Oscillator", 138), # Unlocks with: 2 x Desc_QuantumOscillator_C
DropPodData(80980, -44100, 8303, "Rotor", 0), # Unlocks with: 3 x Desc_Rotor_C
DropPodData(-56144, -72864, 27668, "Quartz Crystal", 0), # Unlocks with: 2 x Desc_QuartzCrystal_C
DropPodData(-95228, 6970, 25142, "High-Speed Connector", 112), # Unlocks with: 11 x Desc_HighSpeedConnector_C
DropPodData(-89284, -50630, 16019, None, 50), # Unlocks with: (No Item)
DropPodData(-94708, 40337, 19832, "Heat Sink", 138), # Unlocks with: 2 x Desc_AluminumPlateReinforced_C
DropPodData(94267, 47237, 9435, "Motor", 0), # Unlocks with: 1 x Desc_Motor_C
DropPodData(87739, -62975, 13444, None, 30), # Unlocks with: (No Item)
DropPodData(12249, 114177, 26721, "AI Limiter", 267), # Unlocks with: 9 x Desc_CircuitBoardHighSpeed_C
DropPodData(115978, 21424, 15519, None, 0), # Unlocks with: (No Item)
DropPodData(-78236, 90857, 20305, "Radio Control Unit", 0), # Unlocks with: 6 x Desc_ModularFrameLightweight_C
DropPodData(-35359, 116594, 21827, "Turbo Motor", 0), # Unlocks with: 6 x Desc_MotorLightweight_C
DropPodData(111479, -54515, 17081, "Stator", 20), # Unlocks with: 1 x Desc_Stator_C
DropPodData(121061, 45324, 17373, None, 0), # Unlocks with: (No Item)
DropPodData(125497, -34949, 8220, None, 50), # Unlocks with: (No Item)
DropPodData(-26327, -129047,7780, "Modular Frame", 0), # Unlocks with: 1 x Desc_ModularFrame_C
DropPodData(21373, 132336, 2510, "Motor", 20), # Unlocks with: 2 x Desc_Motor_C
DropPodData(17807, -136922,13621, "Rotor", 0), # Unlocks with: 1 x Desc_Rotor_C
DropPodData(-118480,74929, 16995, None, 420), # Unlocks with: (No Item)
DropPodData(94940, 105482, 9860, "Heavy Modular Frame", 0), # Unlocks with: 1 x Desc_ModularFrameHeavy_C
DropPodData(-129115,60165, 4800, None, 53), # Unlocks with: (No Item)
DropPodData(-142000,23970, 32660, None, 0), # Unlocks with: (No Item)
DropPodData(46048, 141933, 13064, None, 40), # Unlocks with: (No Item)
DropPodData(144456, 36294, 17301, "Circuit Board", 48), # Unlocks with: 20 x Desc_CircuitBoard_C
DropPodData(-43144, 145820, 7472, "Modular Frame", 0), # Unlocks with: 5 x Desc_ModularFrame_C
DropPodData(-108774,107811, 10154, "Crystal Oscillator", 0), # Unlocks with: 1 x Desc_CrystalOscillator_C
DropPodData(-56987, -144603,2072, "Rotor", 10), # Unlocks with: 1 x Desc_Rotor_C
DropPodData(-152676,33864, 19283, None, 256), # Unlocks with: (No Item)
DropPodData(90313, 129583, 9112, "Crystal Oscillator", 20), # Unlocks with: 2 x Desc_CrystalOscillator_C
DropPodData(111212, -113040,12036, "Screw", 10), # Unlocks with: 15 x Desc_IronScrew_C
DropPodData(-157077,-6312, 25128, "Turbo Motor", 0), # Unlocks with: 8 x Desc_MotorLightweight_C
DropPodData(157249, -40206, 13694, "High-Speed Connector", 0), # Unlocks with: 2 x Desc_HighSpeedConnector_C
DropPodData(-151842,72468, 9945, "Encased Industrial Beam", 0), # Unlocks with: 3 x Desc_SteelPlateReinforced_C
DropPodData(64696, 156038, 14067, "Modular Frame", 0), # Unlocks with: 6 x Desc_ModularFrame_C
DropPodData(-157080,-67028, 11766, "Rotor", 0), # Unlocks with: 4 x Desc_Rotor_C
DropPodData(170057, -10579, 18823, None, 50), # Unlocks with: (No Item)
DropPodData(143671, 92573, 24990, "Crystal Oscillator", 20), # Unlocks with: 2 x Desc_CrystalOscillator_C
DropPodData(127215, -116866,-1397, "Rubber", 0), # Unlocks with: 10 x Desc_Rubber_C
DropPodData(163999, 61333, 21481, "AI Limiter", 0), # Unlocks with: 3 x Desc_CircuitBoardHighSpeed_C
DropPodData(98306, -149781,2552, None, 40), # Unlocks with: (No Item)
DropPodData(5302, -187090,-1608, None, 0), # Unlocks with: (No Item)
DropPodData(188304, 17059, 12949, None, 0), # Unlocks with: (No Item)
DropPodData(84256, -171122,-290, None, 0), # Unlocks with: (No Item)
DropPodData(191366, 37694, 5676, "Computer", 0), # Unlocks with: 4 x Desc_Computer_C
DropPodData(28695, 193441, 17459, "Quickwire", 0), # Unlocks with: 9 x Desc_HighSpeedWire_C
DropPodData(-146044,-137047,2357, "Modular Frame", 0), # Unlocks with: 9 x Desc_ModularFrame_C
DropPodData(-200203,-17766, 12193, "Solid Biofuel", 0), # Unlocks with: 10 x Desc_Biofuel_C
DropPodData(47834, 195703, 2943, "Black Powder", 0), # Unlocks with: 4 x Desc_Gunpowder_C
DropPodData(198418, -41186, 13786, None, 0), # Unlocks with: (No Item)
DropPodData(-195756,-59210, -84, None, 30), # Unlocks with: (No Item)
DropPodData(-121994,166916, -49, "Steel Beam", 20), # Unlocks with: 4 x Desc_SteelPlate_C
DropPodData(88323, 188913, 1420, None, 30), # Unlocks with: (No Item)
DropPodData(-123677,-167107,29710, "Motor", 0), # Unlocks with: 4 x Desc_Motor_C
DropPodData(150633, 146698, 7727, "Crystal Oscillator", 20), # Unlocks with: 2 x Desc_CrystalOscillator_C
DropPodData(-55111, -204857,7844, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
DropPodData(216096, -268, -1592, "Heat Sink", 0), # Unlocks with: 7 x Desc_AluminumPlateReinforced_C
DropPodData(159088, -145116,23164, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
DropPodData(207683, -68352, 3927, "Encased Industrial Beam", 20), # Unlocks with: 27 x Desc_SteelPlateReinforced_C
DropPodData(-189258,116331, -1764, None, 0), # Unlocks with: (No Item)
DropPodData(46951, 221859, 5917, None, 20), # Unlocks with: (No Item)
DropPodData(-9988, 227625, -1017, None, 40), # Unlocks with: (No Item)
DropPodData(232515, -20519, 8979, "Crystal Oscillator", 15), # Unlocks with: 2 x Desc_CrystalOscillator_C
DropPodData(232138,27191, -1629, "Supercomputer", 0), # Unlocks with: 5 x Desc_ComputerSuper_C
DropPodData(-135, -237257,-1760, None, 0), # Unlocks with: (No Item)
DropPodData(-232498,-51432, -386, "Rotor", 0), # Unlocks with: 21 x Desc_Rotor_C
DropPodData(-238333,17321, 19741, "Heat Sink", 0), # Unlocks with: 3 x Desc_AluminumPlateReinforced_C
DropPodData(200510, 131912, 6341, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
DropPodData(-108812,214051, 3200, "Quickwire", 0), # Unlocks with: 1 x Desc_HighSpeedWire_C
DropPodData(232255, 79925, -1275, "Turbo Motor", 67), # Unlocks with: 2 x Desc_MotorLightweight_C
DropPodData(226418, 98109, 7339, None, 200), # Unlocks with: (No Item)
DropPodData(156569, 191767, -9312, "Rubber", 0), # Unlocks with: 4 x Desc_Rubber_C
DropPodData(44579, -244343,-874, None, 0), # Unlocks with: (No Item)
DropPodData(118349, 221905, -7063, "Encased Industrial Beam", 0), # Unlocks with: 6 x Desc_SteelPlateReinforced_C
#DropPodData(249919,59534, 2430, "Quantum Computer", 0), # Unlocks with: 1 x Desc_ComputerQuantum_C
DropPodData(188233, 177201, 9608, "Quickwire", 0), # Unlocks with: 12 x Desc_HighSpeedWire_C
DropPodData(-174494,-197134,-1538, None, 30), # Unlocks with: (No Item)
DropPodData(-50655, -259272,-1667, None, 0), # Unlocks with: (No Item)
DropPodData(30383, 266975, -987, "Screw", 0), # Unlocks with: 12 x Desc_IronScrew_C
DropPodData(272715, 28087, -1586, "Supercomputer", 0), # Unlocks with: 2 x Desc_ComputerSuper_C
DropPodData(-152279,229520,1052, "Modular Frame", 0), # Unlocks with: 5 x Desc_ModularFrame_C
DropPodData(241532, 131343, 17157, None, 0), # Unlocks with: (No Item)
DropPodData(-259577,105048, -1548, None, 0), # Unlocks with: (No Item)
DropPodData(275070, -52585, 5980, None, 0), # Unlocks with: (No Item)
DropPodData(-247303,-142348,4524, "Rotor", 0), # Unlocks with: 4 x Desc_Rotor_C
DropPodData(261797, 124616, -2597, "AI Limiter", 73), # Unlocks with: 3 x Desc_CircuitBoardHighSpeed_C
DropPodData(187056, 223656, -3215, None, 42), # Unlocks with: (No Item)
DropPodData(293299, 51, 522, "Crystal Oscillator", 42), # Unlocks with: 8 x Desc_CrystalOscillator_C
DropPodData(219146, -199880,6503, "Rotor", 0), # Unlocks with: 10 x Desc_Rotor_C
DropPodData(176423, 243273, -9780, "Motor", 19), # Unlocks with: 3 x Desc_Motor_C
DropPodData(291821, 74782, -1574, "Superposition Oscillator", 0), # Unlocks with: 5 x Desc_QuantumOscillator_C
DropPodData(-78884, 292640, -4763, "Modular Frame", 0), # Unlocks with: 5 x Desc_ModularFrame_C
DropPodData(174948, -276436,21151, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
DropPodData(295166, -173139,8083, None, 0), # Unlocks with: (No Item)
DropPodData(349295, -38831, -1485, "Motor", 0), # Unlocks with: 10 x Desc_Motor_C
DropPodData(360114, -106614,11815, "Motor", 0), # Unlocks with: 35 x Desc_Motor_C
DropPodData(303169, -246169,5487, None, 50), # Unlocks with: (No Item)
DropPodData(236508, -312236,9971, "Motor", 0), # Unlocks with: 30 x Desc_Motor_C
DropPodData(360285, -217558,3900, None, 70), # Unlocks with: (No Item)
DropPodData(366637, -303548,-7288, None, 0), # Unlocks with: (No Item)
]

View File

@@ -0,0 +1,41 @@
from enum import Enum
from typing import NamedTuple, Set
from BaseClasses import ItemClassification
class ItemGroups(str, Enum):
Parts = 1
Equipment = 2
Ammo = 3
Recipe = 4
Building = 5
Trap = 6
Lights = 7
Foundations = 8
Transport = 9
Trains = 10
ConveyorMk1 = 11
ConveyorMk2 = 12
ConveyorMk3 = 13
ConveyorMk4 = 14
ConveyorMk5 = 15
ConveyorSupports = 16
PipesMk1 = 17
PipesMk2 = 18
PipelineSupports = 19
HyperTubes = 20
Signs = 21
Pilars = 22
Beams = 23
Walls = 24
Upgrades = 25
Vehicles = 26
Customizer = 27
ConveyorMk6 = 28
class ItemData(NamedTuple):
"""Represents an item in the pool, it could be a resource bundle, production recipe, trap, etc."""
category: Set[ItemGroups]
code: int
type: ItemClassification = ItemClassification.filler
count: int = 1
"""How many of this item exists in the pool. 0 means none, but still defines the item so it can be added in the starting inventory for example"""

View File

@@ -0,0 +1,890 @@
import copy
from random import Random
from typing import ClassVar, Dict, Set, List, TextIO, Tuple, Optional
from BaseClasses import Item, ItemClassification as C, MultiWorld
from .GameLogic import GameLogic, Recipe
from .Options import SatisfactoryOptions
from .ItemData import ItemData, ItemGroups as G
from .Options import SatisfactoryOptions
import logging
class Items:
item_data: ClassVar[Dict[str, ItemData]] = {
# Resource Bundles
"Bundle: Adaptive Control Unit": ItemData(frozenset({G.Parts}), 1338000),
"Bundle: AI Limiter": ItemData(frozenset({G.Parts}), 1338001),
"Bundle: Alclad Aluminum Sheet": ItemData(frozenset({G.Parts}), 1338002),
"Bundle: Blue Power Slug": ItemData(frozenset({G.Parts}), 1338003),
"Bundle: Yellow Power Slug": ItemData(frozenset({G.Parts}), 1338004),
"Bundle: Alien Protein": ItemData(frozenset({G.Parts}), 1338005),
"Bundle: Purple Power Slug": ItemData(frozenset({G.Parts}), 1338006),
"Bundle: Aluminum Casing": ItemData(frozenset({G.Parts}), 1338007),
"Bundle: Aluminum Ingot": ItemData(frozenset({G.Parts}), 1338008),
"Bundle: Aluminum Scrap": ItemData(frozenset({G.Parts}), 1338009),
"Bundle: Assembly Director System": ItemData(frozenset({G.Parts}), 1338010),
"Bundle: Automated Wiring": ItemData(frozenset({G.Parts}), 1338011),
"Bundle: Battery": ItemData(frozenset({G.Parts}), 1338012),
"Bundle: Bauxite": ItemData(frozenset({G.Parts}), 1338013),
"Bundle: Neural-Quantum Processor": ItemData(frozenset({G.Parts}), 1338014), #1.0
"Bundle: Biomass": ItemData(frozenset({G.Parts}), 1338015),
"Bundle: Black Powder": ItemData(frozenset({G.Parts}), 1338016),
"Bundle: Cable": ItemData(frozenset({G.Parts}), 1338017),
"Bundle: Caterium Ingot": ItemData(frozenset({G.Parts}), 1338018),
"Bundle: Caterium Ore": ItemData(frozenset({G.Parts}), 1338019),
"Bundle: Circuit Board": ItemData(frozenset({G.Parts}), 1338020),
"Bundle: Coal": ItemData(frozenset({G.Parts}), 1338021),
"Bundle: Singularity Cell": ItemData(frozenset({G.Parts}), 1338022), #1.0
"Bundle: Compacted Coal": ItemData(frozenset({G.Parts}), 1338023),
"Bundle: Computer": ItemData(frozenset({G.Parts}), 1338024),
"Bundle: Concrete": ItemData(frozenset({G.Parts}), 1338025),
"Bundle: Cooling System": ItemData(frozenset({G.Parts}), 1338026),
"Bundle: Copper Ingot": ItemData(frozenset({G.Parts}), 1338027),
"Bundle: Copper Ore": ItemData(frozenset({G.Parts}), 1338028),
"Bundle: Copper Powder": ItemData(frozenset({G.Parts}), 1338029),
"Bundle: Copper Sheet": ItemData(frozenset({G.Parts}), 1338030),
"Bundle: Adequate Pioneering Statue": ItemData(frozenset({G.Parts}), 1338031),
"Bundle: Crystal Oscillator": ItemData(frozenset({G.Parts}), 1338032),
"Bundle: Electromagnetic Control Rod": ItemData(frozenset({G.Parts}), 1338033),
"Bundle: Empty Canister": ItemData(frozenset({G.Parts}), 1338034),
"Bundle: Empty Fluid Tank": ItemData(frozenset({G.Parts}), 1338035),
"Bundle: Encased Industrial Beam": ItemData(frozenset({G.Parts}), 1338036),
"Bundle: Encased Plutonium Cell": ItemData(frozenset({G.Trap}), 1338037, C.trap),
"Bundle: Encased Uranium Cell": ItemData(frozenset({G.Trap}), 1338038, C.trap),
"Bundle: Fabric": ItemData(frozenset({G.Parts}), 1338039),
"Bundle: FICSIT Coupon": ItemData(frozenset({G.Parts}), 1338040),
"Bundle: AI Expansion Server": ItemData(frozenset({G.Parts}), 1338041), #1.0
"Bundle: Fused Modular Frame": ItemData(frozenset({G.Parts}), 1338042),
"Bundle: Hard Drive": ItemData(frozenset({G.Parts}), 1338043),
"Bundle: Heat Sink": ItemData(frozenset({G.Parts}), 1338044),
"Bundle: Heavy Modular Frame": ItemData(frozenset({G.Parts}), 1338045),
"Bundle: High-Speed Connector": ItemData(frozenset({G.Parts}), 1338046),
"Bundle: Satisfactory Pioneering Statue": ItemData(frozenset({G.Parts}), 1338047),
"Bundle: Pretty Good Pioneering Statue": ItemData(frozenset({G.Parts}), 1338048),
"Bundle: Iron Ingot": ItemData(frozenset({G.Parts}), 1338049),
"Bundle: Iron Ore": ItemData(frozenset({G.Parts}), 1338050),
"Bundle: Iron Plate": ItemData(frozenset({G.Parts}), 1338051),
"Bundle: Iron Rod": ItemData(frozenset({G.Parts}), 1338052),
"Bundle: Golden Nut Statue": ItemData(frozenset({G.Parts}), 1338053),
"Bundle: Leaves": ItemData(frozenset({G.Parts}), 1338054),
"Bundle: Limestone": ItemData(frozenset({G.Parts}), 1338055),
"Bundle: Magnetic Field Generator": ItemData(frozenset({G.Parts}), 1338056),
"Bundle: Mercer Sphere": ItemData(frozenset({G.Parts}), 1338057),
"Bundle: Modular Engine": ItemData(frozenset({G.Parts}), 1338058),
"Bundle: Modular Frame": ItemData(frozenset({G.Parts}), 1338059),
"Bundle: Motor": ItemData(frozenset({G.Parts}), 1338060),
"Bundle: Mycelia": ItemData(frozenset({G.Parts}), 1338061),
"Bundle: Non-fissile Uranium": ItemData(frozenset({G.Trap}), 1338062, C.trap),
"Bundle: Nuclear Pasta": ItemData(frozenset({G.Parts}), 1338063),
"Bundle: Lizard Doggo Statue": ItemData(frozenset({G.Parts}), 1338064),
"Bundle: Organic Data Capsule": ItemData(frozenset({G.Parts}), 1338065),
"Bundle: Packaged Alumina Solution": ItemData(frozenset({G.Parts}), 1338066),
"Bundle: Packaged Fuel": ItemData(frozenset({G.Parts}), 1338067),
"Bundle: Packaged Heavy Oil Residue": ItemData(frozenset({G.Parts}), 1338068),
"Bundle: Packaged Liquid Biofuel": ItemData(frozenset({G.Parts}), 1338069),
"Bundle: Packaged Nitric Acid": ItemData(frozenset({G.Parts}), 1338070),
"Bundle: Packaged Nitrogen Gas": ItemData(frozenset({G.Parts}), 1338071),
"Bundle: Packaged Oil": ItemData(frozenset({G.Parts}), 1338072),
"Bundle: Packaged Sulfuric Acid": ItemData(frozenset({G.Parts}), 1338073),
"Bundle: Packaged Turbofuel": ItemData(frozenset({G.Parts}), 1338074),
"Bundle: Packaged Water": ItemData(frozenset({G.Parts}), 1338075),
"Bundle: Petroleum Coke": ItemData(frozenset({G.Parts}), 1338076),
"Bundle: Plastic": ItemData(frozenset({G.Parts}), 1338077),
"Bundle: Plutonium Fuel Rod": ItemData(frozenset({G.Trap}), 1338078, C.trap),
"Bundle: Plutonium Pellet": ItemData(frozenset({G.Trap}), 1338079, C.trap),
"Bundle: Plutonium Waste": ItemData(frozenset({G.Trap}), 1338080, C.trap),
"Bundle: Polymer Resin": ItemData(frozenset({G.Parts}), 1338081),
"Bundle: Power Shard": ItemData(frozenset({G.Parts}), 1338082),
"Bundle: Confusing Creature Statue": ItemData(frozenset({G.Parts}), 1338083),
"Bundle: Pressure Conversion Cube": ItemData(frozenset({G.Parts}), 1338084),
"Bundle: Alien Power Matrix": ItemData(frozenset({G.Parts}), 1338085), #1.0
"Bundle: Quartz Crystal": ItemData(frozenset({G.Parts}), 1338086),
"Bundle: Quickwire": ItemData(frozenset({G.Parts}), 1338087),
"Bundle: Radio Control Unit": ItemData(frozenset({G.Parts}), 1338088),
"Bundle: Raw Quartz": ItemData(frozenset({G.Parts}), 1338089),
"Bundle: Reinforced Iron Plate": ItemData(frozenset({G.Parts}), 1338090),
"Bundle: Rotor": ItemData(frozenset({G.Parts}), 1338091),
"Bundle: Rubber": ItemData(frozenset({G.Parts}), 1338092),
"Bundle: SAM": ItemData(frozenset({G.Parts}), 1338093), # 1.0
"Bundle: Screw": ItemData(frozenset({G.Parts}), 1338094),
"Bundle: Silica": ItemData(frozenset({G.Parts}), 1338095),
"Bundle: Smart Plating": ItemData(frozenset({G.Parts}), 1338096),
"Bundle: Smokeless Powder": ItemData(frozenset({G.Parts}), 1338097),
"Bundle: Solid Biofuel": ItemData(frozenset({G.Parts}), 1338098),
"Bundle: Somersloop": ItemData(frozenset({G.Parts}), 1338099),
"Bundle: Stator": ItemData(frozenset({G.Parts}), 1338100),
"Bundle: Silver Hog Statue": ItemData(frozenset({G.Parts}), 1338101),
"Bundle: Steel Beam": ItemData(frozenset({G.Parts}), 1338102),
"Bundle: Steel Ingot": ItemData(frozenset({G.Parts}), 1338103),
"Bundle: Steel Pipe": ItemData(frozenset({G.Parts}), 1338104),
"Bundle: Sulfur": ItemData(frozenset({G.Parts}), 1338105),
"Bundle: Supercomputer": ItemData(frozenset({G.Parts}), 1338106),
"Bundle: Superposition Oscillator": ItemData(frozenset({G.Parts}), 1338107),
"Bundle: Thermal Propulsion Rocket": ItemData(frozenset({G.Parts}), 1338108),
"Bundle: Turbo Motor": ItemData(frozenset({G.Parts}), 1338109),
"Bundle: Hog Remains": ItemData(frozenset({G.Parts}), 1338110),
"Bundle: Uranium": ItemData(frozenset({G.Trap}), 1338111, C.trap),
"Bundle: Uranium Fuel Rod": ItemData(frozenset({G.Trap}), 1338112, C.trap),
"Bundle: Uranium Waste": ItemData(frozenset({G.Trap}), 1338113, C.trap),
"Bundle: Versatile Framework": ItemData(frozenset({G.Parts}), 1338114),
"Bundle: Wire": ItemData(frozenset({G.Parts}), 1338115),
"Bundle: Wood": ItemData(frozenset({G.Parts}), 1338116),
"Bundle: Plasma Spitter Remains": ItemData(frozenset({G.Parts}), 1338117),
"Bundle: Stinger Remains": ItemData(frozenset({G.Parts}), 1338118),
"Bundle: Hatcher Remains": ItemData(frozenset({G.Parts}), 1338119),
"Bundle: Alien DNA Capsule": ItemData(frozenset({G.Parts}), 1338120),
# 1.0
"Bundle: Diamonds": ItemData(frozenset({G.Parts}), 1338121),
"Bundle: Time Crystal": ItemData(frozenset({G.Parts}), 1338122),
"Bundle: Ficsite Ingot": ItemData(frozenset({G.Parts}), 1338123),
"Bundle: Ficsite Trigon": ItemData(frozenset({G.Parts}), 1338124),
"Bundle: Reanimated SAM": ItemData(frozenset({G.Parts}), 1338125),
"Bundle: SAM Fluctuator": ItemData(frozenset({G.Parts}), 1338126),
"Bundle: Biochemical Sculptor": ItemData(frozenset({G.Parts}), 1338127),
"Bundle: Ballistic Warp Drive": ItemData(frozenset({G.Parts}), 1338128),
"Bundle: Ficsonium": ItemData(frozenset({G.Trap}), 1338129, C.trap),
"Bundle: Ficsonium Fuel Rod": ItemData(frozenset({G.Trap}), 1338130, C.trap),
"Bundle: Packaged Rocket Fuel": ItemData(frozenset({G.Parts}), 1338131),
"Bundle: Packaged Ionized Fuel": ItemData(frozenset({G.Parts}), 1338132),
# 1.0
#1338131 - 1338149 Reserved for future parts
# Equipment / Ammo
"Bundle: Bacon Agaric": ItemData(frozenset({G.Ammo}), 1338150),
"Bundle: Beryl Nut": ItemData(frozenset({G.Ammo}), 1338151),
"Bundle: Blade Runners": ItemData(frozenset({G.Equipment}), 1338152),
"Bundle: Boom Box": ItemData(frozenset({G.Equipment}), 1338153),
"Bundle: Chainsaw": ItemData(frozenset({G.Equipment}), 1338154),
"Bundle: Cluster Nobelisk": ItemData(frozenset({G.Ammo}), 1338155),
#"Bundle: Color Gun": ItemData(frozenset({G.Equipment}), 1338156), Removed in U8
"Bundle: Cup": ItemData(frozenset({G.Equipment}), 1338157),
"Bundle: Cup (gold)": ItemData(frozenset({G.Equipment}), 1338158, count=0),
"Bundle: Explosive Rebar": ItemData(frozenset({G.Ammo}), 1338159),
"Bundle: Factory Cart": ItemData(frozenset({G.Equipment}), 1338160),
"Bundle: Factory Cart (golden)": ItemData(frozenset({G.Equipment}), 1338161, count=0),
"Bundle: Gas Mask": ItemData(frozenset({G.Equipment}), 1338162),
"Bundle: Gas Nobelisk": ItemData(frozenset({G.Ammo}), 1338163),
"Bundle: Hazmat Suit": ItemData(frozenset({G.Equipment}), 1338164),
"Bundle: Homing Rifle Ammo": ItemData(frozenset({G.Ammo}), 1338165),
"Bundle: Hover Pack": ItemData(frozenset({G.Equipment}), 1338166),
"Bundle: Iron Rebar": ItemData(frozenset({G.Ammo}), 1338167),
"Bundle: Jetpack": ItemData(frozenset({G.Equipment}), 1338168),
"Bundle: Medicinal Inhaler": ItemData(frozenset({G.Ammo}), 1338169),
"Bundle: Nobelisk": ItemData(frozenset({G.Ammo}), 1338170),
"Bundle: Nobelisk Detonator": ItemData(frozenset({G.Equipment}), 1338171),
"Bundle: Nuke Nobelisk": ItemData(frozenset({G.Ammo}), 1338172),
"Bundle: Object Scanner": ItemData(frozenset({G.Equipment}), 1338173),
"Bundle: Paleberry": ItemData(frozenset({G.Ammo}), 1338174),
"Bundle: Parachute": ItemData(frozenset({G.Equipment}), 1338175),
"Bundle: Pulse Nobelisk": ItemData(frozenset({G.Ammo}), 1338176),
"Bundle: Rebar Gun": ItemData(frozenset({G.Equipment}), 1338177),
"Bundle: Rifle": ItemData(frozenset({G.Equipment}), 1338178),
"Bundle: Rifle Ammo": ItemData(frozenset({G.Ammo}), 1338179),
"Bundle: Shatter Rebar": ItemData(frozenset({G.Ammo}), 1338180),
"Bundle: Stun Rebar": ItemData(frozenset({G.Ammo}), 1338181),
"Bundle: Turbo Rifle Ammo": ItemData(frozenset({G.Ammo}), 1338182),
"Bundle: Xeno-Basher": ItemData(frozenset({G.Equipment}), 1338183),
"Bundle: Xeno-Zapper": ItemData(frozenset({G.Equipment}), 1338184),
"Bundle: Zipline": ItemData(frozenset({G.Equipment}), 1338185),
"Bundle: Portable Miner": ItemData(frozenset({G.Equipment}), 1338186),
"Bundle: Gas Filter": ItemData(frozenset({G.Ammo}), 1338187),
# Other
"Small Inflated Pocket Dimension": ItemData(frozenset({G.Upgrades}), 1338188, C.useful, 11),
"Inflated Pocket Dimension": ItemData(frozenset({G.Upgrades}), 1338189, C.useful, 5),
"Expanded Toolbelt": ItemData(frozenset({G.Upgrades}), 1338190, C.useful, 5),
"Dimensional Depot upload from inventory": ItemData(frozenset({G.Upgrades}), 1338191, C.useful),
#1338191 - 1338199 Reserved for future equipment/ammo
#1338200+ Recipes / buildings / schematics
"Recipe: Reinforced Iron Plate": ItemData(frozenset({G.Recipe}), 1338200, C.progression),
"Recipe: Adhered Iron Plate": ItemData(frozenset({G.Recipe}), 1338201, C.progression),
"Recipe: Bolted Iron Plate": ItemData(frozenset({G.Recipe}), 1338202, C.progression),
"Recipe: Stitched Iron Plate": ItemData(frozenset({G.Recipe}), 1338203, C.progression),
"Recipe: Rotor": ItemData(frozenset({G.Recipe}), 1338204, C.progression),
"Recipe: Copper Rotor": ItemData(frozenset({G.Recipe}), 1338205, C.progression),
"Recipe: Steel Rotor": ItemData(frozenset({G.Recipe}), 1338206, C.progression),
"Recipe: Stator": ItemData(frozenset({G.Recipe}), 1338207, C.progression),
"Recipe: Quickwire Stator": ItemData(frozenset({G.Recipe}), 1338208, C.progression),
"Recipe: Plastic": ItemData(frozenset({G.Recipe}), 1338209, C.progression),
"Recipe: Residual Plastic": ItemData(frozenset({G.Recipe}), 1338210, C.progression),
"Recipe: Recycled Plastic": ItemData(frozenset({G.Recipe}), 1338211, C.progression),
"Recipe: Rubber": ItemData(frozenset({G.Recipe}), 1338212, C.progression),
"Recipe: Residual Rubber": ItemData(frozenset({G.Recipe}), 1338213, C.progression),
"Recipe: Recycled Rubber": ItemData(frozenset({G.Recipe}), 1338214, C.progression),
"Recipe: Iron Plate": ItemData(frozenset({G.Recipe}), 1338215, C.progression),
"Recipe: Coated Iron Plate": ItemData(frozenset({G.Recipe}), 1338216, C.progression),
"Recipe: Steel Cast Plate": ItemData(frozenset({G.Recipe}), 1338217, C.progression), # 1.0
"Recipe: Iron Rod": ItemData(frozenset({G.Recipe}), 1338218, C.progression),
"Recipe: Steel Rod": ItemData(frozenset({G.Recipe}), 1338219, C.progression),
"Recipe: Screw": ItemData(frozenset({G.Recipe}), 1338220, C.progression),
"Recipe: Cast Screw": ItemData(frozenset({G.Recipe}), 1338221, C.progression),
"Recipe: Steel Screw": ItemData(frozenset({G.Recipe}), 1338222, C.progression),
"Recipe: Wire": ItemData(frozenset({G.Recipe}), 1338223, C.progression),
"Recipe: Fused Wire": ItemData(frozenset({G.Recipe}), 1338224, C.progression),
"Recipe: Iron Wire": ItemData(frozenset({G.Recipe}), 1338225, C.progression),
"Recipe: Caterium Wire": ItemData(frozenset({G.Recipe}), 1338226, C.progression),
"Recipe: Cable": ItemData(frozenset({G.Recipe}), 1338227, C.progression),
"Recipe: Coated Cable": ItemData(frozenset({G.Recipe}), 1338228, C.progression),
"Recipe: Insulated Cable": ItemData(frozenset({G.Recipe}), 1338229, C.progression),
"Recipe: Quickwire Cable": ItemData(frozenset({G.Recipe}), 1338230, C.progression),
"Recipe: Quickwire": ItemData(frozenset({G.Recipe}), 1338231, C.progression),
"Recipe: Fused Quickwire": ItemData(frozenset({G.Recipe}), 1338232, C.progression),
"Recipe: Copper Sheet": ItemData(frozenset({G.Recipe}), 1338233, C.progression),
"Recipe: Steamed Copper Sheet": ItemData(frozenset({G.Recipe}), 1338234, C.progression),
"Recipe: Steel Pipe": ItemData(frozenset({G.Recipe}), 1338235, C.progression),
"Recipe: Steel Beam": ItemData(frozenset({G.Recipe}), 1338236, C.progression),
"Recipe: Neural-Quantum Processor": ItemData(frozenset({G.Recipe}), 1338237, C.progression), # 1.0
"Recipe: Heavy Oil Residue": ItemData(frozenset({G.Recipe}), 1338238, C.progression),
"Recipe: Polymer Resin": ItemData(frozenset({G.Recipe}), 1338239, C.progression),
"Recipe: Fuel": ItemData(frozenset({G.Recipe}), 1338240, C.progression),
"Recipe: Residual Fuel": ItemData(frozenset({G.Recipe}), 1338241, C.progression),
"Recipe: Diluted Fuel (refinery)": ItemData(frozenset({G.Recipe}), 1338242, C.progression),
"Recipe: AI Expansion Server": ItemData(frozenset({G.Recipe}), 1338243, C.progression), # 1.0
"Recipe: Concrete": ItemData(frozenset({G.Recipe}), 1338244, C.progression),
"Recipe: Rubber Concrete": ItemData(frozenset({G.Recipe}), 1338245, C.progression),
"Recipe: Wet Concrete": ItemData(frozenset({G.Recipe}), 1338246, C.progression),
"Recipe: Fine Concrete": ItemData(frozenset({G.Recipe}), 1338247, C.progression),
"Recipe: Silica": ItemData(frozenset({G.Recipe}), 1338248, C.progression),
"Recipe: Cheap Silica": ItemData(frozenset({G.Recipe}), 1338249, C.progression),
"Recipe: Quartz Crystal": ItemData(frozenset({G.Recipe}), 1338250, C.progression),
"Recipe: Pure Quartz Crystal": ItemData(frozenset({G.Recipe}), 1338251, C.progression),
"Recipe: Iron Ingot": ItemData(frozenset({G.Recipe}), 1338252, C.progression),
"Recipe: Pure Iron Ingot": ItemData(frozenset({G.Recipe}), 1338253, C.progression),
"Recipe: Iron Alloy Ingot": ItemData(frozenset({G.Recipe}), 1338254, C.progression),
"Recipe: Steel Ingot": ItemData(frozenset({G.Recipe}), 1338255, C.progression),
"Recipe: Coke Steel Ingot": ItemData(frozenset({G.Recipe}), 1338256, C.progression),
"Recipe: Compacted Steel Ingot": ItemData(frozenset({G.Recipe}), 1338257, C.progression),
"Recipe: Solid Steel Ingot": ItemData(frozenset({G.Recipe}), 1338258, C.progression),
"Recipe: Copper Ingot": ItemData(frozenset({G.Recipe}), 1338259, C.progression),
"Recipe: Copper Alloy Ingot": ItemData(frozenset({G.Recipe}), 1338260, C.progression),
"Recipe: Pure Copper Ingot": ItemData(frozenset({G.Recipe}), 1338261, C.progression),
"Recipe: Caterium Ingot": ItemData(frozenset({G.Recipe}), 1338262, C.progression),
"Recipe: Pure Caterium Ingot": ItemData(frozenset({G.Recipe}), 1338263, C.progression),
"Recipe: Alien Power Matrix": ItemData(frozenset({G.Recipe}), 1338264), # 1.0
"Recipe: Ficsite Ingot (Aluminum)": ItemData(frozenset({G.Recipe}), 1338265, C.progression), # 1.0
"Recipe: Ficsite Ingot (Caterium)": ItemData(frozenset({G.Recipe}), 1338266, C.progression), # 1.0
"Recipe: Ficsite Ingot (Iron)": ItemData(frozenset({G.Recipe}), 1338267, C.progression), # 1.0
"Recipe: Ficsite Trigon": ItemData(frozenset({G.Recipe}), 1338268, C.progression), # 1.0
"Recipe: Reanimated SAM": ItemData(frozenset({G.Recipe}), 1338269, C.progression), # 1.0
"Recipe: SAM Fluctuator": ItemData(frozenset({G.Recipe}), 1338270, C.progression), # 1.0
"Recipe: Petroleum Coke": ItemData(frozenset({G.Recipe}), 1338271, C.progression),
"Recipe: Compacted Coal": ItemData(frozenset({G.Recipe}), 1338272, C.progression),
"Recipe: Motor": ItemData(frozenset({G.Recipe}), 1338273, C.progression),
"Recipe: Rigor Motor": ItemData(frozenset({G.Recipe}), 1338274, C.progression),
"Recipe: Electric Motor": ItemData(frozenset({G.Recipe}), 1338275, C.progression),
"Recipe: Modular Frame": ItemData(frozenset({G.Recipe}), 1338276, C.progression),
"Recipe: Bolted Frame": ItemData(frozenset({G.Recipe}), 1338277, C.progression),
"Recipe: Steeled Frame": ItemData(frozenset({G.Recipe}), 1338278, C.progression),
"Recipe: Heavy Modular Frame": ItemData(frozenset({G.Recipe}), 1338279, C.progression),
"Recipe: Heavy Flexible Frame": ItemData(frozenset({G.Recipe}), 1338280, C.progression),
"Recipe: Heavy Encased Frame": ItemData(frozenset({G.Recipe}), 1338281, C.progression),
"Recipe: Encased Industrial Beam": ItemData(frozenset({G.Recipe}), 1338282, C.progression),
"Recipe: Encased Industrial Pipe": ItemData(frozenset({G.Recipe}), 1338283, C.progression),
"Recipe: Computer": ItemData(frozenset({G.Recipe}), 1338284, C.progression),
"Recipe: Crystal Computer": ItemData(frozenset({G.Recipe}), 1338285, C.progression),
"Recipe: Caterium Computer": ItemData(frozenset({G.Recipe}), 1338286, C.progression),
"Recipe: Circuit Board": ItemData(frozenset({G.Recipe}), 1338287, C.progression),
"Recipe: Electrode Circuit Board": ItemData(frozenset({G.Recipe}), 1338288, C.progression),
"Recipe: Silicon Circuit Board": ItemData(frozenset({G.Recipe}), 1338289, C.progression),
"Recipe: Caterium Circuit Board": ItemData(frozenset({G.Recipe}), 1338290, C.progression),
"Recipe: Crystal Oscillator": ItemData(frozenset({G.Recipe}), 1338291, C.progression),
"Recipe: Insulated Crystal Oscillator": ItemData(frozenset({G.Recipe}), 1338292, C.progression),
"Recipe: AI Limiter": ItemData(frozenset({G.Recipe}), 1338293, C.progression),
"Recipe: Electromagnetic Control Rod": ItemData(frozenset({G.Recipe}), 1338294, C.progression),
"Recipe: Electromagnetic Connection Rod": ItemData(frozenset({G.Recipe}), 1338295, C.progression),
"Recipe: High-Speed Connector": ItemData(frozenset({G.Recipe}), 1338296, C.progression),
"Recipe: Silicon High-Speed Connector": ItemData(frozenset({G.Recipe}), 1338297, C.progression),
"Recipe: Smart Plating": ItemData(frozenset({G.Recipe}), 1338298, C.progression),
"Recipe: Plastic Smart Plating": ItemData(frozenset({G.Recipe}), 1338299, C.progression),
"Recipe: Versatile Framework": ItemData(frozenset({G.Recipe}), 1338300, C.progression),
"Recipe: Flexible Framework": ItemData(frozenset({G.Recipe}), 1338301, C.progression),
"Recipe: Automated Wiring": ItemData(frozenset({G.Recipe}), 1338302, C.progression),
"Recipe: Automated Speed Wiring": ItemData(frozenset({G.Recipe}), 1338303, C.progression),
"Recipe: Modular Engine": ItemData(frozenset({G.Recipe}), 1338304, C.progression),
"Recipe: Adaptive Control Unit": ItemData(frozenset({G.Recipe}), 1338305, C.progression),
"Recipe: Diluted Fuel": ItemData(frozenset({G.Recipe}), 1338306, C.progression),
"Recipe: Alumina Solution": ItemData(frozenset({G.Recipe}), 1338307, C.progression),
"Recipe: Automated Miner": ItemData(frozenset({G.Recipe}), 1338308, C.progression),
"Recipe: Singularity Cell": ItemData(frozenset({G.Recipe}), 1338309, C.progression), # 1.0
"Recipe: Aluminum Scrap": ItemData(frozenset({G.Recipe}), 1338310, C.progression),
"Recipe: Electrode Aluminum Scrap": ItemData(frozenset({G.Recipe}), 1338311, C.progression),
"Recipe: Instant Scrap": ItemData(frozenset({G.Recipe}), 1338312, C.progression),
"Recipe: Aluminum Ingot": ItemData(frozenset({G.Recipe}), 1338313, C.progression),
"Recipe: Pure Aluminum Ingot": ItemData(frozenset({G.Recipe}), 1338314, C.progression),
"Recipe: Alclad Aluminum Sheet": ItemData(frozenset({G.Recipe}), 1338315, C.progression),
"Recipe: Aluminum Casing": ItemData(frozenset({G.Recipe}), 1338316, C.progression),
"Recipe: Alclad Casing": ItemData(frozenset({G.Recipe}), 1338317, C.progression),
"Recipe: Heat Sink": ItemData(frozenset({G.Recipe}), 1338318, C.progression),
"Recipe: Heat Exchanger": ItemData(frozenset({G.Recipe}), 1338319, C.progression),
"Recipe: Synthetic Power Shard": ItemData(frozenset({G.Recipe}), 1338320, C.progression),
"Recipe: Nitric Acid": ItemData(frozenset({G.Recipe}), 1338321, C.progression),
"Recipe: Fused Modular Frame": ItemData(frozenset({G.Recipe}), 1338322, C.progression),
"Recipe: Heat-Fused Frame": ItemData(frozenset({G.Recipe}), 1338323, C.progression),
"Recipe: Radio Control Unit": ItemData(frozenset({G.Recipe}), 1338324, C.progression),
"Recipe: Radio Connection Unit": ItemData(frozenset({G.Recipe}), 1338325, C.progression),
"Recipe: Radio Control System": ItemData(frozenset({G.Recipe}), 1338326, C.progression),
"Recipe: Pressure Conversion Cube": ItemData(frozenset({G.Recipe}), 1338327, C.progression),
"Recipe: Cooling System": ItemData(frozenset({G.Recipe}), 1338328, C.progression),
"Recipe: Cooling Device": ItemData(frozenset({G.Recipe}), 1338329, C.progression),
"Recipe: Turbo Motor": ItemData(frozenset({G.Recipe}), 1338330, C.progression),
"Recipe: Turbo Electric Motor": ItemData(frozenset({G.Recipe}), 1338331, C.progression),
"Recipe: Turbo Pressure Motor": ItemData(frozenset({G.Recipe}), 1338332, C.progression),
"Recipe: Battery": ItemData(frozenset({G.Recipe}), 1338333, C.progression),
"Recipe: Classic Battery": ItemData(frozenset({G.Recipe}), 1338334, C.progression),
"Recipe: Supercomputer": ItemData(frozenset({G.Recipe}), 1338335, C.progression),
"Recipe: OC Supercomputer": ItemData(frozenset({G.Recipe}), 1338336, C.progression),
"Recipe: Super-State Computer": ItemData(frozenset({G.Recipe}), 1338337, C.progression),
"Recipe: Biochemical Sculptor": ItemData(frozenset({G.Recipe}), 1338338, C.progression), # 1.0
"Recipe: Sulfuric Acid": ItemData(frozenset({G.Recipe}), 1338339, C.progression),
"Recipe: Ballistic Warp Drive": ItemData(frozenset({G.Recipe}), 1338340, C.progression), # 1.0
"Recipe: Encased Uranium Cell": ItemData(frozenset({G.Recipe}), 1338341, C.progression),
"Recipe: Infused Uranium Cell": ItemData(frozenset({G.Recipe}), 1338342, C.progression),
"Recipe: Uranium Fuel Rod": ItemData(frozenset({G.Recipe}), 1338343, C.progression),
"Recipe: Uranium Fuel Unit": ItemData(frozenset({G.Recipe}), 1338344, C.progression),
"Recipe: Aluminum Beam": ItemData(frozenset({G.Recipe}), 1338345, C.progression), # 1.0
"Recipe: Aluminum Rod": ItemData(frozenset({G.Recipe}), 1338346, C.progression), # 1.0
"Recipe: Basic Iron Ingot": ItemData(frozenset({G.Recipe}), 1338347, C.progression), # 1.0
"Recipe: Non-fissile Uranium": ItemData(frozenset({G.Recipe}), 1338348, C.progression),
"Recipe: Fertile Uranium": ItemData(frozenset({G.Recipe}), 1338349, C.progression),
"Recipe: Plutonium Pellet": ItemData(frozenset({G.Recipe}), 1338350),
"Recipe: Encased Plutonium Cell": ItemData(frozenset({G.Recipe}), 1338351),
"Recipe: Instant Plutonium Cell": ItemData(frozenset({G.Recipe}), 1338352),
"Recipe: Plutonium Fuel Rod": ItemData(frozenset({G.Recipe}), 1338353),
"Recipe: Plutonium Fuel Unit": ItemData(frozenset({G.Recipe}), 1338354),
"Recipe: Gas Filter": ItemData(frozenset({G.Recipe}), 1338355, C.progression),
"Recipe: Iodine Infused Filter": ItemData(frozenset({G.Recipe}), 1338356, C.progression),
"Recipe: Assembly Director System": ItemData(frozenset({G.Recipe}), 1338357, C.progression),
"Recipe: Magnetic Field Generator": ItemData(frozenset({G.Recipe}), 1338358, C.progression),
"Recipe: Copper Powder": ItemData(frozenset({G.Recipe}), 1338359, C.progression),
"Recipe: Nuclear Pasta": ItemData(frozenset({G.Recipe}), 1338360, C.progression),
"Recipe: Thermal Propulsion Rocket": ItemData(frozenset({G.Recipe}), 1338361, C.progression),
"Recipe: Ficsonium": ItemData(frozenset({G.Recipe}), 1338362), # 1.0
"Recipe: Ficsonium Fuel Rod": ItemData(frozenset({G.Recipe}), 1338363), # 1.0
"Recipe: Dark Matter Crystal": ItemData(frozenset({G.Recipe}), 1338364, C.progression), # 1.0
"Recipe: Dark Matter Crystallization": ItemData(frozenset({G.Recipe}), 1338365, C.progression), # 1.0
"Recipe: Dark Matter Trap": ItemData(frozenset({G.Recipe}), 1338366, C.progression), # 1.0
"Recipe: Pulse Nobelisk": ItemData(frozenset({G.Recipe}), 1338367, C.useful),
"Recipe: Hatcher Protein": ItemData(frozenset({G.Recipe}), 1338368, C.progression),
"Recipe: Hog Protein": ItemData(frozenset({G.Recipe}), 1338369, C.progression),
"Recipe: Spitter Protein": ItemData(frozenset({G.Recipe}), 1338370, C.progression),
"Recipe: Stinger Protein": ItemData(frozenset({G.Recipe}), 1338371, C.progression),
"Recipe: Biomass (Leaves)": ItemData(frozenset({G.Recipe}), 1338372, C.progression),
"Recipe: Biomass (Wood)": ItemData(frozenset({G.Recipe}), 1338373, C.progression),
"Recipe: Biomass (Mycelia)": ItemData(frozenset({G.Recipe}), 1338374, C.progression),
"Recipe: Biomass (Alien Protein)": ItemData(frozenset({G.Recipe}), 1338375, C.progression),
"Recipe: Turbo Rifle Ammo (Packaged)": ItemData(frozenset({G.Recipe}), 1338376),
"Recipe: Fabric": ItemData(frozenset({G.Recipe}), 1338377, C.progression),
"Recipe: Polyester Fabric": ItemData(frozenset({G.Recipe}), 1338378, C.progression),
"Recipe: Solid Biofuel": ItemData(frozenset({G.Recipe}), 1338379, C.progression),
"Recipe: Liquid Biofuel": ItemData(frozenset({G.Recipe}), 1338380, C.progression),
"Recipe: Empty Canister": ItemData(frozenset({G.Recipe}), 1338381, C.progression),
"Recipe: Coated Iron Canister": ItemData(frozenset({G.Recipe}), 1338382, C.progression),
"Recipe: Steel Canister": ItemData(frozenset({G.Recipe}), 1338383, C.progression),
"Recipe: Empty Fluid Tank": ItemData(frozenset({G.Recipe}), 1338384, C.progression),
"Recipe: Packaged Alumina Solution": ItemData(frozenset({G.Recipe}), 1338385, C.progression),
"Recipe: Packaged Fuel": ItemData(frozenset({G.Recipe}), 1338386, C.progression),
"Recipe: Diluted Packaged Fuel": ItemData(frozenset({G.Recipe}), 1338387, C.progression),
"Recipe: Packaged Heavy Oil Residue": ItemData(frozenset({G.Recipe}), 1338388, C.progression),
"Recipe: Packaged Liquid Biofuel": ItemData(frozenset({G.Recipe}), 1338389, C.progression),
"Recipe: Packaged Nitric Acid": ItemData(frozenset({G.Recipe}), 1338390, C.progression),
"Recipe: Packaged Nitrogen Gas": ItemData(frozenset({G.Recipe}), 1338391, C.progression),
"Recipe: Packaged Oil": ItemData(frozenset({G.Recipe}), 1338392, C.progression),
"Recipe: Packaged Sulfuric Acid": ItemData(frozenset({G.Recipe}), 1338393, C.progression),
"Recipe: Packaged Turbofuel": ItemData(frozenset({G.Recipe}), 1338394, C.progression),
"Recipe: Packaged Water": ItemData(frozenset({G.Recipe}), 1338395, C.progression),
"Recipe: Turbofuel": ItemData(frozenset({G.Recipe}), 1338396, C.progression),
"Recipe: Turbo Heavy Fuel": ItemData(frozenset({G.Recipe}), 1338397, C.progression),
"Recipe: Turbo Blend Fuel": ItemData(frozenset({G.Recipe}), 1338398, C.progression),
"Recipe: Hazmat Suit": ItemData(frozenset({G.Recipe}), 1338399, C.progression),
"Recipe: Gas Mask": ItemData(frozenset({G.Recipe}), 1338400, C.progression),
"Recipe: Black Powder": ItemData(frozenset({G.Recipe}), 1338401, C.progression),
"Recipe: Blade Runners": ItemData(frozenset({G.Recipe}), 1338402, C.useful),
"Recipe: Chainsaw": ItemData(frozenset({G.Recipe}), 1338403, C.useful),
"Recipe: Cluster Nobelisk": ItemData(frozenset({G.Recipe}), 1338404),
"Recipe: Explosive Rebar": ItemData(frozenset({G.Recipe}), 1338405),
"Recipe: Factory Cart": ItemData(frozenset({G.Recipe}), 1338406, C.useful),
"Recipe: Gas Nobelisk": ItemData(frozenset({G.Recipe}), 1338407),
"Recipe: Golden Factory Cart": ItemData(frozenset({G.Recipe}), 1338408),
"Recipe: Homing Rifle Ammo": ItemData(frozenset({G.Recipe}), 1338409),
"Recipe: Iron Rebar": ItemData(frozenset({G.Recipe}), 1338410, C.progression),
"Recipe: Nobelisk": ItemData(frozenset({G.Recipe}), 1338411, C.progression),
"Recipe: Nuke Nobelisk": ItemData(frozenset({G.Recipe}), 1338412),
"Recipe: Nutritional Inhaler": ItemData(frozenset({G.Recipe}), 1338413, C.useful),
"Recipe: Object Scanner": ItemData(frozenset({G.Recipe}), 1338414, C.progression),
"Recipe: Parachute": ItemData(frozenset({G.Recipe}), 1338415, C.useful),
"Recipe: Protein Inhaler": ItemData(frozenset({G.Recipe}), 1338416, C.useful),
"Recipe: Rebar Gun": ItemData(frozenset({G.Recipe}), 1338417, C.useful),
"Recipe: Rifle": ItemData(frozenset({G.Recipe}), 1338418, C.useful),
"Recipe: Rifle Ammo": ItemData(frozenset({G.Recipe}), 1338419, C.progression),
"Recipe: Shatter Rebar": ItemData(frozenset({G.Recipe}), 1338420),
"Recipe: Stun Rebar": ItemData(frozenset({G.Recipe}), 1338421),
"Recipe: Therapeutic Inhaler": ItemData(frozenset({G.Recipe}), 1338422, C.useful),
"Recipe: Turbo Rifle Ammo": ItemData(frozenset({G.Recipe}), 1338423),
"Recipe: Vitamin Inhaler": ItemData(frozenset({G.Recipe}), 1338424, C.useful),
"Recipe: Xeno-Basher": ItemData(frozenset({G.Recipe}), 1338425, C.useful),
"Recipe: Xeno-Zapper": ItemData(frozenset({G.Recipe}), 1338426, C.useful),
"Recipe: Zipline": ItemData(frozenset({G.Recipe}), 1338427, C.useful),
"Recipe: Fine Black Powder": ItemData(frozenset({G.Recipe}), 1338428, C.progression),
"Recipe: Smokeless Powder": ItemData(frozenset({G.Recipe}), 1338429, C.progression),
"Recipe: Alien DNA Capsule": ItemData(frozenset({G.Recipe}), 1338430, C.progression),
"Recipe: Power Shard (1)": ItemData(frozenset({G.Recipe}), 1338431, C.progression),
"Recipe: Power Shard (2)": ItemData(frozenset({G.Recipe}), 1338432, C.useful),
"Recipe: Power Shard (5)": ItemData(frozenset({G.Recipe}), 1338433, C.useful),
# 1.0
"Recipe: Diamonds": ItemData(frozenset({G.Recipe}), 1338434, C.progression),
"Recipe: Cloudy Diamonds": ItemData(frozenset({G.Recipe}), 1338435, C.progression),
"Recipe: Oil-Based Diamonds": ItemData(frozenset({G.Recipe}), 1338436, C.progression),
"Recipe: Petroleum Diamonds": ItemData(frozenset({G.Recipe}), 1338437, C.progression),
"Recipe: Pink Diamonds": ItemData(frozenset({G.Recipe}), 1338438, C.progression),
"Recipe: Turbo Diamonds": ItemData(frozenset({G.Recipe}), 1338439, C.progression),
"Recipe: Time Crystal": ItemData(frozenset({G.Recipe}), 1338440, C.progression),
"Recipe: Superposition Oscillator": ItemData(frozenset({G.Recipe}), 1338441, C.progression),
#"Recipe: Excited Photonic Matter": ItemData(frozenset({G.Recipe}), 1338442, C.progression), should probably be unlocked with converter
"Recipe: Rocket Fuel": ItemData(frozenset({G.Recipe}), 1338443, C.progression),
"Recipe: Nitro Rocket Fuel": ItemData(frozenset({G.Recipe}), 1338444, C.progression),
"Recipe: Ionized Fuel": ItemData(frozenset({G.Recipe}), 1338445, C.useful),
"Recipe: Packaged Rocket Fuel": ItemData(frozenset({G.Recipe}), 1338446, C.progression),
"Recipe: Packaged Ionized Fuel": ItemData(frozenset({G.Recipe}), 1338447, C.useful),
"Recipe: Dark-Ion Fuel": ItemData(frozenset({G.Recipe}), 1338448, C.useful),
"Recipe: Quartz Purification": ItemData(frozenset({G.Recipe}), 1338449, C.progression),
"Recipe: Fused Quartz Crystal": ItemData(frozenset({G.Recipe}), 1338450, C.progression),
"Recipe: Leached Caterium Ingot": ItemData(frozenset({G.Recipe}), 1338451, C.progression),
"Recipe: Leached Copper Ingot": ItemData(frozenset({G.Recipe}), 1338452, C.progression),
"Recipe: Leached Iron ingot": ItemData(frozenset({G.Recipe}), 1338453, C.progression),
"Recipe: Molded Beam": ItemData(frozenset({G.Recipe}), 1338454, C.progression),
"Recipe: Molded Steel Pipe": ItemData(frozenset({G.Recipe}), 1338455, C.progression),
"Recipe: Plastic AI Limiter": ItemData(frozenset({G.Recipe}), 1338456, C.progression),
"Recipe: Tempered Caterium Ingot": ItemData(frozenset({G.Recipe}), 1338457, C.progression),
"Recipe: Tempered Copper Ingot": ItemData(frozenset({G.Recipe}), 1338458, C.progression),
# 1.0
#1338459 - 1338599 Reserved for future recipes
#1338400 - 1338899 buildings / others
"Building: Constructor": ItemData(frozenset({G.Building}), 1338600, C.progression),
"Building: Assembler": ItemData(frozenset({G.Building}), 1338601, C.progression),
"Building: Manufacturer": ItemData(frozenset({G.Building}), 1338602, C.progression),
"Building: Packager": ItemData(frozenset({G.Building}), 1338603, C.progression),
"Building: Refinery": ItemData(frozenset({G.Building}), 1338604, C.progression),
"Building: Blender": ItemData(frozenset({G.Building}), 1338605, C.progression),
"Building: Particle Accelerator": ItemData(frozenset({G.Building}), 1338606, C.progression),
"Building: Biomass Burner": ItemData(frozenset({G.Building}), 1338607, C.progression),
"Building: Coal Generator": ItemData(frozenset({G.Building}), 1338608, C.progression),
"Building: Geothermal Generator": ItemData(frozenset({G.Building}), 1338609, C.progression),
"Building: Nuclear Power Plant": ItemData(frozenset({G.Building}), 1338610, C.progression),
"Building: Miner Mk.1": ItemData(frozenset({G.Building}), 1338611, C.progression),
"Building: Miner Mk.2": ItemData(frozenset({G.Building}), 1338612, C.progression),
"Building: Miner Mk.3": ItemData(frozenset({G.Building}), 1338613, C.progression),
"Building: Oil Extractor": ItemData(frozenset({G.Building}), 1338614, C.progression),
"Building: Water Extractor": ItemData(frozenset({G.Building}), 1338615, C.progression),
"Building: Smelter": ItemData(frozenset({G.Building}), 1338616, C.progression),
"Building: Foundry": ItemData(frozenset({G.Building}), 1338617, C.progression),
"Building: Fuel Generator": ItemData(frozenset({G.Building}), 1338618, C.progression),
"Building: Resource Well Pressurizer": ItemData(frozenset({G.Building}), 1338619, C.progression),
"Building: Equipment Workshop": ItemData(frozenset({G.Building}), 1338620, C.progression),
"Building: AWESOME Sink": ItemData(frozenset({G.Building}), 1338621, C.progression),
"Building: AWESOME Shop": ItemData(frozenset({G.Building}), 1338622, C.progression),
"Building: Painted Beams": ItemData(frozenset({G.Beams}), 1338623, C.filler),
"Building: Blueprint Designer": ItemData(frozenset({G.Building}), 1338624, C.filler),
"Building: Fluid Buffer": ItemData(frozenset({G.Building}), 1338625, C.filler),
"Building: Industrial Fluid Buffer": ItemData(frozenset({G.Building}), 1338626, C.filler),
"Building: Jump Pad": ItemData(frozenset({G.Building}), 1338627, C.filler),
"Building: Ladder": ItemData(frozenset({G.Building}), 1338628, C.filler),
"Building: MAM": ItemData(frozenset({G.Building}), 1338629, C.progression),
"Building: Personal Storage Box": ItemData(frozenset({G.Building}), 1338630, C.filler),
"Building: Power Storage": ItemData(frozenset({G.Building}), 1338631, C.progression),
"Building: U-Jelly Landing Pad": ItemData(frozenset({G.Building}), 1338632, C.useful),
"Building: Power Switch": ItemData(frozenset({G.Building}), 1338633, C.useful),
"Building: Priority Power Switch": ItemData(frozenset({G.Building}), 1338634, C.useful),
"Building: Storage Container": ItemData(frozenset({G.Building}), 1338635, C.useful, 0),
"Building: Lookout Tower": ItemData(frozenset({G.Building}), 1338636, C.filler),
#"Building: Power Pole Mk.1": ItemData(frozenset({G.Building}), 1338637, C.progression), # available from start
"Building: Power Pole Mk.2": ItemData(frozenset({G.Building}), 1338638, C.useful),
"Building: Power Pole Mk.3": ItemData(frozenset({G.Building}), 1338639, C.useful),
"Building: Industrial Storage Container": ItemData(frozenset({G.Building}), 1338640, C.filler),
"Building: Conveyor Merger": ItemData(frozenset({G.Building}), 1338641, C.progression),
"Building: Conveyor Splitter": ItemData(frozenset({G.Building}), 1338642, C.progression),
"Building: Conveyor Mk.1": ItemData(frozenset({G.Building, G.ConveyorMk1}), 1338643, C.progression),
"Building: Conveyor Mk.2": ItemData(frozenset({G.Building, G.ConveyorMk2}), 1338644, C.progression),
"Building: Conveyor Mk.3": ItemData(frozenset({G.Building, G.ConveyorMk3}), 1338645, C.progression),
"Building: Conveyor Mk.4": ItemData(frozenset({G.Building, G.ConveyorMk4}), 1338646, C.progression),
"Building: Conveyor Mk.5": ItemData(frozenset({G.Building, G.ConveyorMk5}), 1338647, C.progression),
"Building: Conveyor Lift Mk.1": ItemData(frozenset({G.Building, G.ConveyorMk1}), 1338648, C.useful),
"Building: Conveyor Lift Mk.2": ItemData(frozenset({G.Building, G.ConveyorMk2}), 1338649, C.useful),
"Building: Conveyor Lift Mk.3": ItemData(frozenset({G.Building, G.ConveyorMk3}), 1338650, C.useful),
"Building: Conveyor Lift Mk.4": ItemData(frozenset({G.Building, G.ConveyorMk4}), 1338651, C.useful),
"Building: Conveyor Lift Mk.5": ItemData(frozenset({G.Building, G.ConveyorMk5}), 1338652, C.useful),
"Building: Metal Beams": ItemData(frozenset({G.Beams}), 1338653, C.filler, 0),
"Building: Stackable Conveyor Pole": ItemData(frozenset({G.Building, G.ConveyorSupports}), 1338654, C.useful),
"Building: Conveyor Wall Mount": ItemData(frozenset({G.Building, G.ConveyorSupports}), 1338655, C.useful, 0),
"Building: Conveyor Lift Floor Hole": ItemData(frozenset({G.Building, G.ConveyorSupports}), 1338656, C.useful, 0),
"Building: Conveyor Ceiling Mount": ItemData(frozenset({G.Building, G.ConveyorSupports}), 1338657, C.useful, 0),
"Building: Pipes Mk.1": ItemData(frozenset({G.Building, G.PipesMk1}), 1338658, C.progression),
"Building: Pipes Mk.2": ItemData(frozenset({G.Building, G.PipesMk2}), 1338659, C.progression),
"Building: Pipeline Pump Mk.1": ItemData(frozenset({G.Building, G.PipesMk1}), 1338660, C.progression),
"Building: Pipeline Pump Mk.2": ItemData(frozenset({G.Building, G.PipesMk2}), 1338661, C.progression),
"Building: Pipeline Junction Cross": ItemData(frozenset({G.Building, G.PipesMk1, G.PipesMk2}), 1338662, C.progression),
"Building: Valve": ItemData(frozenset({G.Building, G.PipesMk1, G.PipesMk2}), 1338663, C.useful),
"Building: Stackable Pipeline Support": ItemData(frozenset({G.Building, G.PipelineSupports}), 1338664, C.useful, 0),
"Building: Wall Pipeline Support": ItemData(frozenset({G.Building, G.PipelineSupports}), 1338665, C.useful, 0),
"Building: Pipeline Wall Hole": ItemData(frozenset({G.Building, G.PipelineSupports}), 1338666, C.useful, 0),
"Building: Pipeline Floor Hole": ItemData(frozenset({G.Building, G.PipelineSupports}), 1338667, C.useful, 0),
"Building: Lights Control Panel": ItemData(frozenset({G.Building, G.Lights}), 1338668, C.filler, 0),
"Building: Wall Mounted Flood Light": ItemData(frozenset({G.Building, G.Lights}), 1338669, C.filler, 0),
"Building: Street Light": ItemData(frozenset({G.Building, G.Lights}), 1338670, C.filler, 0),
"Building: Flood Light Tower": ItemData(frozenset({G.Building, G.Lights}), 1338671, C.filler, 0),
"Building: Ceiling Light": ItemData(frozenset({G.Building, G.Lights}), 1338672, C.filler, 0),
"Building: Power Tower": ItemData(frozenset({G.Building}), 1338673, C.useful),
"Building: Walls Orange": ItemData(frozenset({G.Building, G.Walls}), 1338674, C.progression),
"Building: Radar Tower": ItemData(frozenset({G.Building}), 1338675, C.useful),
"Building: Smart Splitter": ItemData(frozenset({G.Building}), 1338676, C.useful),
"Building: Programmable Splitter": ItemData(frozenset({G.Building}), 1338677, C.useful),
"Building: Label Sign Bundle": ItemData(frozenset({G.Building, G.Signs}), 1338678, C.filler, 0),
"Building: Display Sign Bundle": ItemData(frozenset({G.Building, G.Signs}), 1338679, C.filler, 0),
"Building: Billboard Set": ItemData(frozenset({G.Building, G.Signs}), 1338680, C.filler, 0),
"Building: Walls Metal": ItemData(frozenset({G.Building, G.Walls}), 1338681, C.filler, 0),
"Building: Metal Pillar": ItemData(frozenset({G.Pilars}), 1338682, C.filler, 0),
"Building: Concrete Pillar": ItemData(frozenset({G.Pilars}), 1338683, C.filler, 0),
"Building: Frame Pillar": ItemData(frozenset({G.Pilars}), 1338684, C.filler, 0),
"Building: Walls Concrete": ItemData(frozenset({G.Building, G.Walls}), 1338685, C.filler, 0),
#"Building: Big Metal Pillar": ItemData(frozenset({G.Pilars}), 1338686, C.filler, 0),
#"Building: Big Concrete Pillar": ItemData(frozenset({G.Pilars}), 1338687, C.filler, 0),
#"Building: Big Frame Pillar": ItemData(frozenset({G.Pilars}), 1338688, C.filler, 0),
#"Building: Beam Support": ItemData(frozenset({G.Beams}), 1338689, C.filler, 0),
#"Building: Beam Connector": ItemData(frozenset({G.Beams}), 1338690, C.filler, 0),
#"Building: Beam Connector Double": ItemData(frozenset({G.Beams}), 1338691, C.filler, 0),
"Building: Foundation": ItemData(frozenset({G.Building, G.Foundations}), 1338692, C.progression),
"Building: Half Foundation": ItemData(frozenset({G.Foundations}), 1338693, C.filler, 0),
"Building: Corner Ramp Pack": ItemData(frozenset({G.Foundations}), 1338694, C.filler, 0),
"Building: Inverted Ramp Pack": ItemData(frozenset({G.Foundations}), 1338695, C.filler, 0),
"Building: Inverted Corner Ramp Pack": ItemData(frozenset({G.Foundations}), 1338696, C.filler, 0),
"Building: Quarter Pipes Pack": ItemData(frozenset({G.Foundations}), 1338697, C.filler, 0),
"Building: Quarter Pipe Extensions Pack": ItemData(frozenset({G.Foundations}), 1338698, C.filler, 0),
"Building: Frame foundation": ItemData(frozenset({G.Foundations}), 1338699, C.filler, 0),
"Building: Wall Outlet Mk.1": ItemData(frozenset({G.Building}), 1338700, C.useful),
"Building: Wall Outlet Mk.2": ItemData(frozenset({G.Building}), 1338701, C.useful),
"Building: Wall Outlet Mk.3": ItemData(frozenset({G.Building}), 1338702, C.useful),
"Building: Modern Catwalks": ItemData(frozenset({G.Building}), 1338703, C.filler, 0),
"Building: Industrial Walkways": ItemData(frozenset({G.Building}), 1338704, C.filler, 0),
"Building: Stairs": ItemData(frozenset({G.Building}), 1338705, C.filler, 0),
"Building: Clean Pipeline Mk.1": ItemData(frozenset({G.Building}), 1338706, C.filler, 0),
"Building: Clean Pipeline Mk.2": ItemData(frozenset({G.Building}), 1338707, C.filler, 0),
"Building: Road Barrier": ItemData(frozenset({G.Building}), 1338708, C.filler, 0),
"Building: Modern Railing": ItemData(frozenset({G.Building}), 1338709, C.filler, 0),
"Building: Industrial Railing": ItemData(frozenset({G.Building}), 1338710, C.filler, 0),
"Building: Double Ramp Pack": ItemData(frozenset({G.Foundations}), 1338711, C.filler, 0),
"Building: Conveyor Walls": ItemData(frozenset({G.Walls}), 1338712, C.filler, 0),
"Building: Inverted Ramp Wall Bundle": ItemData(frozenset({G.Walls}), 1338713, C.filler, 0),
"Building: Ramp Wall Bundle": ItemData(frozenset({G.Walls}), 1338714, C.filler, 0),
"Building: Door Walls": ItemData(frozenset({G.Walls}), 1338715, C.filler, 0),
"Building: Tilted Walls": ItemData(frozenset({G.Walls}), 1338716, C.filler, 0),
"Building: Windowed Walls": ItemData(frozenset({G.Walls}), 1338717, C.filler, 0),
"Building: Steel-framed Windows": ItemData(frozenset({G.Walls}), 1338718, C.filler, 0),
"Building: Gates": ItemData(frozenset({G.Walls}), 1338719, C.filler, 0),
"Building: Roofs": ItemData(frozenset({G.Walls}), 1338720, C.filler, 0),
"Building: Roof Corners": ItemData(frozenset({G.Walls}), 1338721, C.filler, 0),
# 1.0
"Building: Converter": ItemData(frozenset({G.Building}), 1338722, C.progression),
"Building: Quantum Encoder": ItemData(frozenset({G.Building}), 1338723, C.progression),
"Building: Portal": ItemData(frozenset({G.Building}), 1338724, C.filler),
"Building: Conveyor Mk.6": ItemData(frozenset({G.Building, G.ConveyorMk6}), 1338725, C.progression),
"Building: Conveyor Lift Mk.6": ItemData(frozenset({G.Building, G.ConveyorMk6}), 1338726, C.useful),
"Building: Alien Power Augmenter": ItemData(frozenset({G.Building}), 1338727, C.progression),
"Building: Dimensional Depot Uploader": ItemData(frozenset({G.Building}), 1338728, C.useful),
# 1.0
#1338729 - 1338749 Reserved for Cosmetics
"Customizer: Asphalt Foundation Material": ItemData(frozenset({G.Customizer, G.Foundations}), 1338750, C.filler, 0),
"Customizer: Concrete Foundation Material": ItemData(frozenset({G.Customizer, G.Foundations}), 1338751, C.filler, 0),
"Customizer: Concrete Wall Material": ItemData(frozenset({G.Customizer, G.Walls}), 1338752, C.filler, 0),
"Customizer: Glass Roof Material": ItemData(frozenset({G.Customizer, G.Walls}), 1338753, C.filler, 0),
"Customizer: Grip Metal Foundation Material": ItemData(frozenset({G.Customizer, G.Foundations}), 1338754, C.filler, 0),
"Customizer: Coated Concrete Foundation Material": ItemData(frozenset({G.Customizer, G.Foundations}), 1338755, C.filler, 0),
"Customizer: Metal Roof Material": ItemData(frozenset({G.Customizer, G.Walls}), 1338756, C.filler, 0),
"Customizer: Steel Wall Material": ItemData(frozenset({G.Customizer, G.Walls}), 1338757, C.filler, 0),
"Customizer: Tar Roof Material": ItemData(frozenset({G.Customizer, G.Walls}), 1338758, C.filler, 0),
"Customizer: Arrow Patterns": ItemData(frozenset({G.Customizer, G.Foundations}), 1338759, C.filler, 0),
"Customizer: Dotted Line Patterns": ItemData(frozenset({G.Customizer, G.Foundations}), 1338760, C.filler, 0),
"Customizer: Solid Line Patterns": ItemData(frozenset({G.Customizer, G.Foundations}), 1338761, C.filler, 0),
"Customizer: Factory Icon Patterns": ItemData(frozenset({G.Customizer, G.Foundations}), 1338762, C.filler, 0),
"Customizer: Transportation Icon Patterns": ItemData(frozenset({G.Customizer, G.Foundations}), 1338763, C.filler, 0),
"Customizer: Number Patterns": ItemData(frozenset({G.Customizer, G.Foundations}), 1338764, C.filler, 0),
"Customizer: Pathway Patterns": ItemData(frozenset({G.Customizer, G.Foundations}), 1338765, C.filler, 0),
"Customizer: Factory Zone Patterns": ItemData(frozenset({G.Customizer, G.Foundations}), 1338766, C.filler, 0),
# 1.0
"Customizer: Steel-Framed Windows": ItemData(frozenset({G.Customizer, G.Walls}), 1338767, C.filler, 0),
"Customizer: Construction Fences": ItemData(frozenset({G.Customizer}), 1338768, C.filler, 0),
"Customizer: Unpainted Finish": ItemData(frozenset({G.Customizer}), 1338769, C.filler, 0),
"Customizer: Copper Paint Finish": ItemData(frozenset({G.Customizer}), 1338770, C.filler, 0),
"Customizer: Chrome Paint Finish": ItemData(frozenset({G.Customizer}), 1338771, C.filler, 0),
"Customizer: Carbon Steel Finish": ItemData(frozenset({G.Customizer}), 1338772, C.filler, 0),
"Customizer: Caterium Paint Finish": ItemData(frozenset({G.Customizer}), 1338773, C.filler, 0),
# 1.0
#1338773 - 1338799 Reserved for buildings
# Transports 1338800 - 1338898
# Drones (including Drone)
"Transport: Drones": ItemData(frozenset({G.Transport}), 1338800, C.useful),
# Trains (including Empty Platform, rails, station, locomotive)
"Transport: Trains": ItemData(frozenset({G.Transport, G.Trains}), 1338801, C.useful),
"Transport: Fluid Trains": ItemData(frozenset({G.Transport, G.Trains}), 1338802, C.useful),
# Tracker / Truck (including truck station)
"Transport: Tractor": ItemData(frozenset({G.Transport, G.Vehicles}), 1338803, C.useful),
"Transport: Truck": ItemData(frozenset({G.Transport, G.Vehicles}), 1338804, C.useful),
"Transport: Explorer": ItemData(frozenset({G.Transport, G.Vehicles}), 1338805, C.useful),
"Transport: Factory Cart": ItemData(frozenset({G.Transport, G.Vehicles}), 1338806, C.useful),
"Transport: Factory Cart (golden)": ItemData(frozenset({G.Transport, G.Vehicles}), 1338807, C.filler),
"Transport: Cyber Wagon": ItemData(frozenset({G.Transport, G.Vehicles}), 1338808, C.filler),
# Hypertubes (including supports / pipes / entrance / holes)
"Transport: Hypertube": ItemData(frozenset({G.Transport, G.HyperTubes}), 1338809, C.useful),
"Transport: Hypertube Floor Hole": ItemData(frozenset({G.Transport, G.HyperTubes}), 1338810, C.filler),
"Transport: Hypertube Wall Support": ItemData(frozenset({G.Transport, G.HyperTubes}), 1338811, C.filler),
"Transport: Hypertube Wall Hole": ItemData(frozenset({G.Transport, G.HyperTubes}), 1338812, C.filler),
#1338900 - 1338998 Handled by trap system (includes a few non-trap things)
# Regenerate via /Script/Blutility.EditorUtilityWidgetBlueprint'/Archipelago/Debug/EU_GenerateTrapIds.EU_GenerateTrapIds'
"Trap: Hog": ItemData(frozenset({G.Trap}), 1338900, C.trap),
"Trap: Alpha Hog": ItemData(frozenset({G.Trap}), 1338901, C.trap),
"Trap: Johnny": ItemData(frozenset({G.Trap}), 1338902, C.trap),
"Trap: Cliff Hog": ItemData(frozenset({G.Trap}), 1338903, C.trap),
"Trap: Nuclear Hog": ItemData(frozenset({G.Trap}), 1338904, C.trap),
"Trap: Not the Bees": ItemData(frozenset({G.Trap}), 1338905, C.trap),
"Trap: Hatcher": ItemData(frozenset({G.Trap}), 1338906, C.trap),
"Trap: Doggo with Pulse Nobelisk": ItemData(frozenset({G.Trap}), 1338907, C.trap),
"Trap: Doggo with Nuke Nobelisk": ItemData(frozenset({G.Trap}), 1338908, C.trap),
"Doggo with Power Slug": ItemData(frozenset({G.Parts}), 1338909, C.filler),
"Trap: Doggo with Gas Nobelisk": ItemData(frozenset({G.Trap}), 1338910, C.trap),
"Trap: Spore Flower": ItemData(frozenset({G.Trap}), 1338911, C.trap),
"Trap: Stinger": ItemData(frozenset({G.Trap}), 1338912, C.trap),
"Trap: Gas Stinger": ItemData(frozenset({G.Trap}), 1338913, C.trap),
"Trap: Small Stinger": ItemData(frozenset({G.Trap}), 1338914, C.trap),
"Trap: Spitter": ItemData(frozenset({G.Trap}), 1338915, C.trap),
"Trap: Alpha Spitter": ItemData(frozenset({G.Trap}), 1338916, C.trap),
"Trap: Nuclear Waste Drop": ItemData(frozenset({G.Trap}), 1338917, C.trap),
"Trap: Plutonium Waste Drop": ItemData(frozenset({G.Trap}), 1338918, C.trap),
"Trap: Elite Hatcher": ItemData(frozenset({G.Trap}), 1338919, C.trap),
"Trap: Can of Beans": ItemData(frozenset({G.Trap}), 1338920, C.trap),
"Trap: Fart Cloud": ItemData(frozenset({G.Trap}), 1338921, C.trap),
#Item id range upper bound
"Building: Space Elevator": ItemData(frozenset({G.Building}), 1338999, C.progression)
}
non_unique_item_categories: ClassVar[Set[G]] = frozenset({ G.Parts, G.Equipment, G.Ammo, G.Trap, G.Upgrades })
pool_item_categories: ClassVar[Set[G]] = frozenset({G.Recipe, G.Building, G.Equipment, G.Transport, G.Upgrades})
item_names_and_ids: ClassVar[Dict[str, int]] = {name: item_data.code for name, item_data in item_data.items()}
filler_items: ClassVar[Tuple[str, ...]] = tuple(item for item, details in item_data.items()
if not details.category.isdisjoint(frozenset({ G.Parts, G.Ammo })))
@classmethod
def get_item_names_per_category(cls) -> Dict[str, Set[str]]:
categories: Dict[str, Set[str]] = {}
for name, data in cls.item_data.items():
for category in data.category:
categories.setdefault(category.name, set()).add(name)
return categories
player: int
logic: GameLogic
random: Random
precalculated_progression_recipes: Optional[Dict[str, Recipe]]
precalculated_progression_recipes_names: Optional[Set[str]]
def __init__(self, player: Optional[int], logic: GameLogic, random: Random, options: SatisfactoryOptions):
self.player = player
self.logic = logic
self.random = random
if options.experimental_generation: # TODO major performance boost if we can get it stable
self.precalculated_progression_recipes = self.select_progression_recipes()
self.precalculated_progression_recipes_names = set(
recipe.name for recipe in self.precalculated_progression_recipes.values()
)
else:
self.precalculated_progression_recipes = None
self.precalculated_progression_recipes_names = None
def select_recipe_for_part_that_does_not_depend_on_parent_recipes(self,
part: str, parts_to_avoid: Dict[str, str]) -> Recipe:
recipes: List[Recipe] = list(self.logic.recipes[part])
implicit_recipe = next(filter(lambda r: r.implicitly_unlocked, recipes), None)
if implicit_recipe:
return implicit_recipe
while (len(recipes) > 0):
recipe: Recipe = recipes.pop(self.random.randrange(len(recipes)))
if recipe.inputs and any(input in parts_to_avoid for input in recipe.inputs):
continue
return recipe
raise Exception(f"No recipe available for {part}")
def build_progression_recipe_tree(self, parts: tuple[str, ...], selected_recipes: Dict[str, str]):
for part in parts:
recipe: Recipe = \
self.select_recipe_for_part_that_does_not_depend_on_parent_recipes(part, selected_recipes)
selected_recipes[part] = recipe.name
child_recipes: Dict[str, Recipe] = {}
if (recipe.inputs):
for input in recipe.inputs:
child_recipes[input] = \
self.select_recipe_for_part_that_does_not_depend_on_parent_recipes(input, selected_recipes)
for part, child_recipe in child_recipes.items():
selected_recipes[part] = child_recipe.name
for child_recipe in child_recipes.values():
if child_recipe.inputs:
self.build_progression_recipe_tree(child_recipe.inputs, selected_recipes)
def select_progression_recipes(self) -> Dict[str, Recipe]:
selected_recipes: Dict[str, Recipe] = {}
while not self.is_beatable(selected_recipes):
selected_recipes = self.select_random_progression_recipes()
return selected_recipes
def is_beatable(self, recipes: Dict[str, Recipe]) -> bool:
if not recipes:
return False
craftable_parts: Set[str] = set()
pending_recipes_by_part: Dict[str, Recipe] = copy.deepcopy(recipes)
for part, recipe_tuples in self.logic.recipes.items():
for recipe in recipe_tuples:
if recipe.implicitly_unlocked:
craftable_parts.add(part)
while pending_recipes_by_part:
new_collected_parts: Set[str] = set()
for part, recipe in pending_recipes_by_part.items():
if all(input in craftable_parts for input in recipe.inputs):
new_collected_parts.add(part)
if not new_collected_parts:
return False
craftable_parts = craftable_parts.union(new_collected_parts)
for part in new_collected_parts:
del pending_recipes_by_part[part]
return True
def select_random_progression_recipes(self) -> Dict[str, Recipe]:
selected_recipes: Dict[str, str] = {}
for part, recipes in self.logic.recipes.items():
implicit_recipe: Recipe = next(filter(lambda r: r.implicitly_unlocked, recipes), None)
if implicit_recipe:
continue
selected_recipes[part] = self.random.choice(recipes)
return selected_recipes
@classmethod
def create_item(cls, instance: Optional["Items"], name: str, player: int) -> Item:
data: ItemData = cls.item_data[name]
type = data.type
if type == C.progression and not name.startswith("Building: ") and \
instance and instance.precalculated_progression_recipes_names:
if name not in instance.precalculated_progression_recipes_names:
type = C.useful
logging.info(f"Downscaling .. {name}")
else:
logging.warn(f"Preserving .. {name}")
return Item(name, type, data.code, player)
def get_filler_item_name(self, random: Random, options: SatisfactoryOptions) -> str:
trap_chance: int = options.trap_chance.value
enabled_traps: List[str] = options.trap_selection_override.value
if enabled_traps and random.random() < (trap_chance / 100):
return random.choice(enabled_traps)
else:
return random.choice(self.filler_items)
def get_excluded_items(self, multiworld: MultiWorld, options: SatisfactoryOptions) -> Set[str]:
excluded_items: Set[str] = set()
for item in multiworld.precollected_items[self.player]:
if item.name in self.item_data \
and self.item_data[item.name].category.isdisjoint(self.non_unique_item_categories) \
and item.name not in options.start_inventory_from_pool:
excluded_items.add(item.name)
return excluded_items
def build_item_pool(self, random: Random, multiworld: MultiWorld,
options: SatisfactoryOptions, number_of_locations: int) -> List[Item]:
excluded_from_pool: Set[str] = self.get_excluded_items(multiworld, options) \
.union(self.logic.implicitly_unlocked_recipes.keys())
pool: List[Item] = []
for name, data in self.item_data.items():
if name == "Building: Blueprint Designer":
halp = 10
if data.count > 0 \
and not data.category.isdisjoint(self.pool_item_categories) \
and name not in excluded_from_pool:
for _ in range(data.count):
item = self.create_item(self, name, self.player)
pool.append(item)
for _ in range(number_of_locations - len(pool)):
item = self.create_item(self, self.get_filler_item_name(random, options), self.player)
pool.append(item)
return pool
def write_progression_chain(self, multiworld: MultiWorld, spoiler_handle: TextIO):
if self.precalculated_progression_recipes:
player_name = f'{multiworld.get_player_name(self.player)}: ' if multiworld.players > 1 else ''
spoiler_handle.write('\n\nSelected Satisfactory Recipes:\n\n')
spoiler_handle.write('\n'.join(
f"{player_name}{part} -> {recipe.name}"
for part, recipes_per_part in self.logic.recipes.items()
for recipe in recipes_per_part
if recipe.name in self.precalculated_progression_recipes_names
))

View File

@@ -0,0 +1,396 @@
from typing import List, Optional, Callable, Tuple, Dict, Iterable, ClassVar
from BaseClasses import CollectionState
from .GameLogic import GameLogic, Recipe, Building, PowerInfrastructureLevel, DropPodData
from .StateLogic import StateLogic, EventId, part_event_prefix, building_event_prefix
from .Items import Items
from .Options import SatisfactoryOptions
from math import ceil, floor
class LocationData():
region: str
name: str
event_name: str
code: Optional[int]
non_progression: Optional[bool]
rule: Optional[Callable[[CollectionState], bool]]
def __init__(self, region: str, name: str, code: Optional[int], event_name: Optional[str] = None,
non_progression: Optional[bool] = False, rule: Optional[Callable[[CollectionState], bool]] = None):
self.region = region
self.name = name
self.code = code
self.rule = rule
self.non_progression = non_progression
self.event_name = event_name or name
class Part(LocationData):
@staticmethod
def get_parts(state_logic: StateLogic, recipes: Tuple[Recipe, ...], name: str, items: Items) -> List[LocationData]:
recipes_per_region: Dict[str, List[Recipe]] = {}
for recipe in recipes:
recipes_per_region.setdefault(recipe.building or "Overworld", []).append(recipe)
return [Part(state_logic, region, recipes_for_region, name, items)
for region, recipes_for_region in recipes_per_region.items()]
def __init__(self, state_logic: StateLogic, region: str, recipes: Iterable[Recipe], name: str, items: Items):
super().__init__(region, part_event_prefix + name + region, EventId, part_event_prefix + name,
rule = self.can_produce_any_recipe_for_part(state_logic, recipes, name, items))
def can_produce_any_recipe_for_part(self, state_logic: StateLogic, recipes: Iterable[Recipe],
name: str, items: Items) -> Callable[[CollectionState], bool]:
def can_build_by_any_recipe(state: CollectionState) -> bool:
if name == "Steel Ingot":
debug = True
if items.precalculated_progression_recipes and name in items.precalculated_progression_recipes:
can_produce: bool = state_logic.can_produce_specific_recipe_for_part(
state, items.precalculated_progression_recipes[name])
can_produce_anyway: bool
if can_produce:
return can_produce
else:
can_produce_anyway = \
any(state_logic.can_produce_specific_recipe_for_part(state, recipe) for recipe in recipes)
if can_produce_anyway:
debug = True
return False # can_produce_anyway
else:
return any(state_logic.can_produce_specific_recipe_for_part(state, recipe) for recipe in recipes)
return can_build_by_any_recipe
class EventBuilding(LocationData):
def __init__(self, game_logic: GameLogic, state_logic: StateLogic, building_name: str, building: Building):
super().__init__("Overworld", building_event_prefix + building_name, EventId,
rule = self.can_create_building(game_logic, state_logic, building))
def can_create_building(self, game_logic: GameLogic, state_logic: StateLogic, building: Building
) -> Callable[[CollectionState], bool]:
def can_build(state: CollectionState) -> bool:
if building.name == "Building: Foundry":
debug = True
return state_logic.has_recipe(state, building) \
and state_logic.can_power(state, building.power_requirement) \
and state_logic.can_produce_all_allowing_handcrafting(state, game_logic, building.inputs)
return can_build
class PowerInfrastructure(LocationData):
def __init__(self, game_logic: GameLogic, state_logic: StateLogic,
powerLevel: PowerInfrastructureLevel, recipes: Iterable[Recipe]):
super().__init__("Overworld", building_event_prefix + powerLevel.to_name(), EventId,
rule = self.can_create_power_infrastructure(game_logic, state_logic, powerLevel, recipes))
def can_create_power_infrastructure(self, game_logic: GameLogic, state_logic: StateLogic,
powerLevel: PowerInfrastructureLevel, recipes: Iterable[Recipe]
) -> Callable[[CollectionState], bool]:
def can_power(state: CollectionState) -> bool:
return any(state_logic.can_power(state, level) for level in PowerInfrastructureLevel if level > powerLevel)\
or any(state_logic.can_build(state, recipe.building) and
state_logic.can_produce_all_allowing_handcrafting(state, game_logic, recipe.inputs)
for recipe in recipes)
return can_power
class ElevatorTier(LocationData):
def __init__(self, tier: int, state_logic: StateLogic, game_logic: GameLogic):
super().__init__("Overworld", f"Elevator Tier {tier + 1}", EventId,
rule = lambda state: state_logic.can_build(state, "Space Elevator") and \
state_logic.can_produce_all(state, game_logic.space_elevator_tiers[tier].keys()))
class HubSlot(LocationData):
def __init__(self, tier: int, milestone: int, slot: int, locationId: int):
super().__init__(f"Hub {tier}-{milestone}", f"Hub {tier}-{milestone}, item {slot}", locationId)
class MamSlot(LocationData):
def __init__(self, tree: str, nodeName: str, locationId: int):
super().__init__(f"{tree}: {nodeName}", f"{tree}: {nodeName}", locationId)
class ShopSlot(LocationData):
def __init__(self, state_logic: Optional[StateLogic], slot: int, cost: int, locationId: int):
super().__init__("AWESOME Shop", f"AWESOME Shop purchase {slot}", locationId,
rule = self.can_purchase_from_shop(state_logic, cost))
def can_purchase_from_shop(self, state_logic: Optional[StateLogic], cost) -> Callable[[CollectionState], bool]:
def can_purchase(state: CollectionState) -> bool:
if not state_logic or cost < 20:
return True
elif (cost >= 20 and cost < 50):
return state_logic.is_game_phase(state, 1)
elif (cost >= 50 and cost < 100):
return state_logic.is_game_phase(state, 2)
else:
return state_logic.is_game_phase(state, 3)
return can_purchase
class DropPod(LocationData):
def __init__(self, data: DropPodData, state_logic: Optional[StateLogic],
locationId: int, tier: int, can_hold_progression: bool):
# drop pod locations are unlocked by hard drives, there is currently no direct mapping between location and hard drive
# we currently do not know how many hdd require gass or radioactive protection
# coordinates are for us to reference them, there is no real link between coordinate and check
def get_region(gassed: Optional[bool], radioactive: Optional[bool]) -> str:
#if radioactive:
# return "Radioactive Area"
#elif gassed:
# return "Gas Area"
#else:
# return "Overworld"
return f"Hub Tier {tier}"
def get_rule(unlocked_by: Optional[str], power_needed: int) -> Callable[[CollectionState], bool]:
# Power is kept out of logic. with energy link its simple,
# without you just going to have to figure it your yourself
def logic_rule(state: CollectionState):
return not unlocked_by or state_logic and state_logic.can_produce(state, unlocked_by)
return logic_rule
super().__init__(get_region(data.gassed, data.radioactive), f"Hard drive random check {locationId - 1338600}", locationId,
non_progression = not can_hold_progression, rule = get_rule(data.item, data.power))
class Locations():
game_logic: Optional[GameLogic]
options: Optional[SatisfactoryOptions]
state_logic: Optional[StateLogic]
items: Optional[Items]
hub_location_start: ClassVar[int] = 1338000
max_tiers: ClassVar[int] = 10
max_milestones: ClassVar[int] = 5
max_slots: ClassVar[int] = 10
drop_pod_location_id_start: ClassVar[int] = 1338600
drop_pod_location_id_end: ClassVar[int] = 1338699
def __init__(self, game_logic: Optional[GameLogic] = None, options: Optional[SatisfactoryOptions] = None,
state_logic: Optional[StateLogic] = None, items: Optional[Items] = None):
self.game_logic = game_logic
self.options = options
self.state_logic = state_logic
self.items = items
def get_base_location_table(self) -> List[LocationData]:
return [
MamSlot("Alien Organisms", "Inflated Pocket Dimension", 1338500),
MamSlot("Alien Organisms", "Hostile Organism Detection", 1338501),
MamSlot("Alien Organisms", "Expanded Toolbelt", 1338502),
MamSlot("Alien Organisms", "Bio-Organic Properties", 1338503),
MamSlot("Alien Organisms", "Stinger Research", 1338504),
MamSlot("Alien Organisms", "Hatcher Research", 1338505),
MamSlot("Alien Organisms", "Hog Research", 1338506),
MamSlot("Alien Organisms", "Spitter Research", 1338507),
MamSlot("Alien Organisms", "Structural Analysis", 1338508),
MamSlot("Alien Organisms", "Protein Inhaler", 1338509),
MamSlot("Alien Organisms", "The Rebar Gun", 1338510),
MamSlot("Caterium", "Caterium Electronics", 1338511),
MamSlot("Caterium", "Bullet Guidance System", 1338512),
MamSlot("Caterium", "High-Speed Connector", 1338513),
MamSlot("Caterium", "Caterium", 1338514),
MamSlot("Caterium", "Caterium Ingots", 1338515),
MamSlot("Caterium", "Quickwire", 1338516),
MamSlot("Caterium", "Power Switch", 1338517),
MamSlot("Caterium", "Power Poles Mk.2", 1338518),
MamSlot("Caterium", "AI Limiter", 1338519),
MamSlot("Caterium", "Smart Splitter", 1338520),
MamSlot("Caterium", "Programmable Splitter", 1338521),
MamSlot("Mycelia", "Gas Mask", 1338522), # 1.0
MamSlot("Caterium", "Zipline", 1338523),
MamSlot("Caterium", "Geothermal Generator", 1338524),
MamSlot("Caterium", "Priority Power Switch", 1338525),
MamSlot("Caterium", "Stun Rebar", 1338526),
MamSlot("Caterium", "Power Poles Mk.3", 1338527),
MamSlot("Mycelia", "Therapeutic Inhaler", 1338528),
MamSlot("Mycelia", "Expanded Toolbelt", 1338529),
MamSlot("Mycelia", "Mycelia", 1338530),
MamSlot("Mycelia", "Fabric", 1338531),
MamSlot("Mycelia", "Medical Properties", 1338532),
MamSlot("Mycelia", "Toxic Cellular Modification", 1338533),
MamSlot("Mycelia", "Vitamin Inhaler", 1338534),
MamSlot("Mycelia", "Parachute", 1338535),
MamSlot("Mycelia", "Synthethic Polyester Fabric", 1338536),
MamSlot("Nutrients", "Bacon Agaric", 1338537),
MamSlot("Nutrients", "Beryl Nut", 1338538),
MamSlot("Nutrients", "Paleberry", 1338539),
MamSlot("Nutrients", "Nutritional Processor", 1338540),
MamSlot("Nutrients", "Nutritional Inhaler", 1338541),
MamSlot("Power Slugs", "Slug Scanning", 1338542),
MamSlot("Power Slugs", "Blue Power Slugs", 1338543),
MamSlot("Power Slugs", "Yellow Power Shards", 1338544),
MamSlot("Power Slugs", "Purple Power Shards", 1338545),
MamSlot("Power Slugs", "Overclock Production", 1338546),
MamSlot("Quartz", "Crystal Oscillator", 1338547),
MamSlot("Quartz", "Quartz Crystals", 1338548),
MamSlot("Quartz", "Quartz", 1338549),
MamSlot("Quartz", "Shatter Rebar", 1338550),
MamSlot("Quartz", "Silica", 1338551),
MamSlot("Quartz", "Explosive Resonance Application", 1338552),
MamSlot("Quartz", "Blade Runners", 1338553),
MamSlot("Quartz", "The Explorer", 1338554),
MamSlot("Quartz", "Radio Signal Scanning", 1338555),
MamSlot("Quartz", "Inflated Pocket Dimension", 1338556),
MamSlot("Quartz", "Radar Technology", 1338557),
MamSlot("Sulfur", "The Nobelisk Detonator", 1338558),
MamSlot("Sulfur", "Smokeless Powder", 1338559),
MamSlot("Sulfur", "Sulfur", 1338560),
MamSlot("Sulfur", "Inflated Pocket Dimension", 1338561),
MamSlot("Sulfur", "The Rifle", 1338562),
MamSlot("Sulfur", "Compacted Coal", 1338563),
MamSlot("Sulfur", "Black Powder", 1338564),
MamSlot("Sulfur", "Explosive Rebar", 1338565),
MamSlot("Sulfur", "Cluster Nobelisk", 1338566),
MamSlot("Sulfur", "Experimental Power Generation", 1338567),
MamSlot("Sulfur", "Turbo Rifle Ammo", 1338568),
MamSlot("Sulfur", "Turbo Fuel", 1338569),
MamSlot("Sulfur", "Expanded Toolbelt", 1338570),
MamSlot("Sulfur", "Nuclear Deterrent Development", 1338571),
# 1.0
MamSlot("Power Slugs", "Synthetic Power Shards", 1338572),
MamSlot("Sulfur", "Rocket Fuel", 1338573),
MamSlot("Sulfur", "Ionized Fuel", 1338574),
MamSlot("Alien Technology", "SAM Analysis", 1338575),
MamSlot("Alien Technology", "SAM Reanimation", 1338576),
MamSlot("Alien Technology", "SAM Fluctuator", 1338577),
MamSlot("Alien Technology", "Mercer Sphere Analysis", 1338578),
MamSlot("Alien Technology", "Dimensional Depot", 1338579),
MamSlot("Alien Technology", "Manual Depot Uploader", 1338580),
MamSlot("Alien Technology", "Depot Expansion (200%)", 1338581),
MamSlot("Alien Technology", "Depot Expansion (300%)", 1338582),
MamSlot("Alien Technology", "Depot Expansion (400%)", 1338583),
MamSlot("Alien Technology", "Depot Expansion (500%)", 1338584),
MamSlot("Alien Technology", "Upload Upgrade: 30/min", 1338585),
MamSlot("Alien Technology", "Upload Upgrade: 60/min", 1338586),
MamSlot("Alien Technology", "Upload Upgrade: 120/min", 1338587),
MamSlot("Alien Technology", "Upload Upgrade: 240/min", 1338588),
MamSlot("Alien Technology", "Somersloop Analysis", 1338589),
MamSlot("Alien Technology", "Alien Energy Harvesting", 1338590),
MamSlot("Alien Technology", "Production Amplifier", 1338591),
MamSlot("Alien Technology", "Power Augmenter", 1338592),
MamSlot("Alien Technology", "Alien Power Matrix", 1338593),
# 1.0
# 1338600 - 1338699 - Harddrives - Harddrives
ShopSlot(self.state_logic, 1, 3, 1338700),
ShopSlot(self.state_logic, 2, 3, 1338701),
ShopSlot(self.state_logic, 3, 5, 1338702),
ShopSlot(self.state_logic, 4, 5, 1338703),
ShopSlot(self.state_logic, 5, 10, 1338704),
ShopSlot(self.state_logic, 6, 10, 1338705),
ShopSlot(self.state_logic, 7, 20, 1338706),
ShopSlot(self.state_logic, 8, 20, 1338707),
ShopSlot(self.state_logic, 9, 50, 1338708),
ShopSlot(self.state_logic, 10, 50, 1338709)
]
def get_locations_for_data_package(self) -> Dict[str, int]:
"Must include all posiable location names and thier id's"
location_table = self.get_base_location_table()
location_table.extend(self.get_hub_locations())
location_table.extend(self.get_drop_pod_locations())
location_table.append(LocationData("Overworld", "UpperBound", 1338999))
return {location.name: location.code for location in location_table}
def get_locations(self) -> List[LocationData]:
"Only return location used in this game based on settings"
if not self.game_logic or not self.options or not self.state_logic or not self.items:
raise Exception("Locations need to be initialized with logic, options and items before using this method")
location_table = self.get_base_location_table()
location_table.extend(self.get_hub_locations())
location_table.extend(self.get_drop_pod_locations())
location_table.extend(self.get_logical_event_locations())
return location_table
def get_hub_locations(self) -> List[LocationData]:
location_table: List[LocationData] = []
hub_location_id = self.hub_location_start
for tier in range(1, self.max_tiers + 1):
for milestone in range(1, self.max_milestones + 1):
for slot in range(1, self.max_slots + 1):
if not self.game_logic:
location_table.append(HubSlot(tier, milestone, slot, hub_location_id))
else:
if tier <= len(self.game_logic.hub_layout) \
and milestone <= len(self.game_logic.hub_layout[tier - 1]) \
and slot <= self.game_logic.slots_per_milestone:
location_table.append(HubSlot(tier, milestone, slot, hub_location_id))
hub_location_id += 1
return location_table
def get_logical_event_locations(self) -> List[LocationData]:
location_table: List[LocationData] = []
# for performance plan is to upfront calculated everything we need
# and than create one massive state.has_all for each logical gate (hub tiers, elevator tiers)
location_table.extend(
ElevatorTier(index, self.state_logic, self.game_logic)
for index, parts in enumerate(self.game_logic.space_elevator_tiers))
location_table.extend(
part
for part_name, recipes in self.game_logic.recipes.items()
for part in Part.get_parts(self.state_logic, recipes, part_name, self.items))
location_table.extend(
EventBuilding(self.game_logic, self.state_logic, name, building)
for name, building in self.game_logic.buildings.items())
location_table.extend(
PowerInfrastructure(self.game_logic, self.state_logic, power_level, recipes)
for power_level, recipes in self.game_logic.requirement_per_powerlevel.items())
return location_table
def get_drop_pod_locations(self) -> List[LocationData]:
drop_pod_locations: List[DropPod] = []
bucket_size: int = 0
drop_pod_data: List[DropPodData] = []
if self.game_logic:
bucket_size = floor(
(self.drop_pod_location_id_end - self.drop_pod_location_id_start) / len(self.game_logic.hub_layout))
drop_pod_data = self.game_logic.drop_pods
# sort, easily obtainable first, should be deterministic
drop_pod_data.sort(key = lambda data: ("!" if data.item == None else data.item) + str(data.x - data.z))
for location_id in range(self.drop_pod_location_id_start, self.drop_pod_location_id_end + 1):
if not self.game_logic or not self.state_logic or not self.options:
drop_pod_locations.append(DropPod(DropPodData(0, 0, 0, None, 0), None, location_id, 1, False))
else:
location_id_normalized: int = location_id - self.drop_pod_location_id_start
data: DropPodData = drop_pod_data[location_id_normalized]
can_hold_progression: bool = location_id_normalized < self.options.hard_drive_progression_limit.value
tier = min(ceil((location_id_normalized + 1) / bucket_size), len(self.game_logic.hub_layout))
drop_pod_locations.append(DropPod(data, self.state_logic, location_id, tier, can_hold_progression))
return drop_pod_locations

View File

@@ -0,0 +1,388 @@
from dataclasses import dataclass
from typing import Dict, List, Any, Tuple, ClassVar, cast
from enum import IntEnum
from Options import PerGameCommonOptions, DeathLink, AssembleOptions, Visibility
from Options import Range, Toggle, OptionSet, StartInventoryPool, NamedRange, Choice
class Placement(IntEnum):
starting_inventory = 0
early = 1
somewhere = 2
class PlacementLogicMeta(AssembleOptions):
def __new__(mcs, name: str, bases: Tuple[type], attrs: Dict[Any, Any]) -> "PlacementLogicMeta":
if "default" in attrs and isinstance(attrs["default"], Placement):
attrs["default"] = int(attrs["default"])
cls = super(PlacementLogicMeta, mcs).__new__(mcs, name, bases, attrs)
return cast(PlacementLogicMeta, cls)
class PlacementLogic(Choice, metaclass=PlacementLogicMeta):
option_unlocked_from_start = Placement.starting_inventory
option_early_game = Placement.early
option_somewhere = Placement.somewhere
class ChoiceMapMeta(AssembleOptions):
def __new__(mcs, name: str, bases: Tuple[type], attrs: Dict[Any, Any]) -> "ChoiceMapMeta":
if "choices" in attrs:
for index, choice in enumerate(attrs["choices"].keys()):
option_name = "option_" + choice.replace(' ', '_')
attrs[option_name] = index
cls = super(ChoiceMapMeta, mcs).__new__(mcs, name, bases, attrs)
return cast(ChoiceMapMeta, cls)
class ChoiceMap(Choice, metaclass=ChoiceMapMeta):
# TODO `default` doesn't do anything, default is always the first `choices` value. if uncommented it messes up the template file generation (caps mismatch)
choices: ClassVar[Dict[str, List[str]]]
def get_selected_list(self) -> List[str]:
for index, choice in enumerate(self.choices.keys()):
if index == self.value:
return self.choices[choice]
class ElevatorTier(NamedRange):
"""
Ship these Space Elevator packages to finish.
Does nothing if *Space Elevator Tier* goal is not enabled.
"""
display_name = "Goal: Space Elevator shipment"
default = 2
range_start = 1
range_end = 4
special_range_names = {
"one package (tiers 1-2)": 1,
"two packages (tiers 1-4)": 2,
"three packages (tiers 1-6)": 3,
"four packages (tiers 7-8)": 4,
"five packages (tier 9)": 5,
}
class ResourceSinkPoints(NamedRange):
"""
Sink an amount of items totalling this amount of points to finish.
Does nothing if *AWESOME Sink Points* goal is not enabled.
In the base game, it takes 347 coupons to unlock every non-repeatable purchase, or 1895 coupons to purchase every non-producible item.
Use the **TFIT - Ficsit Information Tool** mod or the Satisfactory wiki to find out how many points items are worth.
If you have *Free Samples* enabled, consider setting this higher so that you can't reach the goal just by sinking your Free Samples.
"""
# Coupon data for above comment from https://satisfactory.wiki.gg/wiki/AWESOME_Shop
display_name = "Goal: AWESOME Sink points"
default = 2166000
range_start = 2166000
range_end = 18436379500
special_range_names = {
"50 coupons (~2m points)": 2166000,
"100 coupons (~18m points)": 17804500,
"150 coupons (~61m points)": 60787500,
"200 coupons (~145m points)": 145053500,
"250 coupons (~284m points)": 284442000,
"300 coupons (~493m points)": 492825000,
"350 coupons (~784m points)": 784191000,
"400 coupons (~1,2b points)": 1172329500,
"450 coupons (~1,7b points)": 1671112500,
"500 coupons (~2b points)": 2294578500,
"550 coupons (~3b points)": 3056467000,
"600 coupons (~4b points)": 3970650000,
"650 coupons (~5b points)": 5051216000,
"700 coupons (~6b points)": 6311854500,
"750 coupons (~8b points)": 7766437500,
"800 coupons (~9b points)": 9429103500,
"850 coupons (~11b points)": 11313492000,
"900 coupons (~13b points)": 13433475000,
"950 coupons (~16b points)": 15803241000,
"1000 coupons (~18b points)": 18436379500
}
class HardDriveProgressionLimit(Range):
"""
How many Hard Drives can contain progression items.
Hard Drives above this count cannot contain progression, but can still be useful.
There are 118 total hard drives.
"""
display_name = "Hard Drive Progression Items"
default = 0
range_start = 0
range_end = 100
class FreeSampleEquipment(Range):
"""
How many free sample items of Equipment items should be given when they are unlocked.
(ex. Jetpack, Rifle)
"""
display_name = "Free Samples: Equipment"
default = 1
range_start = 0
range_end = 10
class FreeSampleBuildings(Range):
"""
How many copies of a Building's construction cost to give as a free sample when they are unlocked.
Space Elevator is always excluded.
(ex. Packager, Constructor, Smelter)
"""
display_name = "Free Samples: Buildings"
default = 5
range_start = 0
range_end = 10
class FreeSampleParts(NamedRange):
"""
How free sample items of general crafting components should be given when a recipe for them is unlocked.
Space Elevator Project Parts are always excluded.
Negative numbers mean that fraction of a full stack.
(ex. Iron Plate, Packaged Turbofuel, Reinforced Modular Frame)
"""
display_name = "Free Samples: Parts"
default = -2
range_start = -5
range_end = 500
special_range_names = {
"disabled": 0,
"half_stack": -2,
"one_stack": -1,
"1": 1,
"50": 50,
"100": 100,
"200": 200,
"500": 500,
}
class FreeSampleRadioactive(Toggle):
"""
Allow free samples to include radioactive parts.
Remember, they are delivered directly to your player inventory.
"""
display_name = "Free Samples: Radioactive"
class TrapChance(Range):
"""
Chance of traps in the item pool.
Traps will only replace filler items such as parts and resources.
- **0:** No traps will be present
- **100:** Every filler item will be a trap.
"""
display_name = "Trap Chance"
range_start = 0
range_end = 100
default = 10
_trap_types = {
"Trap: Doggo with Pulse Nobelisk",
"Trap: Doggo with Nuke Nobelisk",
"Trap: Doggo with Gas Nobelisk",
"Trap: Hog",
"Trap: Alpha Hog",
"Trap: Cliff Hog",
"Trap: Nuclear Hog",
"Trap: Johnny",
"Trap: Hatcher",
"Trap: Elite Hatcher",
"Trap: Small Stinger",
"Trap: Stinger",
"Trap: Gas Stinger",
"Trap: Spore Flower",
"Trap: Spitter",
"Trap: Alpha Spitter",
"Trap: Not the Bees",
"Trap: Nuclear Waste Drop",
"Trap: Plutonium Waste Drop",
"Trap: Can of Beans",
"Trap: Fart Cloud",
# Radioactive parts delivered via portal
"Bundle: Uranium",
"Bundle: Uranium Fuel Rod",
"Bundle: Uranium Waste",
"Bundle: Plutonium Fuel Rod",
"Bundle: Plutonium Pellet",
"Bundle: Plutonium Waste",
"Bundle: Non-fissile Uranium",
"Bundle: Ficsonium",
"Bundle: Ficsonium Fuel Rod"
}
class TrapSelectionPreset(ChoiceMap):
"""
Themed presets of trap types to enable.
If you want more control, use *Trap Override* or visit the Weighted Options page.
"""
display_name = "Trap Presets"
choices = {
"Normal": ["Trap: Doggo with Pulse Nobelisk", "Trap: Doggo with Gas Nobelisk", "Trap: Hog", "Trap: Alpha Hog", "Trap: Hatcher", "Trap: Elite Hatcher", "Trap: Small Stinger", "Trap: Stinger", "Trap: Spitter", "Trap: Alpha Spitter", "Trap: Not the Bees", "Trap: Nuclear Waste Drop", "Bundle: Uranium", "Bundle: Non-fissile Uranium", "Trap: Can of Beans", "Trap: Fart Cloud"],
"Gentle": ["Trap: Doggo with Pulse Nobelisk", "Trap: Hog", "Trap: Spitter", "Trap: Can of Beans"],
"Harder": ["Trap: Doggo with Pulse Nobelisk", "Trap: Doggo with Nuke Nobelisk", "Trap: Doggo with Gas Nobelisk", "Trap: Alpha Hog", "Trap: Cliff Hog", "Trap: Spore Flower", "Trap: Hatcher", "Trap: Elite Hatcher", "Trap: Stinger", "Trap: Alpha Spitter", "Trap: Not the Bees", "Trap: Fart Cloud", "Trap: Nuclear Waste Drop", "Trap: Plutonium Waste Drop", "Bundle: Uranium", "Bundle: Uranium Fuel Rod", "Bundle: Uranium Waste", "Bundle: Plutonium Fuel Rod", "Bundle: Plutonium Pellet", "Bundle: Plutonium Waste", "Bundle: Non-fissile Uranium"],
"All": list(_trap_types),
"Ruthless": ["Trap: Doggo with Nuke Nobelisk", "Trap: Nuclear Hog", "Trap: Cliff Hog", "Trap: Elite Hatcher", "Trap: Spore Flower", "Trap: Gas Stinger", "Trap: Nuclear Waste Drop", "Trap: Plutonium Waste Drop", "Bundle: Uranium Fuel Rod", "Bundle: Uranium Waste", "Bundle: Plutonium Fuel Rod", "Bundle: Plutonium Pellet", "Bundle: Plutonium Waste", "Bundle: Non-fissile Uranium", "Bundle: Ficsonium", "Bundle: Ficsonium Fuel Rod"],
"All Arachnids All the Time": ["Trap: Small Stinger", "Trap: Stinger", "Trap: Gas Stinger"],
"Whole Hog": ["Trap: Hog", "Trap: Alpha Hog", "Trap: Cliff Hog", "Trap: Nuclear Hog", "Trap: Johnny"],
"Nicholas Cage": ["Trap: Hatcher", "Trap: Elite Hatcher", "Trap: Not the Bees"],
"Fallout": ["Trap: Doggo with Nuke Nobelisk", "Trap: Nuclear Hog", "Trap: Nuclear Waste Drop", "Trap: Plutonium Waste Drop", "Bundle: Uranium", "Bundle: Uranium Fuel Rod", "Bundle: Uranium Waste", "Bundle: Plutonium Fuel Rod", "Bundle: Plutonium Waste", "Bundle: Ficsonium", "Bundle: Ficsonium Fuel Rod"],
}
default="Normal"
class TrapSelectionOverride(OptionSet):
"""
Precise list of traps that may be in the item pool to find.
If you select anything with this option it will be used instead of the *Trap Presets* setting.
"""
display_name = "Trap Override"
valid_keys = _trap_types
default = {}
class EnergyLink(Toggle):
"""
Allow transferring energy to and from other worlds using the Power Storage building.
0% of the energy is lost in the transfer.
"""
display_name = "EnergyLink"
class MamLogic(PlacementLogic):
"""
Where to place the MAM building in logic.
Earlier means it will be more likely you need to interact with it for progression purposes.
"""
display_name = "MAM Placement"
default = Placement.early
class AwesomeLogic(PlacementLogic):
"""
Where to place the AWESOME Shop and Sink buildings in logic.
Earlier means it will be more likely you need to interact with it for progression purposes.
"""
display_name = "AWESOME Stuff Placement"
default = Placement.early
class EnergyLinkLogic(PlacementLogic):
"""
Where to place the EnergyLink building (or Power Storage if EnergyLink is disabled) in logic.
Earlier means it will be more likely to get access to it early into your game.
"""
display_name = "EnergyLink Placement"
default = Placement.early
class SplitterLogic(PlacementLogic):
"""
Where to place the Conveyor Splitter and Merger buildings in logic.
Earlier means it will be more likely to get access to it early into your game.
"""
display_name = "Splitter and Merger Placement"
default = Placement.starting_inventory
_skip_tutorial_starting_items = [
# https://satisfactory.wiki.gg/wiki/Onboarding
"Bundle: Portable Miner",
"Bundle: Iron Plate",
"Bundle: Concrete",
"Bundle: Iron Rod",
"Bundle: Wire",
"Bundle: Reinforced Iron Plate",
"Bundle: Cable"
]
_default_starting_items = _skip_tutorial_starting_items + [
"Bundle: Iron Ingot",
"Bundle: Copper Ingot",
"Bundle: Concrete",
"Building: Blueprint Designer",
"Expanded Toolbelt",
"Inflated Pocket Dimension",
"Building: Personal Storage Box"
]
_default_plus_foundations_starting_items = _default_starting_items + [
"Building: Foundation",
"Building: Half Foundation"
]
_foundation_lover_starting_items = _default_plus_foundations_starting_items + [
"Bundle: Iron Plate", "Bundle: Iron Plate", "Bundle: Iron Plate",
"Bundle: Concrete", "Bundle: Concrete", "Bundle: Concrete"
]
class StartingInventoryPreset(ChoiceMap):
"""
What resources (and buildings) the player should start with in their inventory.
If you want more control, visit the Weighted Options page or edit the YAML directly.
- **Barebones**: Nothing but the default xeno zapper and buildings.
- **Skip Tutorial Inspired**: Inspired by the items you would have if you skipped the base game's tutorial.
- **Archipelago**: The starting items we think will lead to a fun experience.
- **Foundations**: 'Archipelago' option, but also guaranteeing that you have foundations unlocked at the start.
- **Foundation Lover**: You really like foundations.
"""
display_name = "Starting Goodies Presets"
choices = {
"Archipelago": _default_starting_items,
"Barebones": [], # Nothing but the xeno zapper
"Skip Tutorial Inspired": _skip_tutorial_starting_items,
"Foundations": _default_plus_foundations_starting_items,
"Foundation Lover": _foundation_lover_starting_items
}
default = "Archipelago"
class GoalSelection(OptionSet):
"""
What will be your goal(s)?
Configure them further with other options.
"""
display_name = "Select your Goals"
valid_keys = {
"Space Elevator Tier",
"AWESOME Sink Points",
# "Exploration",
# "FICSMAS Tree",
}
default = {"Space Elevator Tier"}
class GoalRequirement(Choice):
"""
Of the goals selected in *Select your Goals*, how many must be reached to complete your slot?
"""
display_name = "Goal Requirements"
option_require_any_one_goal = 0
option_require_all_goals = 1
default = 0
class ExperimentalGeneration(Toggle):
"""
Attempts to only mark recipes as progression if they are on your path to victory.
WARNING: has a very high change of generation failure and should therefore only be used in single player games.
"""
display_name = "Experimental Generation"
visibility = Visibility.none
@dataclass
class SatisfactoryOptions(PerGameCommonOptions):
goal_selection: GoalSelection
goal_requirement: GoalRequirement
final_elevator_package: ElevatorTier
final_awesome_sink_points: ResourceSinkPoints
hard_drive_progression_limit: HardDriveProgressionLimit
free_sample_equipment: FreeSampleEquipment
free_sample_buildings: FreeSampleBuildings
free_sample_parts: FreeSampleParts
free_sample_radioactive: FreeSampleRadioactive
starting_inventory_preset: StartingInventoryPreset
mam_logic_placement: MamLogic
awesome_logic_placement: AwesomeLogic
energy_link_logic_placement: EnergyLinkLogic
splitter_placement: SplitterLogic
trap_chance: TrapChance
trap_selection_preset: TrapSelectionPreset
trap_selection_override: TrapSelectionOverride
death_link: DeathLink
energy_link: EnergyLink
start_inventory_from_pool: StartInventoryPool
experimental_generation: ExperimentalGeneration

View File

@@ -0,0 +1,186 @@
from typing import List, Set, Dict, Tuple, Optional, Callable
from BaseClasses import MultiWorld, Region, Location, Item, CollectionState
from .Locations import LocationData
from .GameLogic import GameLogic, PowerInfrastructureLevel
from .StateLogic import StateLogic
from .Options import SatisfactoryOptions, Placement
class SatisfactoryLocation(Location):
game: str = "Satisfactory"
event_name: Optional[str]
def __init__(self, player: int, data: LocationData, region: Region):
super().__init__(player, data.name, data.code, region)
self.event_name = data.event_name
if data.code is None:
self.event = True
self.locked = True
if (data.rule):
self.access_rule = data.rule
if (data.non_progression):
self.item_rule = self.non_progression_only
@staticmethod
def non_progression_only(item: Item) -> bool:
return not item.advancement
def create_regions_and_return_locations(world: MultiWorld, options: SatisfactoryOptions, player: int,
game_logic: GameLogic, state_logic: StateLogic, locations: List[LocationData]):
region_names: List[str] = [
"Menu",
"Overworld",
"Gas Area",
"Radioactive Area",
"Mam",
"AWESOME Shop"
]
for hub_tier, milestones_per_hub_tier in enumerate(game_logic.hub_layout, 1):
region_names.append(f"Hub Tier {hub_tier}")
for minestone, _ in enumerate(milestones_per_hub_tier, 1):
region_names.append(f"Hub {hub_tier}-{minestone}")
for building_name, building in game_logic.buildings.items():
if building.can_produce:
region_names.append(building_name)
for tree_name, tree in game_logic.man_trees.items():
region_names.append(tree_name)
for node in tree.nodes:
region_names.append(f"{tree_name}: {node.name}")
locations_per_region: Dict[str, LocationData] = get_locations_per_region(locations)
regions: Dict[str, Region] = create_regions(world, player, locations_per_region, region_names)
if __debug__:
throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys())
world.regions += regions.values()
super_early_game_buildings: List[str] = [
"Foundation",
"Walls Orange"
]
early_game_buildings: List[str] = [
PowerInfrastructureLevel.Automated.to_name()
]
if options.mam_logic_placement.value == Placement.early:
early_game_buildings.append("MAM")
if options.awesome_logic_placement.value == Placement.early:
early_game_buildings.append("AWESOME Sink")
early_game_buildings.append("AWESOME Shop")
if options.energy_link_logic_placement.value == Placement.early:
early_game_buildings.append("Power Storage")
if options.splitter_placement == Placement.early:
super_early_game_buildings.append("Conveyor Splitter")
super_early_game_buildings.append("Conveyor Merger")
connect(regions, "Menu", "Overworld")
connect(regions, "Overworld", "Hub Tier 1")
connect(regions, "Hub Tier 1", "Hub Tier 2",
lambda state: state_logic.can_build_all(state, super_early_game_buildings))
connect(regions, "Hub Tier 2", "Hub Tier 3", lambda state: state.has("Elevator Tier 1", player)
and state_logic.can_build_all(state, early_game_buildings))
connect(regions, "Hub Tier 3", "Hub Tier 4")
connect(regions, "Hub Tier 4", "Hub Tier 5", lambda state: state.has("Elevator Tier 2", player))
connect(regions, "Hub Tier 5", "Hub Tier 6")
connect(regions, "Hub Tier 6", "Hub Tier 7", lambda state: state.has("Elevator Tier 3", player))
connect(regions, "Hub Tier 7", "Hub Tier 8")
connect(regions, "Hub Tier 8", "Hub Tier 9", lambda state: state.has("Elevator Tier 4", player))
connect(regions, "Overworld", "Gas Area", lambda state:
state_logic.can_produce_all(state, ("Gas Mask", "Gas Filter")))
connect(regions, "Overworld", "Radioactive Area", lambda state:
state_logic.can_produce_all(state, ("Hazmat Suit", "Iodine Infused Filter")))
connect(regions, "Overworld", "Mam", lambda state: state_logic.can_build(state, "MAM"))
connect(regions, "Overworld", "AWESOME Shop", lambda state:
state_logic.can_build_all(state, ("AWESOME Shop", "AWESOME Sink")))
def can_produce_all_allowing_handcrafting(parts: Tuple[str, ...]) -> Callable[[CollectionState], bool]:
def logic_rule(state: CollectionState):
return state_logic.can_produce_all_allowing_handcrafting(state, game_logic, parts)
return logic_rule
for hub_tier, milestones_per_hub_tier in enumerate(game_logic.hub_layout, 1):
for minestone, parts_per_milestone in enumerate(milestones_per_hub_tier, 1):
connect(regions, f"Hub Tier {hub_tier}", f"Hub {hub_tier}-{minestone}",
can_produce_all_allowing_handcrafting(parts_per_milestone.keys()))
for building_name, building in game_logic.buildings.items():
if building.can_produce:
connect(regions, "Overworld", building_name,
lambda state, building_name=building_name: state_logic.can_build(state, building_name))
for tree_name, tree in game_logic.man_trees.items():
connect(regions, "Mam", tree_name)
for node in tree.nodes:
if not node.depends_on:
connect(regions, tree_name, f"{tree_name}: {node.name}",
lambda state, parts=node.unlock_cost.keys(): state_logic.can_produce_all(state, parts))
else:
for parent in node.depends_on:
connect(regions, f"{tree_name}: {parent}", f"{tree_name}: {node.name}",
lambda state, parts=node.unlock_cost.keys(): state_logic.can_produce_all(state, parts))
def throwIfAnyLocationIsNotAssignedToARegion(regions: Dict[str, Region], regionNames: Set[str]):
existingRegions = set()
for region in regions.keys():
existingRegions.add(region)
if (regionNames - existingRegions):
raise Exception(f"Satisfactory: the following regions are used in locations: {regionNames - existingRegions}, but no such region exists")
def create_region(world: MultiWorld, player: int,
locations_per_region: Dict[str, List[LocationData]], name: str) -> Region:
region = Region(name, player, world)
if name in locations_per_region:
for location_data in locations_per_region[name]:
location = SatisfactoryLocation(player, location_data, region)
region.locations.append(location)
return region
def create_regions(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]],
region_names: List[str]) -> Dict[str, Region]:
regions: Dict[str, Region] = {}
for name in region_names:
regions[name] = create_region(world, player, locations_per_region, name)
return regions
def connect(regions: Dict[str, Region], source: str, target: str,
rule: Optional[Callable[[CollectionState], bool]] = None):
sourceRegion = regions[source]
targetRegion = regions[target]
sourceRegion.connect(targetRegion, rule=rule)
def get_locations_per_region(locations: List[LocationData]) -> Dict[str, List[LocationData]]:
per_region: Dict[str, List[LocationData]] = {}
for location in locations:
per_region.setdefault(location.region, []).append(location)
return per_region

View File

@@ -0,0 +1,95 @@
from typing import Tuple, List, Optional, Set, Iterable
from BaseClasses import CollectionState
from .GameLogic import GameLogic, Recipe, PowerInfrastructureLevel
from .Options import SatisfactoryOptions
EventId: Optional[int] = None
part_event_prefix = "Can Produce: "
building_event_prefix = "Can Build: "
class StateLogic:
player: int
options: SatisfactoryOptions
initial_unlocked_items: Set[str]
def __init__(self, player: int, options: SatisfactoryOptions):
self.player = player
self.options = options
def has_recipe(self, state: CollectionState, recipe: Recipe):
return recipe.implicitly_unlocked or state.has(recipe.name, self.player)
def can_build(self, state: CollectionState, building_name: Optional[str]) -> bool:
return building_name is None or state.has(building_event_prefix + building_name, self.player)
def can_build_any(self, state: CollectionState, building_names: Optional[Iterable[str]]) -> bool:
return building_names is None or \
state.has_any(map(self.to_building_event, building_names), self.player)
def can_build_all(self, state: CollectionState, building_names: Optional[Iterable[str]]) -> bool:
return building_names is None or \
state.has_all(map(self.to_building_event, building_names), self.player)
def can_produce(self, state: CollectionState, part_name: Optional[str]) -> bool:
return part_name is None or state.has(part_event_prefix + part_name, self.player)
def can_power(self, state: CollectionState, power_level: Optional[PowerInfrastructureLevel]) -> bool:
return power_level is None or state.has(building_event_prefix + power_level.to_name(), self.player)
def can_produce_all(self, state: CollectionState, parts: Optional[Iterable[str]]) -> bool:
if parts and "SAM" in parts:
debug = "Now"
return parts is None or \
state.has_all(map(self.to_part_event, parts), self.player)
def can_produce_all_allowing_handcrafting(self, state: CollectionState, logic: GameLogic,
parts: Optional[Tuple[str, ...]]) -> bool:
def can_handcraft_part(part: str) -> bool:
if self.can_produce(state, part):
return True
elif part not in logic.handcraftable_recipes:
return False
recipes: List[Recipe] = logic.handcraftable_recipes[part]
return any(
self.has_recipe(state, recipe)
and (not recipe.inputs or self.can_produce_all_allowing_handcrafting(state, logic, recipe.inputs))
for recipe in recipes)
return not parts or all(can_handcraft_part(part) for part in parts)
def can_produce_specific_recipe_for_part(self, state: CollectionState, recipe: Recipe) -> bool:
if recipe.needs_pipes and (
not self.can_build_any(state, ("Pipes Mk.1", "Pipes Mk.2")) or
not self.can_build_any(state, ("Pipeline Pump Mk.1", "Pipeline Pump Mk.2"))):
return False
if recipe.is_radio_active and not self.can_produce_all(state, ("Hazmat Suit", "Iodine Infused Filter")):
return False
if not self.options.experimental_generation and recipe.minimal_belt_speed and \
not self.can_build_any(state, map(self.to_belt_name, range(recipe.minimal_belt_speed, 6))):
return False
return self.has_recipe(state, recipe) \
and self.can_build(state, recipe.building) \
and self.can_produce_all(state, recipe.inputs)
def is_game_phase(self, state: CollectionState, phase: int) -> bool:
return state.has(f"Elevator Tier {phase}", self.player)
@staticmethod
def to_part_event(part: str) -> str:
return part_event_prefix + part
@staticmethod
def to_building_event(part: str) -> str:
return building_event_prefix + part
@staticmethod
def to_belt_name(power_level: int) -> str:
return "Conveyor Mk." + str(power_level)

View File

@@ -0,0 +1,16 @@
from BaseClasses import Tutorial
from ..AutoWorld import WebWorld
class SatisfactoryWebWorld(WebWorld):
theme = "dirt"
setup = Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Satisfactory Archipelago mod and connect it to an Archipelago Multiworld",
"English",
"setup_en.md",
"setup/en",
["Robb", "Jarno"]
)
tutorials = [setup]
rich_text_options_doc = True

View File

@@ -0,0 +1,172 @@
from typing import Dict, List, Set, TextIO, ClassVar, Tuple
from BaseClasses import Item, MultiWorld, ItemClassification, CollectionState
from .GameLogic import GameLogic
from .Items import Items
from .Locations import Locations, LocationData
from .StateLogic import EventId, StateLogic
from .Options import SatisfactoryOptions, Placement
from .Regions import SatisfactoryLocation, create_regions_and_return_locations
from .Web import SatisfactoryWebWorld
from ..AutoWorld import World
class SatisfactoryWorld(World):
"""
Satisfactory is a first-person open-world factory building game with a dash of exploration and combat.
Explore an alien planet, create multi-story factories, and enter conveyor belt heaven!
"""
game = "Satisfactory"
options_dataclass = SatisfactoryOptions
options: SatisfactoryOptions
topology_present = False
data_version = 0
web = SatisfactoryWebWorld()
item_name_to_id = Items.item_names_and_ids
location_name_to_id = Locations().get_locations_for_data_package()
item_name_groups = Items.get_item_names_per_category()
game_logic: ClassVar[GameLogic] = GameLogic()
state_logic: StateLogic
items: Items
def __init__(self, multiworld: "MultiWorld", player: int):
super().__init__(multiworld, player)
self.items = None
def generate_early(self) -> None:
self.state_logic = StateLogic(self.player, self.options)
self.items = Items(self.player, self.game_logic, self.random, self.options)
if not self.options.goal_selection.value:
raise Exception("""Satisfactory: player {} needs to choose a goal, the option goal_selection is empty"""
.format(self.multiworld.player_name[self.player]))
if self.options.mam_logic_placement.value == Placement.starting_inventory:
self.push_precollected("Building: MAM")
if self.options.awesome_logic_placement.value == Placement.starting_inventory:
self.push_precollected("Building: AWESOME Sink")
self.push_precollected("Building: AWESOME Shop")
if self.options.energy_link_logic_placement.value == Placement.starting_inventory:
self.push_precollected("Building: Power Storage")
if self.options.splitter_placement == Placement.starting_inventory:
self.push_precollected("Building: Conveyor Splitter")
self.push_precollected("Building: Conveyor Merger")
if not self.options.trap_selection_override.value:
self.options.trap_selection_override.value = self.options.trap_selection_preset.get_selected_list()
starting_inventory: List[str] = self.options.starting_inventory_preset.get_selected_list()
for item_name in starting_inventory:
self.push_precollected(item_name)
def create_regions(self) -> None:
locations: List[LocationData] = \
Locations(self.game_logic, self.options, self.state_logic, self.items).get_locations()
create_regions_and_return_locations(
self.multiworld, self.options, self.player, self.game_logic, self.state_logic, locations)
def create_items(self) -> None:
self.setup_events()
number_of_locations: int = len(self.multiworld.get_unfilled_locations(self.player))
self.multiworld.itempool += \
self.items.build_item_pool(self.random, self.multiworld, self.options, number_of_locations)
def set_rules(self) -> None:
resource_sink_goal: bool = "AWESOME Sink Points" in self.options.goal_selection
last_elevator_tier: int = \
len(self.game_logic.space_elevator_tiers) if resource_sink_goal \
else self.options.final_elevator_package.value
required_parts: Set[str] = set(self.game_logic.space_elevator_tiers[last_elevator_tier - 1].keys())
if resource_sink_goal:
required_parts.union(self.game_logic.buildings["AWESOME Sink"].inputs)
required_parts_tuple: Tuple[str, ...] = tuple(required_parts)
self.multiworld.completion_condition[self.player] = \
lambda state: self.state_logic.can_produce_all(state, required_parts_tuple)
def collect(self, state: CollectionState, item: Item) -> bool:
change = super().collect(state, item)
if change and item.name == "Recipe: Quartz Purification":
state.prog_items[self.player]["Recipe: Distilled Silica"] = 1
return change
def remove(self, state: CollectionState, item: Item) -> bool:
change = super().remove(state, item)
if change and item.name == "Recipe: Quartz Purification":
del state.prog_items[self.player]["Recipe: Distilled Silica"]
return change
def fill_slot_data(self) -> Dict[str, object]:
slot_hub_layout: List[List[Dict[str, int]]] = []
for tier, milestones in enumerate(self.game_logic.hub_layout, 1):
slot_hub_layout.append([])
for milestone, parts in enumerate(milestones, 1):
slot_hub_layout[tier - 1].append({})
for part, amount in parts.items():
# ItemIDs of bundles are shared with their component item
bundled_name = f"Bundle: {part}"
slot_hub_layout[tier - 1][milestone - 1][self.item_name_to_id[bundled_name]] = amount
return {
"Data": {
"HubLayout": slot_hub_layout,
"SlotsPerMilestone": self.game_logic.slots_per_milestone,
"Options": {
"GoalSelection": self.options.goal_selection.value,
"GoalRequirement": self.options.goal_requirement.value,
"FinalElevatorTier": self.options.final_elevator_package.value,
"FinalResourceSinkPoints": self.options.final_awesome_sink_points.value,
"EnableHardDriveGacha": True if self.options.hard_drive_progression_limit else False,
"FreeSampleEquipment": self.options.free_sample_equipment.value,
"FreeSampleBuildings": self.options.free_sample_buildings.value,
"FreeSampleParts": self.options.free_sample_parts.value,
"FreeSampleRadioactive": bool(self.options.free_sample_radioactive),
"EnergyLink": bool(self.options.energy_link)
}
},
"DeathLink": bool(self.options.death_link)
}
def write_spoiler(self, spoiler_handle: TextIO):
self.items.write_progression_chain(self.multiworld, spoiler_handle)
def get_filler_item_name(self) -> str:
return self.items.get_filler_item_name(self.random, self.options)
def setup_events(self):
location: SatisfactoryLocation
for location in self.multiworld.get_locations(self.player):
if location.address == EventId:
item_name = location.event_name
item = Item(item_name, ItemClassification.progression, EventId, self.player)
location.place_locked_item(item)
location.show_in_spoiler = False
def create_item(self, name: str) -> Item:
return Items.create_item(self.items, name, self.player)
def push_precollected(self, item_name: str) -> None:
item = self.create_item(item_name)
self.multiworld.push_precollected(item)

View File

@@ -0,0 +1,174 @@
# Satisfactory
<!-- Spellchecker config - cspell:ignore FICSIT Nobelisk Zoop -->
## Where is the settings page?
The [player settings page for this game](../player-settings)
contains all the options you need to configure and export a config file.
> ⚠ Pre-Release Note: The above link does not work because it would go to the live Archipelago site.
> Manually construct a yaml yourself from the one pinned in the Discord:
> <https://discord.com/channels/731205301247803413/1018853131859267656>
## What does randomization do to this game?
In Satisfactory, the HUB Milestones and MAM Research Nodes are shuffled,
causing technologies to be obtained in a non-standard order.
The costs of unlocking these technologies are also shuffled.
There are also a few new purchases in the AWESOME Shop.
An alternate recipe you've never used before may end up required to progress, for example.
<!-- TODO
Hard Drive scanning results are also optionally shuffled,
meaning that scanning a Hard Drive will result in a selection between 3 random items.
-->
## What is the goal of Satisfactory?
The player can choose from a number of goals using their YAML settings:
- Complete a certain [Space Elevator](https://satisfactory.wiki.gg/wiki/Space_Elevator) tier
<!-- TODO with optionally randomized required items -->
- Supply items to the [AWESOME Sink](https://satisfactory.wiki.gg/wiki/AWESOME_Sink) totalling a configurable amount of points to finish.
In the current implementation, selecting multiple goals
requires completion of any one goal to complete the slot.
## What Satisfactory items can appear in other players' worlds?
Satisfactory's technologies are removed from the HUB and MAM and placed into other players' worlds.
When those technologies are found, they are sent back to Satisfactory
along with, optionally, free samples of those technologies.
Other players' worlds may have Resource Bundles of building materials, equipment, ammunition, or FICSIT Coupons.
They may also contain Traps.
## What is a Free Sample?
A free sample is a package of items in Satisfactory granted in addition to a technology received from another world.
For equipment and component crafting recipes, this is the output product.
For buildings, this is the ingredients for the building.
For example, receiving the [Nobelisk Detonator MAM Node](https://satisfactory.wiki.gg/wiki/Nobelisk_Detonator#Unlocking)
would give you one Nobelisk Detonator and 50 Nobelisk,
receiving the [Jump Pads Milestone](https://satisfactory.wiki.gg/wiki/Milestones#Tier_2)
would give you the ingredients to construct 5 Jump Pads and 5 U-Jelly Landing Pads, etc.
In Satisfactory multiplayer, each Satisfactory player gets a copy of the sample.
You can separately configure how many samples to receive for buildings, equipment, and crafting components
in your player settings.
## What is a Resource Bundle?
A resource bundle is a package of items received as a check from another world.
They must be collected by constructing an Archipelago Portal.
For example, `Bundle: Jetpack` would contain a single jetpack.
## What does another world's item look like in Satisfactory?
In Satisfactory, items which need to be sent to other worlds appear in the HUB and MAM as info cards
in a similar manner to the base game's building and recipe unlocks.
Info cards have the Archipelago icon
and are color coded to indicate what Archipelago progression type they are.
Hover over them to read a description, since many Satisfactory UIs (such as the MAM) cut this information off.
![screenshot of HUB with some remote and some local items](https://raw.githubusercontent.com/Jarno458/SatisfactoryArchipelagoMod/main/Docs/localAndRemoteItems.JPG)
Upon successful unlock of the technology, the item will be sent to its home world.
## When the pioneer receives an item, what happens?
When the player receives a technology, it is instantly unlocked and able to be crafted or constructed.
A message will appear in the chat to notify the player,
and if free samples are enabled the player may also receive some items delivered directly to their inventory.
Bundles will instantly be added to the Archipelago Portal network and can be collected at any Archipelago Portal.
## What is EnergyLink?
EnergyLink is an energy storage supported by certain games that is shared across all worlds in a multiworld.
In Satisfactory, if enabled in the player settings, all base-game Power Storage buildings will act as Energy Link interfaces.
They will deposit surplus produced energy and draw energy from the shared storage when needed.
Just like the base game, there is no limit to the discharge/draw rate of one building,
and each Power Storage provides TODO MW of charging throughput.
The shared storage has unlimited capacity, but TODO% of energy is lost during depositing.
The amount of energy currently in the shared storage is displayed in the Archipelago client
and appears in the Power Storage building UI.
## What is the Archipelago Portal?
The Archipelago Portal is a building that serves multiple purposes:
- Collecting received "Resource Bundle"-type items.
- Transfering items within your Satisfactory world
- Transfering items between multiple Satisfactory worlds
- Gifting items to other games that support the Archipelago Gifting system.
The building requires power to operate.
You can build multiple portals or use faster belts to increase their bandwith.
However, they currently have no filtering capabilities,
so you must deal with this problem when handling their output items.
## What is a Trap?
You can optionally enable that some Traps be mixed into the item pool.
Traps are items that will instantly trigger some sort of surprise on the player when received.
Their severity varies from annoyance to killing the player.
A few traps are included in the default options.
## Where do I run Archipelago commands?
You can use the game's build-in chat menu.
Check the game's keybinding options to see how to open it.
Run the `/help` command to list all available commands.
Note that Archipelago commands are _not_ prefixed with `!` inside of Satisfactory.
Note that multiple base-game bugs affect the chat menu's functionality
and Archipelago can put a lot of info into the chat.
You may wish to launch the Archipelago Text Client and use it to run commands instead of the game's chat.
## Multiplayer and Dedicated Servers
It is possible to host a Satisfactory Archipelago Slot using the game's built in host-and-play multiplayer, allowing other Satisfactory players to join in constructing your factory.
This experience is wonderfull - but there are few things not yet properly working for multiplayer
* Death-links do not kill clients
* Starting inventory for clients is missing
Remember that client players must have the same mods installed as the host player to join,
however, they do not need to configure Archipelago connection settings.
Dedicated server support is only working for windows at the moment.
## Additional Mods
It is possible to load other Satisfactory mods in tandem with the Archipelago Satisfactory mod.
However, no guarantee is made that any mods except the "Certified Compatible Mods" listed below will work correctly,
especially if they affect game progression, recipes, or add unlocks to base-game technologies.
Content added by unaffiliated mods may end up inaccessible based on your chosen slot settings,
for example, its milestones could be in a tier that is after your goal.
You may be able to write patches using [ContentLib](https://ficsit.app/mod/ContentLib)
to adjust other mods to work with your slot settings,
but doing so is out of the scope of this guide.
Use unaffiliated mods at your own risk, support will not be offered.
The following mods are **required dependencies** of the Archipelago mod and **will automatically be installed for you**
when you install it using the Satisfactory Mod Manager:
- [ContentLib](https://ficsit.app/mod/ContentLib) - Runtime content generation.
- [Free Samples](https://ficsit.app/mod/FreeSamples) - Used to implement the Free Samples options. Even if you don't have this game option enabled, the mod will still be present, but its functionality will be disabled.
- [MAM Enhancer](https://ficsit.app/mod/MAMTips) - Allows viewing MAM research nodes in detail. Enables you to hover over the items/unlocks of a node to see more info, especially important when their names get long.
- [FixClientResourceSinkPoints](https://ficsit.app/mod/FixClientResourceSinkPoints) - Fixes a bug where AWESOME Sink points values aren't loaded properly on multiplayer clients.
### Certified Compatible Mods
The following mods are known to work with Archipelago:
<!-- Nog's Chat currently broken -->
<!-- - [Nog's Chat](https://ficsit.app/mod/NogsChat) - Easily repeat past chat messages, improving the user experience of running Archipelago commands in the game's chat window. -->
- [The FICSIT Information Tool](https://ficsit.app/mod/TFIT) - View how many Sink Points items are worth and how points-profitable recipes are. Helpful for the AWESOME Points goal.
- [Faster Manual Crafting Redux](https://ficsit.app/mod/FasterManualCraftingRedux) - Reduce the early game manual crafting grind with a manual crafting speed that ramps up as you craft larger batches at once.
<!-- TODO Test these -->
<!-- - [Infinite Zoop](https://ficsit.app/mod/InfiniteZoop) - Adds a research tree in the MAM where you can improve your Zoop capacity. Also enables multi-row & column Wall and Foundation construction. -->
<!-- - [Nog's Research](https://ficsit.app/mod/NogsResearch/) - Queue Milestones and MAM Nodes for automatic research in the style of Factorio's research queue. Queue type might need to be changed to soft class reference to save CL schematics. -->

View File

@@ -0,0 +1,196 @@
# Satisfactory Setup Guide
<!-- Spellchecker config - cspell:ignore FICSIT Randomizer Plando -->
## Required Software
- Satisfactory, either
- Steam [Satisfactory (Steam)](https://store.steampowered.com/app/526870/Satisfactory/)
- Epic [Satisfactory (Epic)](https://www.epicgames.com/store/en-US/product/satisfactory/home)
- Satisfactory Mod Manager, either
- Automatically via [smm.ficsit.app](https://smm.ficsit.app/) or
- Manually via [latest stable release on GitHub](https://github.com/satisfactorymodding/SatisfactoryModManager/releases/latest/)
## Overview
This guide will walk you through installing the Satisfactory Archipelago mod via the Mod Manager
and entering Archipelago server connection details in the mod configuration options.
The server will send the required data to the game client and create the content required by the seed at runtime.
## Create a Config (.yaml) File
### What is a config file and why do I need one?
Your config file contains a set of configuration options
which provide the generator with information about how it should generate your game.
Each player of a multiworld will provide their own config file.
This setup allows each player to enjoy an experience customized for their taste,
and different players in the same multiworld can all have different options.
### Where do I get a config file?
The Player Settings page on the website
allows you to configure your personal settings and export a config file from them.
Satisfactory player settings page: [Satisfactory Settings Page](/games/Satisfactory/player-settings)
> ⚠ Pre-Release Note: The above link does not work because it would go to the live Archipelago site.
> Manually construct a yaml yourself from the one pinned in the Discord:
> <https://discord.com/channels/731205301247803413/1018853131859267656>
### Verifying Your Config File
If you would like to validate your config file to make sure it works,
you may do so on the YAML Validator page.
YAML Validator page: [Yaml Validation Page](/mysterycheck)
> ⚠ Pre-Release Note: The above link does not work because it would go to the live Archipelago site.
> Manually construct a yaml yourself from the one pinned in the Discord:
> <https://discord.com/channels/731205301247803413/1018853131859267656>
### Starting Inventory
TODO talk about how you can use the Plando? Weighted Options? Manual yaml editing? page to edit your starting inventory,
giving yourself specific technologies (ex. splitters/mergers) and item bundles (ex. start with extra ) out of the gates.
### Advanced Configuration
Advanced users can utilize the
[Weighted Options Page](/weighted-options)
and [Plando](/tutorial/Archipelago/plando)
to futher customize their experience.
> ⚠ Pre-Release Note: The above links do not work because it would go to the live Archipelago site.
> See these links instead:
>
> - <https://archipelago.gg/tutorial/Archipelago/advanced_settings/en>
> - <https://archipelago.gg/tutorial/Archipelago/plando/en>
## Prepare to Host Your Own Satisfactory Game
### Defining Some Terms
In Archipelago, multiple Satisfactory worlds may be played simultaneously.
Each of these worlds must be hosted by a Satisfactory game client, each of which is connected to the Archipelago Server.
<!-- In Archipelago, multiple Satisfactory worlds may be played simultaneously.
Each of these worlds must be hosted by a Satisfactory Server which is connected to the Archipelago Server via the Archipelago mod. -->
This guide uses the following terms to refer to the software:
- **Archipelago Server** - The central Archipelago server, which connects all games to each other.
<!-- - **Satisfactory Server** - The Satisfactory instance which will be used to host, and potentially also play, the game. It must be supplied with the Archipelago Server connection details. Any number of Satisfactory Clients may connect to this server. -->
<!-- - **Satisfactory Client** - The Satisfactory instance with which additional players can use to connect to the same Satisfactory world. They must also have the Archipelago mod installed, but require no configuration. -->
- **Satisfactory Client** - The Satisfactory instance which will be used to host, and play, the game.
It is important to note that the Satisfactory Archipelago mod
is not yet compatible with dedicated servers or in-game multiplayer.
Each Satisfactory world must be hosted and played by an individual player.
### Installing Satisfactory
Purchase and install Satisfactory via one the sources linked [above](#required-software).
Launch the game at least once to ensure that the Mod Manager can detect the game's install location.
Make sure that you are running the correct branch of the game (Early Access or Experimental) that Archipelago supports.
Learn how to switch branches here:
[Satisfactory Modding Documentation FAQ: Switching Branches](https://docs.ficsit.app/satisfactory-modding/latest/faq.html#_how_do_i_get_the_experimental_or_early_access_branch_of_the_game)
### Installing Satisfactory Mod Manager
The Mod Manager is used to install and manage mods for Satisfactory.
It automatically detects your game install location and automatically handles mod dependencies for you.
Download the Mod Manager here:
[Satisfactory Mod Manager automatic download via ficsit.app](https://smm.ficsit.app/)
Directions for setting and using up the Mod Manager can be found here:
[Satisfactory Modding Documentation FAQ: Installing the Mod Manager](https://docs.ficsit.app/satisfactory-modding/latest/ForUsers/SatisfactoryModManager.html)
### Installing the Archipelago Mod
Once the Mod Manager is installed you can install mods directly in the manager or via the Satisfactory Mod Repository website.
Inside the Mod Manager, search for and install the "Archipelago Randomizer".
Alternatively, visit the mod page: [Archipelago Randomizer mod on ficsit.app](https://ficsit.app/mod/Archipelago).
Once on the mod page, click the "Install" link in the Latest Versions card.
The Mod Manager will install all required dependency mods for you with no additional action required.
As soon as you have the relevant mods installed, you do not need to launch the game through the Mod Manager - desktop shortcuts, Steam, Epic. etc. will all launch the game with mods still loaded.
### Installing Additional Mods
You may also wish to install some of the suggested mods mentioned on the
[Archipelago Info page for Satisfactory](/games/Satisfactory/info/en#additional-mods).
> ⚠ Pre-Release Note: The above link does not work because it would go to the live Archipelago site.
> Use this link instead:
> <https://github.com/Jarno458/Archipelago/blob/Satisfactory/worlds/satisfactory/docs/en_Satisfactory.md#additional-mods>
### Entering Connection Details
After you have installed the mods, launch the game via the Mod Manager or via your preferred method.
Once the game has launched, click on the 'Mods' button on the main menu and open the Archipelago entry.
Next, enter the connection details in the relevant fields.
You can hover over the fields in the menu for more information and example values.
- **URI**: Archipelago Server URI and port, for example, `archipelago.gg:49236`
- **Username**: The name you entered as your Player Name when you created your config file. It's also listed in the Name column of your room page.
- **Password**: The password for your slot, blank if you did not assign one.
- **Archipelago Enabled**: Make sure this is checked, otherwise no server connection will be attempted.
- **Debug Mode**: Don't enable it unless the developers ask you to when reporting problems.
- **Force override settings in save**: Leave false for now. It is useful when the server changed ports. Read its tooltip for more info.
Note that the Satisfactory Client does _not_ need a copy of your Archipelago config file.
The mod communicates with the Archipelago Server, which already has your config file,
to generate the required content at runtime.
### Creating a New World
Once you have entered connection details, create a new world using the game's New Game menu.
Make sure to check 'Skip Intro' if you don't want to deal with the game's tutorial sequence.
Consider enabling Advanced Game Settings to allow dealing with bugs that may arise.
Within the Advanced Game Settings menus,
you may wish to switch the "Keep Inventory" setting to "Keep Everything" to avoid dropping items on death,
although this will never lock you out of progression.
### Verifying Connection Success
Once connected to the AP server,
you can issue the `/help` command in the game's chat to list available commands, such as `/hint`.
For more information about the commands you can use, see the [Commands Guide](/tutorial/Archipelago/commands/en).
Note that Archipelago commands are not prefixed with `!` inside of Satisfactory.
You may wish to use the Text Client to run commands since Satisfactory's in game chat is not very user friendly.
> ⚠ Pre-Release Note: The above link does not work because it would go to the live Archipelago site.
> Use this link instead:
> <https://archipelago.gg/tutorial/Archipelago/commands/en>
<!-- ## Other Settings
TODO implement filter_item_sends and bridge_chat_out mentioned in the Factorio guide? -->
## Troubleshooting
TODO what is the scope of this section? How much do we help with vs. sending people somewhere else
- If you are having trouble connecting to the Archipelago server,
make sure you have entered the correct server address and port.
The server port may have changed if the room went to sleep.
If you need to enter a new port,
use the "Force override settings in save" option on the mod options menu before loading into a save.
- If you are having trouble using the Satisfactory Mod Manager, join the [Satisfactory Modding Discord](https://discord.ficsit.app) for support.
- If you encounter a game crash, please report it to us via the [Satisfactory Modding Discord](https://discord.ficsit.app).
Please include the following information:
- What you were doing when the crash occurred.
<!-- - If you were a Satisfactory multiplayer host or client, and if you were playing on a dedicated server. -->
- Use the Mod Manager to generate a debug zip and attach that file.
[Satisfactory Modding Documentation FAQ: Generating a debug zip](https://docs.ficsit.app/satisfactory-modding/latest/faq.html#_where_can_i_find_the_games_log_files)
- Attach your Archipelago config file to your report.
## Additional Resources
- Satisfactory Wiki: [Satisfactory Official Wiki](https://satisfactory.wiki.gg/wiki/)
- Satisfactory Modding FAQ page: [Satisfactory Modding Documentation FAQ](https://docs.ficsit.app/satisfactory-modding/latest/faq.html)
- Satisfactory Archipelago Item names (for hints/starting inventory/etc.) can be found [on the mod's github](https://github.com/Jarno458/Archipelago/blob/Satisfactory/worlds/satisfactory/Items.py)