Progress commmit incase my pc crashes

This commit is contained in:
Jarno Westhof
2025-03-11 23:14:40 +01:00
parent 4b65e45ecc
commit 26f9574894
6 changed files with 180 additions and 91 deletions

View File

@@ -1,5 +1,6 @@
from random import Random
from typing import Set, Tuple
from typing import Optional
from collections.abc import Iterable
from .GameLogic import GameLogic, Recipe
from .Options import SatisfactoryOptions
from .Options import SatisfactoryOptions
@@ -8,17 +9,18 @@ class CriticalPathCalculator:
logic: GameLogic
random: Random
potential_required_parts: Set[str]
potential_required_buildings: Set[str]
potential_required_parts: set[str]
potential_required_buildings: set[str]
potential_required_belt_speed: int
potential_required_pipes: bool
potential_required_radioactive: bool
potential_required_power: int
potential_required_recipes_names: Set[str]
potential_required_recipes_names: set[str]
def __init__(self, logic: GameLogic, random: Random, options: SatisfactoryOptions):
self.logic = logic
self.random = random
self.options = options
self.potential_required_parts = set()
self.potential_required_buildings = set()
@@ -27,27 +29,59 @@ class CriticalPathCalculator:
self.potential_required_radioactive = False
self.potential_required_power: int = 1
self.select_minimal_required_parts_for(
tuple(self.logic.space_elevator_tiers[options.final_elevator_package - 1].keys())
)
for i in range(self.potential_required_belt_speed, 1):
self.select_minimal_required_parts_for(self.logic.buildings[f"Conveyor Mk.{i}"].inputs)
selected_power_infrastructure: dict[int, Recipe] = {}
self.select_minimal_required_parts_for(self.logic.space_elevator_tiers[options.final_elevator_package-1].keys())
for tree in self.logic.man_trees.values():
self.select_minimal_required_parts_for(tree.access_items)
for node in tree.nodes:
# Bullet Guidance System - Rifle Ammo
# Stun Rebar - Iron Rebar
# Radar Technology - Heavy Modular Frame
# Turbo Rifle Ammo - Packaged Turbofuel, Rifle Ammo
# Nuclear Deterrent Development - Encased Uranium Cell
# Rocket Fuel - Packaged Turbofuel
# Ionized Fuel - Ionized Fuel
if node.name == "Hostile Organism Detection":
Debug = True
if node.minimal_tier > options.final_elevator_package:
continue
self.select_minimal_required_parts_for(node.unlock_cost.keys())
self.select_minimal_required_parts_for_building("MAM")
self.select_minimal_required_parts_for_building("AWESOME Sink")
self.select_minimal_required_parts_for_building("AWESOME Shop")
self.select_minimal_required_parts_for_building("Space Elevator")
self.select_minimal_required_parts_for_building("Conveyor Splitter")
self.select_minimal_required_parts_for_building("Conveyor Merger")
self.select_minimal_required_parts_for_building("Equipment Workshop")
self.select_minimal_required_parts_for_building("Foundation")
self.select_minimal_required_parts_for_building("Walls Orange")
self.select_minimal_required_parts_for_building("Power Storage")
for i in range(1, self.potential_required_belt_speed + 1):
self.select_minimal_required_parts_for_building(f"Conveyor Mk.{i}")
if self.potential_required_pipes:
self.select_minimal_required_parts_for(self.logic.buildings["Pipeline Pump Mk.1"].inputs)
self.select_minimal_required_parts_for(self.logic.buildings["Pipeline Pump Mk.2"].inputs)
self.select_minimal_required_parts_for_building("Pipes Mk.1")
self.select_minimal_required_parts_for_building("Pipeline Pump Mk.1")
if self.potential_required_radioactive:
self.select_minimal_required_parts_for(self.logic.recipes["Hazmat Suit"][0].inputs)
self.select_minimal_required_parts_for(self.logic.recipes["Iodine Infused Filter"][0].inputs)
for i in range(self.potential_required_belt_speed, 1):
for i in range(1, self.potential_required_power + 1):
power_recipe = random.choice(self.logic.requirement_per_powerlevel[i])
selected_power_infrastructure[i] = power_recipe
self.select_minimal_required_parts_for(power_recipe.inputs)
self.potential_required_buildings.add(power_recipe.building)
self.select_minimal_required_parts_for_building(power_recipe.building)
self.potential_required_recipes_names = set(
recipe.name
for part in self.potential_required_parts
for recipe in self.logic.recipes[part]
if recipe.minimal_tier <= self.options.final_elevator_package
)
self.potential_required_recipes_names.update(
"Building: "+ building
@@ -56,24 +90,53 @@ class CriticalPathCalculator:
debug = True
def select_minimal_required_parts_for_building(self, building: str) -> None:
self.select_minimal_required_parts_for(self.logic.buildings[building].inputs)
self.potential_required_buildings.add(building)
def select_minimal_required_parts_for(self, parts: Tuple[str]) -> None:
if parts:
for part in parts:
if part in self.potential_required_parts:
def select_minimal_required_parts_for(self, parts: Optional[Iterable[str]]) -> None:
if parts is None:
return
for part in parts:
if part in self.potential_required_parts:
continue
if part == "Radio Control Unit":
Debug = True
self.potential_required_parts.add(part)
for recipe in self.logic.recipes[part]:
if part == "Fuel":
Debug = True
if recipe.minimal_tier > self.options.final_elevator_package:
continue
self.potential_required_parts.add(part)
if recipe.minimal_belt_speed == 5:
Debug = True
for recipe in self.logic.recipes[part]:
self.potential_required_belt_speed = \
max(self.potential_required_belt_speed, recipe.minimal_belt_speed)
self.potential_required_belt_speed = \
max(self.potential_required_belt_speed, recipe.minimal_belt_speed)
self.select_minimal_required_parts_for(recipe.inputs)
if recipe.needs_pipes:
self.potential_required_pipes = True
if recipe.is_radio_active:
self.potential_required_radioactive = True
if recipe.building:
if recipe.building == "Blender":
debug = True
self.select_minimal_required_parts_for(recipe.inputs)
self.select_minimal_required_parts_for(self.logic.buildings[recipe.building].inputs)
self.potential_required_buildings.add(recipe.building)
if self.logic.buildings[recipe.building].power_requirement:
self.potential_required_power = \
max(self.potential_required_power, self.logic.buildings[recipe.building].power_requirement)
max(self.potential_required_power,
self.logic.buildings[recipe.building].power_requirement)
debug = True

View File

@@ -56,13 +56,14 @@ class Recipe():
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, ...]
minimal_tier: int
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):
additional_outputs: Optional[Tuple[str, ...]] = None, minimal_tier: Optional[int] = 1):
self.name = "Recipe: " + name
self.building = building
self.inputs = inputs
@@ -70,6 +71,7 @@ class Recipe():
self.handcraftable = handcraftable
self.implicitly_unlocked = implicitly_unlocked
self.additional_outputs = additional_outputs
self.minimal_tier = minimal_tier
all_parts: List[str] = [name]
if inputs:
@@ -100,11 +102,14 @@ class MamNode():
"""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"""
minimal_tier: Optional[int]
def __init__(self, name: str, unlock_cost: Dict[str, int], depends_on: Tuple[str, ...]):
def __init__(self, name: str, unlock_cost: Dict[str, int], depends_on: Tuple[str, ...],
minimal_tier: Optional[int] = 1):
self.name = name
self.unlock_cost = unlock_cost
self.depends_on = depends_on
self.minimal_tier = minimal_tier
class MamTree():
@@ -253,7 +258,7 @@ class GameLogic:
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("Aluminum Beam", "Constructor", ("Aluminum Ingot", ), minimal_tier=2),
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", )),
@@ -266,7 +271,7 @@ class GameLogic:
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("Diluted Fuel", "Blender", ("Heavy Oil Residue", "Water"), minimal_tier=2),
Recipe("Residual Fuel", "Refinery", ("Heavy Oil Residue", ))),
"Concrete": (
Recipe("Concrete", "Constructor", ("Limestone", ), handcraftable=True, implicitly_unlocked=True),
@@ -277,14 +282,14 @@ class GameLogic:
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", ))),
Recipe("Distilled Silica", "Blender", ("Dissolved Silica", "Limestone", "Water"), additional_outputs=("Water", ), minimal_tier=2)),
"Dissolved Silica": (
Recipe("Quartz Purification", "Refinery", ("Raw Quartz", "Nitric Acid"), additional_outputs=("Quartz Crystal", ), minimal_belt_speed=2), ),
Recipe("Quartz Purification", "Refinery", ("Raw Quartz", "Nitric Acid"), additional_outputs=("Quartz Crystal", ), minimal_belt_speed=2, minimal_tier=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)),
Recipe("Quartz Purification", "Refinery", ("Raw Quartz", "Nitric Acid"), additional_outputs=("Dissolved Silica", ), minimal_belt_speed=2, minimal_tier=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),
@@ -364,48 +369,48 @@ class GameLogic:
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)),
Recipe("Alumina Solution", "Refinery", ("Bauxite", "Water"), additional_outputs=("Silica", ), minimal_belt_speed=2, minimal_tier=2),
Recipe("Sloppy Alumina", "Refinery", ("Bauxite", "Water"), minimal_belt_speed=3, minimal_tier=2)),
"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)),
Recipe("Aluminum Scrap", "Refinery", ("Alumina Solution", "Coal"), additional_outputs=("Water", ), minimal_belt_speed=4, minimal_tier=2),
Recipe("Electrode Aluminum Scrap", "Refinery", ("Alumina Solution", "Petroleum Coke"), additional_outputs=("Water", ), minimal_belt_speed=4, minimal_tier=2),
Recipe("Instant Scrap", "Blender", ("Bauxite", "Coal", "Sulfuric Acid", "Water"), additional_outputs=("Water", ), minimal_belt_speed=3, minimal_tier=2)),
"Aluminum Ingot": (
Recipe("Aluminum Ingot", "Foundry", ("Aluminum Scrap", "Silica"), minimal_belt_speed=2, handcraftable=True),
Recipe("Pure Aluminum Ingot", "Smelter", ("Aluminum Scrap", ))),
Recipe("Aluminum Ingot", "Foundry", ("Aluminum Scrap", "Silica"), minimal_belt_speed=2, handcraftable=True, minimal_tier=2),
Recipe("Pure Aluminum Ingot", "Smelter", ("Aluminum Scrap", ), minimal_tier=2)),
"Alclad Aluminum Sheet": (
Recipe("Alclad Aluminum Sheet", "Assembler", ("Aluminum Ingot", "Copper Ingot"), handcraftable=True), ),
Recipe("Alclad Aluminum Sheet", "Assembler", ("Aluminum Ingot", "Copper Ingot"), handcraftable=True, minimal_tier=2), ),
"Aluminum Casing": (
Recipe("Aluminum Casing", "Constructor", ("Alclad Aluminum Sheet", ), handcraftable=True),
Recipe("Alclad Casing", "Assembler", ("Aluminum Ingot", "Copper Ingot"))),
Recipe("Aluminum Casing", "Constructor", ("Alclad Aluminum Sheet", ), handcraftable=True, minimal_tier=2),
Recipe("Alclad Casing", "Assembler", ("Aluminum Ingot", "Copper Ingot"), minimal_tier=2)),
"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)),
Recipe("Heat Sink", "Assembler", ("Alclad Aluminum Sheet", "Silica"), minimal_belt_speed=2, handcraftable=True, minimal_tier=2),
Recipe("Heat Exchanger", "Assembler", ("Aluminum Casing", "Rubber"), minimal_belt_speed=3, minimal_tier=2)),
"Nitric Acid": (
Recipe("Nitric Acid", "Blender", ("Nitrogen Gas", "Water", "Iron Plate")), ),
Recipe("Nitric Acid", "Blender", ("Nitrogen Gas", "Water", "Iron Plate"), minimal_tier=2), ),
"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)),
Recipe("Fused Modular Frame", "Blender", ("Heavy Modular Frame", "Aluminum Casing", "Nitrogen Gas"), minimal_belt_speed=2, minimal_tier=2),
Recipe("Heat-Fused Frame", "Blender", ("Heavy Modular Frame", "Aluminum Ingot", "Nitric Acid", "Fuel"), minimal_belt_speed=3, minimal_tier=2)),
"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)),
Recipe("Radio Control Unit", "Manufacturer", ("Aluminum Casing", "Crystal Oscillator", "Computer"), handcraftable=True, minimal_tier=2),
Recipe("Radio Connection Unit", "Manufacturer", ("Heat Sink", "High-Speed Connector", "Quartz Crystal"), minimal_tier=2),
Recipe("Radio Control System", "Manufacturer", ("Crystal Oscillator", "Circuit Board", "Aluminum Casing", "Rubber"), minimal_belt_speed=2, minimal_tier=2)),
"Pressure Conversion Cube": (
Recipe("Pressure Conversion Cube", "Assembler", ("Fused Modular Frame", "Radio Control Unit"), handcraftable=True), ),
Recipe("Pressure Conversion Cube", "Assembler", ("Fused Modular Frame", "Radio Control Unit"), handcraftable=True, minimal_tier=2), ),
"Cooling System": (
Recipe("Cooling System", "Blender", ("Heat Sink", "Rubber", "Water", "Nitrogen Gas")),
Recipe("Cooling Device", "Blender", ("Heat Sink", "Motor", "Nitrogen Gas"))),
Recipe("Cooling System", "Blender", ("Heat Sink", "Rubber", "Water", "Nitrogen Gas"), minimal_tier=2),
Recipe("Cooling Device", "Blender", ("Heat Sink", "Motor", "Nitrogen Gas"), minimal_tier=2)),
"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"))),
Recipe("Turbo Motor", "Manufacturer", ("Cooling System", "Radio Control Unit", "Motor", "Rubber"), handcraftable=True, minimal_tier=2),
Recipe("Turbo Electric Motor", "Manufacturer", ("Motor", "Radio Control Unit", "Electromagnetic Control Rod", "Rotor"), minimal_tier=2),
Recipe("Turbo Pressure Motor", "Manufacturer", ("Motor", "Pressure Conversion Cube", "Packaged Nitrogen Gas", "Stator"), minimal_tier=2)),
"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)),
Recipe("Battery", "Blender", ("Sulfuric Acid", "Alumina Solution", "Aluminum Casing"), additional_outputs=("Water", ), minimal_tier=2),
Recipe("Classic Battery", "Manufacturer", ("Sulfur", "Alclad Aluminum Sheet", "Plastic", "Wire"), minimal_belt_speed=2, minimal_tier=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"))),
Recipe("OC Supercomputer", "Assembler", ("Radio Control Unit", "Cooling System"), minimal_tier=2),
Recipe("Super-State Computer", "Manufacturer", ("Computer", "Electromagnetic Control Rod", "Battery", "Wire"), minimal_tier=2)),
"Sulfuric Acid": (
Recipe("Sulfuric Acid", "Refinery", ("Sulfur", "Water")), ),
"Encased Uranium Cell": (
@@ -438,7 +443,7 @@ class GameLogic:
"Copper Powder": (
Recipe("Copper Powder", "Constructor", ("Copper Ingot", ), handcraftable=True), ),
"Nuclear Pasta": (
Recipe("Nuclear Pasta", "Particle Accelerator", ("Copper Powder", "Pressure Conversion Cube")), ),
Recipe("Nuclear Pasta", "Particle Accelerator", ("Copper Powder", "Pressure Conversion Cube"), minimal_tier=2), ),
"Thermal Propulsion Rocket": (
Recipe("Thermal Propulsion Rocket", "Manufacturer", ("Modular Engine", "Turbo Motor", "Cooling System", "Fused Modular Frame")), ),
"Alien Protein": (
@@ -450,7 +455,7 @@ class GameLogic:
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)),
Recipe("Biomass (Alien Protein)", "Constructor", ("Alien Protein", ), minimal_belt_speed=4, handcraftable=True)),
"Fabric": (
Recipe("Fabric", "Assembler", ("Biomass", "Mycelia"), handcraftable=True, minimal_belt_speed=2),
Recipe("Polyester Fabric", "Refinery", ("Polymer Resin", "Water"))),
@@ -463,7 +468,7 @@ class GameLogic:
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), ),
Recipe("Empty Fluid Tank", "Constructor", ("Aluminum Ingot", ), handcraftable=True, minimal_tier=2), ),
"Packaged Alumina Solution": (
Recipe("Packaged Alumina Solution", "Packager", ("Alumina Solution", "Empty Canister"), minimal_belt_speed=2), ),
"Packaged Fuel": (
@@ -488,7 +493,7 @@ class GameLogic:
"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"))),
Recipe("Turbo Blend Fuel", "Blender", ("Fuel", "Heavy Oil Residue", "Sulfur", "Petroleum Coke"), minimal_tier=2)),
"Gas Mask": (
Recipe("Gas Mask", "Equipment Workshop", ("Rubber", "Plastic", "Fabric"), handcraftable=True, minimal_belt_speed=0), ),
"Alien DNA Capsule": (
@@ -508,7 +513,7 @@ class GameLogic:
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
Recipe("Synthetic Power Shard", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Time Crystal", "Dark Matter Crystal", "Quartz Crystal"), minimal_tier=2)), # 1.0
"Object Scanner": (
Recipe("Object Scanner", "Equipment Workshop", ("Reinforced Iron Plate", "Wire", "Screw"), handcraftable=True), ),
"Xeno-Zapper": (
@@ -516,8 +521,8 @@ class GameLogic:
#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", ))),
Recipe("Rocket Fuel", "Blender", ("Turbofuel", "Nitric Acid"), additional_outputs=("Compacted Coal", ), minimal_tier=2),
Recipe("Nitro Rocket Fuel", "Blender", ("Fuel", "Nitrogen Gas", "Sulfur", "Coal"), minimal_belt_speed=2, additional_outputs=("Compacted Coal", ), minimal_tier=2)),
#"Ionized Fuel": (
# Recipe("Ionized Fuel", "Refinery", ("Rocket Fuel", "Power Shard"), additional_outputs=("Compacted Coal", )), ),
"Packaged Rocket Fuel": (
@@ -580,7 +585,7 @@ class GameLogic:
"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),
"Blender": Building("Blender", ("Motor", "Heavy Modular Frame", "Aluminum Casing", "Radio Control Unit"), PowerInfrastructureLevel.Advanced),
"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")),
@@ -783,7 +788,7 @@ class GameLogic:
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",)),
MamNode("Alien Power Matrix", {"Singularity Cell":50,"Power Shard":100,"SAM Fluctuator":500}, depends_on=("Power Augmenter",), minimal_tier=4),
)),
# 1.0
"Caterium": MamTree(("Caterium Ore", ), ( # Caterium (BPD_ResearchTree_Caterium_C)
@@ -829,7 +834,7 @@ class GameLogic:
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
MamNode("Synthetic Power Shards", {"Power Shard":10,"Time Crystal":100,"Quartz Crystal":200,}, depends_on=("Purple Power Shards", ), minimal_tier=4), # 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)
@@ -855,12 +860,12 @@ class GameLogic:
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 Rifle Ammo", {"Rifle Ammo":1000,"Packaged Turbofuel":50,"Aluminum Casing":100,}, depends_on=("The Rifle", ), minimal_tier=2), #(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
MamNode("Nuclear Deterrent Development", {"Nobelisk":500,"Encased Uranium Cell":10,"AI Limiter":100,}, depends_on=("Cluster Nobelisk", ), minimal_tier=2), #(Research_Sulfur_5_1_C) # 1.0
MamNode("Rocket Fuel", {"Hard Drive":1,"Empty Fluid Tank":10,"Packaged Turbofuel":100,}, depends_on=("Turbo Fuel", ), minimal_tier=3), # 1.0
MamNode("Ionized Fuel", {"Hard Drive":1,"Power Shard":100,"Packaged Rocket Fuel":200,}, depends_on=("Turbo Fuel", ), minimal_tier=4), # 1.0
))
}

View File

@@ -28,10 +28,14 @@ class LocationData():
class Part(LocationData):
@staticmethod
def get_parts(state_logic: StateLogic, recipes: Tuple[Recipe, ...], name: str, items: Items) -> List[LocationData]:
def get_parts(state_logic: StateLogic, recipes: Tuple[Recipe, ...], name: str, items: Items,
final_elevator_tier: int) -> List[LocationData]:
recipes_per_region: Dict[str, List[Recipe]] = {}
for recipe in recipes:
if recipe.minimal_tier > final_elevator_tier:
continue
recipes_per_region.setdefault(recipe.building or "Overworld", []).append(recipe)
return [Part(state_logic, region, recipes_for_region, name, items)
@@ -44,6 +48,9 @@ class Part(LocationData):
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 == "Iron 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])
@@ -74,7 +81,10 @@ class EventBuilding(LocationData):
) -> Callable[[CollectionState], bool]:
def can_build(state: CollectionState) -> bool:
return state_logic.has_recipe(state, building) \
if building.name == "Building: Manufacturer":
debug = True
return (building.implicitly_unlocked or 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)
@@ -92,6 +102,9 @@ class PowerInfrastructure(LocationData):
) -> Callable[[CollectionState], bool]:
def can_power(state: CollectionState) -> bool:
if powerLevel == PowerInfrastructureLevel.Automated:
debug = True
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)
@@ -127,11 +140,11 @@ class ShopSlot(LocationData):
if not state_logic or cost < 20:
return True
elif (cost >= 20 and cost < 50):
return state_logic.is_game_phase(state, 1)
return state_logic.is_elevator_tier(state, 1)
elif (cost >= 50 and cost < 100):
return state_logic.is_game_phase(state, 2)
return state_logic.is_elevator_tier(state, 2)
else:
return state_logic.is_game_phase(state, 3)
return state_logic.is_elevator_tier(state, 3)
return can_purchase
@@ -309,7 +322,7 @@ class Locations():
location_table = self.get_base_location_table()
location_table.extend(self.get_hub_locations(True, self.max_tiers))
location_table.extend(self.get_drop_pod_locations(True, self.max_tiers))
location_table.extend(self.get_drop_pod_locations(True, self.max_tiers, set()))
location_table.append(LocationData("Overworld", "UpperBound", 1338999))
return {location.name: location.code for location in location_table}
@@ -324,8 +337,8 @@ class Locations():
location_table = self.get_base_location_table()
location_table.extend(self.get_hub_locations(False, max_tier_for_game))
location_table.extend(self.get_drop_pod_locations(False, max_tier_for_game))
location_table.extend(self.get_logical_event_locations())
location_table.extend(self.get_drop_pod_locations(False, max_tier_for_game, self.critical_path.potential_required_parts))
location_table.extend(self.get_logical_event_locations(self.options.final_elevator_package))
return location_table
@@ -358,7 +371,7 @@ class Locations():
return location_table
def get_logical_event_locations(self) -> List[LocationData]:
def get_logical_event_locations(self, final_elevator_tier: int) -> List[LocationData]:
location_table: List[LocationData] = []
# for performance plan is to upfront calculated everything we need
@@ -372,7 +385,7 @@ class Locations():
part
for part_name, recipes in self.game_logic.recipes.items()
if part_name in self.critical_path.potential_required_parts
for part in Part.get_parts(self.state_logic, recipes, part_name, self.items))
for part in Part.get_parts(self.state_logic, recipes, part_name, self.items, final_elevator_tier))
location_table.extend(
EventBuilding(self.game_logic, self.state_logic, name, building)
for name, building in self.game_logic.buildings.items()
@@ -384,7 +397,8 @@ class Locations():
return location_table
def get_drop_pod_locations(self, for_data_package: bool, max_tier: int) -> List[LocationData]:
def get_drop_pod_locations(self, for_data_package: bool, max_tier: int, available_parts: set[str]) \
-> List[LocationData]:
drop_pod_locations: List[DropPod] = []
bucket_size: int
@@ -403,10 +417,15 @@ class Locations():
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
if location_id_normalized == 81:
debug = True
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), max_tier)
drop_pod_locations.append(DropPod(data, self.state_logic, location_id, tier, can_hold_progression))
if not data.item or data.item in available_parts:
drop_pod_locations.append(DropPod(data, self.state_logic, location_id, tier, can_hold_progression))
return drop_pod_locations

View File

@@ -33,7 +33,6 @@ def create_regions_and_return_locations(world: MultiWorld, options: Satisfactory
game_logic: GameLogic, state_logic: StateLogic, locations: List[LocationData]):
region_names: List[str] = [
"Menu",
"Overworld",
"Gas Area",
"Radioactive Area",
@@ -89,9 +88,8 @@ def create_regions_and_return_locations(world: MultiWorld, options: Satisfactory
super_early_game_buildings.append("Conveyor Merger")
if options.final_elevator_package == 1:
super_early_game_buildings.append(early_game_buildings)
super_early_game_buildings.extend(early_game_buildings)
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))

View File

@@ -60,6 +60,9 @@ class StateLogic:
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.name == "Recipe: Iron Ingot":
debug = True
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"))):
@@ -76,8 +79,8 @@ class StateLogic:
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:
limited_phase = min(self.options.final_elevator_package * 2, phase)
def is_elevator_tier(self, state: CollectionState, phase: int) -> bool:
limited_phase = min(self.options.final_elevator_package, phase)
return state.has(f"Elevator Tier {limited_phase}", self.player)

View File

@@ -23,6 +23,7 @@ class SatisfactoryWorld(World):
topology_present = False
data_version = 0
web = SatisfactoryWebWorld()
origin_region_name = "Overworld"
item_name_to_id = Items.item_names_and_ids
location_name_to_id = Locations().get_locations_for_data_package()