mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-22 07:35:37 -07:00
Fixed generation with different elevator tiers
This commit is contained in:
@@ -61,7 +61,8 @@ class TestBase(unittest.TestCase):
|
||||
self.assertFalse(region.can_reach(state))
|
||||
else:
|
||||
with self.subTest("Region should be reached", region=region.name):
|
||||
self.assertTrue(region.can_reach(state))
|
||||
if not region.can_reach(state):
|
||||
self.assertTrue(region.can_reach(state))
|
||||
|
||||
with self.subTest("Completion Condition"):
|
||||
self.assertTrue(multiworld.can_beat_game(state))
|
||||
|
||||
@@ -19,6 +19,10 @@ class CriticalPathCalculator:
|
||||
__potential_required_pipes: bool
|
||||
__potential_required_radioactive: bool
|
||||
|
||||
parts_to_exclude: set[str]
|
||||
recipes_to_exclude: set[str]
|
||||
buildings_to_exclude: set[str]
|
||||
|
||||
def __init__(self, logic: GameLogic, random: Random, options: SatisfactoryOptions):
|
||||
self.logic = logic
|
||||
self.random = random
|
||||
@@ -30,7 +34,6 @@ class CriticalPathCalculator:
|
||||
|
||||
self.__potential_required_belt_speed = 1
|
||||
self.__potential_required_pipes = False
|
||||
self.__potential_required_radioactive = False
|
||||
|
||||
selected_power_infrastructure: dict[int, Recipe] = {}
|
||||
|
||||
@@ -57,9 +60,8 @@ class CriticalPathCalculator:
|
||||
self.select_minimal_required_parts_for_building("Power Storage")
|
||||
self.select_minimal_required_parts_for_building("Miner Mk.2")
|
||||
|
||||
#equipment
|
||||
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)
|
||||
if self.logic.recipes["Uranium"][0].minimal_tier <= options.final_elevator_package:
|
||||
self.select_minimal_required_parts_for(("Hazmat Suit", "Iodine Infused Filter"))
|
||||
|
||||
for i in range(1, self.__potential_required_belt_speed + 1):
|
||||
self.select_minimal_required_parts_for_building(f"Conveyor Mk.{i}")
|
||||
@@ -68,9 +70,6 @@ class CriticalPathCalculator:
|
||||
self.select_minimal_required_parts_for_building("Pipes Mk.2")
|
||||
self.select_minimal_required_parts_for_building("Pipeline Pump Mk.1")
|
||||
self.select_minimal_required_parts_for_building("Pipeline Pump Mk.2")
|
||||
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(1, self.required_power_level + 1):
|
||||
power_recipe = random.choice(self.logic.requirement_per_powerlevel[i])
|
||||
selected_power_infrastructure[i] = power_recipe
|
||||
@@ -85,6 +84,55 @@ class CriticalPathCalculator:
|
||||
)
|
||||
self.required_item_names.update("Building: "+ building for building in self.required_buildings)
|
||||
|
||||
self.parts_to_exclude = set()
|
||||
self.buildings_to_exclude = set()
|
||||
self.recipes_to_exclude = set(
|
||||
recipe.name
|
||||
for part in self.logic.recipes
|
||||
for recipe in self.logic.recipes[part]
|
||||
if recipe.minimal_tier > self.options.final_elevator_package
|
||||
)
|
||||
|
||||
excluded_count = len(self.recipes_to_exclude)
|
||||
while True:
|
||||
for part in self.logic.recipes:
|
||||
if part in self.parts_to_exclude:
|
||||
continue
|
||||
|
||||
for recipe in self.logic.recipes[part]:
|
||||
if recipe.name in self.recipes_to_exclude:
|
||||
continue
|
||||
|
||||
if recipe.inputs and any(input in self.parts_to_exclude for input in recipe.inputs):
|
||||
self.recipes_to_exclude.add(recipe.name)
|
||||
|
||||
if all(r.name in self.recipes_to_exclude for r in self.logic.recipes[part]):
|
||||
self.parts_to_exclude.add(part)
|
||||
|
||||
new_buildings_to_exclude = set(
|
||||
building_name
|
||||
for building_name, building in self.logic.buildings.items()
|
||||
if building_name not in self.buildings_to_exclude
|
||||
and building.inputs and any(input in self.parts_to_exclude for input in building.inputs)
|
||||
)
|
||||
|
||||
self.recipes_to_exclude.update(
|
||||
recipe_per_part.name
|
||||
for building_to_exclude in new_buildings_to_exclude
|
||||
for recipes_per_part in self.logic.recipes.values()
|
||||
for recipe_per_part in recipes_per_part
|
||||
if recipe_per_part.building == building_to_exclude
|
||||
)
|
||||
|
||||
self.buildings_to_exclude.update(new_buildings_to_exclude)
|
||||
|
||||
new_length = len(self.recipes_to_exclude)
|
||||
if new_length == excluded_count:
|
||||
break
|
||||
excluded_count = new_length
|
||||
|
||||
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.required_buildings.add(building)
|
||||
|
||||
@@ -191,15 +191,15 @@ class GameLogic:
|
||||
"Crude Oil": (
|
||||
Recipe("Crude Oil", "Oil Extractor", implicitly_unlocked=True), ),
|
||||
"Bauxite": (
|
||||
Recipe("Bauxite", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
|
||||
Recipe("Bauxite", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True, minimal_tier=2), ),
|
||||
"Nitrogen Gas": (
|
||||
Recipe("Nitrogen Gas", "Resource Well Pressurizer", implicitly_unlocked=True), ),
|
||||
Recipe("Nitrogen Gas", "Resource Well Pressurizer", implicitly_unlocked=True, minimal_tier=2), ),
|
||||
"Uranium": (
|
||||
Recipe("Uranium", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True), ),
|
||||
Recipe("Uranium", "Miner Mk.1", handcraftable=True, implicitly_unlocked=True, minimal_tier=2), ),
|
||||
|
||||
# Special Items
|
||||
"Uranium Waste": (
|
||||
Recipe("Uranium Waste", "Nuclear Power Plant", ("Uranium Fuel Rod", "Water"), implicitly_unlocked=True), ),
|
||||
Recipe("Uranium Waste", "Nuclear Power Plant", ("Uranium Fuel Rod", "Water"), implicitly_unlocked=True, minimal_tier=2), ),
|
||||
#"Plutonium Waste": (
|
||||
# Recipe("Plutonium Waste", "Nuclear Power Plant", ("Plutonium Fuel Rod", "Water"), implicitly_unlocked=True), ),
|
||||
|
||||
@@ -280,7 +280,7 @@ class GameLogic:
|
||||
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("Alumina Solution", "Refinery", ("Bauxite", "Water"), additional_outputs=("Alumina Solution", ), minimal_belt_speed=2, minimal_tier=2),
|
||||
Recipe("Cheap Silica", "Assembler", ("Raw Quartz", "Limestone")),
|
||||
Recipe("Distilled Silica", "Blender", ("Dissolved Silica", "Limestone", "Water"), additional_outputs=("Water", ), minimal_tier=2)),
|
||||
"Dissolved Silica": (
|
||||
@@ -356,18 +356,18 @@ class GameLogic:
|
||||
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"))),
|
||||
Recipe("Versatile Framework", "Assembler", ("Modular Frame", "Steel Beam"), minimal_tier=2),
|
||||
Recipe("Flexible Framework", "Manufacturer", ("Modular Frame", "Steel Beam", "Rubber"), minimal_tier=2)),
|
||||
"Automated Wiring": (
|
||||
Recipe("Automated Wiring", "Assembler", ("Stator", "Cable")),
|
||||
Recipe("Automated Speed Wiring", "Manufacturer", ("Stator", "Wire", "High-Speed Connector"), minimal_belt_speed=2)),
|
||||
Recipe("Automated Wiring", "Assembler", ("Stator", "Cable"), minimal_tier=2),
|
||||
Recipe("Automated Speed Wiring", "Manufacturer", ("Stator", "Wire", "High-Speed Connector"), minimal_belt_speed=2, minimal_tier=2)),
|
||||
"Modular Engine": (
|
||||
Recipe("Modular Engine", "Manufacturer", ("Motor", "Rubber", "Smart Plating")), ),
|
||||
Recipe("Modular Engine", "Manufacturer", ("Motor", "Rubber", "Smart Plating"), minimal_tier=3), ),
|
||||
"Adaptive Control Unit": (
|
||||
Recipe("Adaptive Control Unit", "Manufacturer", ("Automated Wiring", "Circuit Board", "Heavy Modular Frame", "Computer")), ),
|
||||
Recipe("Adaptive Control Unit", "Manufacturer", ("Automated Wiring", "Circuit Board", "Heavy Modular Frame", "Computer"), minimal_tier=3), ),
|
||||
"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")), ),
|
||||
Recipe("Automated Miner", "Assembler", ("Steel Pipe", "Iron Plate")), ),
|
||||
"Alumina Solution": (
|
||||
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)),
|
||||
@@ -433,19 +433,19 @@ class GameLogic:
|
||||
"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), ),
|
||||
Recipe("Iodine Infused Filter", "Manufacturer", ("Gas Filter", "Quickwire", "Aluminum Casing"), handcraftable=True, minimal_tier=2), ),
|
||||
"Hazmat Suit": (
|
||||
Recipe("Hazmat Suit", "Equipment Workshop", ("Rubber", "Plastic", "Fabric", "Alclad Aluminum Sheet"), handcraftable=True, minimal_belt_speed=0), ),
|
||||
Recipe("Hazmat Suit", "Equipment Workshop", ("Rubber", "Plastic", "Fabric", "Alclad Aluminum Sheet"), handcraftable=True, minimal_tier=2), ),
|
||||
"Assembly Director System": (
|
||||
Recipe("Assembly Director System", "Assembler", ("Adaptive Control Unit", "Supercomputer")), ),
|
||||
Recipe("Assembly Director System", "Assembler", ("Adaptive Control Unit", "Supercomputer"), minimal_tier=4), ),
|
||||
"Magnetic Field Generator": (
|
||||
Recipe("Magnetic Field Generator", "Assembler", ("Versatile Framework", "Electromagnetic Control Rod")), ),
|
||||
Recipe("Magnetic Field Generator", "Assembler", ("Versatile Framework", "Electromagnetic Control Rod"), minimal_tier=4), ),
|
||||
"Copper Powder": (
|
||||
Recipe("Copper Powder", "Constructor", ("Copper Ingot", ), handcraftable=True), ),
|
||||
"Nuclear Pasta": (
|
||||
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")), ),
|
||||
Recipe("Thermal Propulsion Rocket", "Manufacturer", ("Modular Engine", "Turbo Motor", "Cooling System", "Fused Modular Frame"), minimal_tier=4), ),
|
||||
"Alien Protein": (
|
||||
Recipe("Hatcher Protein", "Constructor", ("Hatcher Remains", ), handcraftable=True),
|
||||
Recipe("Hog Protein", "Constructor", ("Hog Remains", ), handcraftable=True),
|
||||
@@ -513,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"), minimal_tier=2)), # 1.0
|
||||
Recipe("Synthetic Power Shard", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Time Crystal", "Dark Matter Crystal", "Quartz Crystal"), minimal_tier=4)), # 1.0
|
||||
"Object Scanner": (
|
||||
Recipe("Object Scanner", "Equipment Workshop", ("Reinforced Iron Plate", "Wire", "Screw"), handcraftable=True), ),
|
||||
"Xeno-Zapper": (
|
||||
@@ -559,9 +559,9 @@ class GameLogic:
|
||||
"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")), ),
|
||||
Recipe("Biochemical Sculptor", "Blender", ("Assembly Director System", "Ficsite Trigon", "Water"), minimal_tier=5), ),
|
||||
"Ballistic Warp Drive": (
|
||||
Recipe("Ballistic Warp Drive", "Manufacturer", ("Thermal Propulsion Rocket", "Singularity Cell", "Superposition Oscillator", "Dark Matter Crystal")), ),
|
||||
Recipe("Ballistic Warp Drive", "Manufacturer", ("Thermal Propulsion Rocket", "Singularity Cell", "Superposition Oscillator", "Dark Matter Crystal"), minimal_tier=5), ),
|
||||
|
||||
# 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": (
|
||||
@@ -574,7 +574,7 @@ class GameLogic:
|
||||
"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")), ),
|
||||
Recipe("AI Expansion Server", "Quantum Encoder", ("Dark Matter Residue", "Excited Photonic Matter", "Magnetic Field Generator", "Neural-Quantum Processor", "Superposition Oscillator"), minimal_tier=5), ),
|
||||
###
|
||||
#1.0
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class ItemGroups(IntFlag):
|
||||
Vehicles = 1 << 26
|
||||
Customizer = 1 << 27
|
||||
ConveyorMk6 = 1 << 28
|
||||
BasicNeeds = 1 << 29
|
||||
AlwaysUseful = 1 << 29
|
||||
|
||||
|
||||
class ItemData(NamedTuple):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import copy
|
||||
from random import Random
|
||||
from typing import ClassVar, Dict, Set, List, Tuple, Optional
|
||||
from BaseClasses import Item, ItemClassification as C, MultiWorld
|
||||
@@ -194,7 +193,7 @@ class Items:
|
||||
"Small Inflated Pocket Dimension": ItemData(G.Upgrades, 1338188, C.useful, 11),
|
||||
"Inflated Pocket Dimension": ItemData(G.Upgrades, 1338189, C.useful, 5),
|
||||
"Expanded Toolbelt": ItemData(G.Upgrades, 1338190, C.useful, 5),
|
||||
"Dimensional Depot upload from inventory": ItemData(G.Upgrades | G.BasicNeeds, 1338191, C.useful),
|
||||
"Dimensional Depot upload from inventory": ItemData(G.Upgrades, 1338191, C.useful),
|
||||
|
||||
#1338191 - 1338199 Reserved for future equipment/ammo
|
||||
|
||||
@@ -493,17 +492,17 @@ class Items:
|
||||
"Building: Fuel Generator": ItemData(G.Building, 1338618, C.progression),
|
||||
"Building: Resource Well Pressurizer": ItemData(G.Building, 1338619, C.progression),
|
||||
"Building: Equipment Workshop": ItemData(G.Building, 1338620, C.progression),
|
||||
"Building: AWESOME Sink": ItemData(G.Building | G.BasicNeeds, 1338621, C.progression),
|
||||
"Building: AWESOME Shop": ItemData(G.Building | G.BasicNeeds, 1338622, C.progression),
|
||||
"Building: AWESOME Sink": ItemData(G.Building | G.AlwaysUseful, 1338621, C.progression),
|
||||
"Building: AWESOME Shop": ItemData(G.Building | G.AlwaysUseful, 1338622, C.progression),
|
||||
"Building: Painted Beams": ItemData(G.Beams, 1338623, C.filler),
|
||||
"Building: Blueprint Designer": ItemData(G.Building, 1338624, C.filler, 0), # unlocked by default
|
||||
"Building: Fluid Buffer": ItemData(G.Building, 1338625, C.filler),
|
||||
"Building: Industrial Fluid Buffer": ItemData(G.Building, 1338626, C.filler),
|
||||
"Building: Jump Pad": ItemData(G.Building, 1338627, C.filler),
|
||||
"Building: Ladder": ItemData(G.Building, 1338628, C.filler),
|
||||
"Building: MAM": ItemData(G.Building | G.BasicNeeds, 1338629, C.progression),
|
||||
"Building: MAM": ItemData(G.Building | G.AlwaysUseful, 1338629, C.progression),
|
||||
"Building: Personal Storage Box": ItemData(G.Building, 1338630, C.filler),
|
||||
"Building: Power Storage": ItemData(G.Building | G.BasicNeeds, 1338631, C.progression),
|
||||
"Building: Power Storage": ItemData(G.Building | G.AlwaysUseful, 1338631, C.progression),
|
||||
"Building: U-Jelly Landing Pad": ItemData(G.Building, 1338632, C.useful),
|
||||
"Building: Power Switch": ItemData(G.Building, 1338633, C.useful),
|
||||
"Building: Priority Power Switch": ItemData(G.Building, 1338634, C.useful),
|
||||
@@ -513,8 +512,8 @@ class Items:
|
||||
"Building: Power Pole Mk.2": ItemData(G.Building, 1338638, C.useful),
|
||||
"Building: Power Pole Mk.3": ItemData(G.Building, 1338639, C.useful),
|
||||
"Building: Industrial Storage Container": ItemData(G.Building, 1338640, C.filler),
|
||||
"Building: Conveyor Merger": ItemData(G.Building | G.BasicNeeds, 1338641, C.progression),
|
||||
"Building: Conveyor Splitter": ItemData(G.Building | G.BasicNeeds, 1338642, C.progression),
|
||||
"Building: Conveyor Merger": ItemData(G.Building | G.AlwaysUseful, 1338641, C.progression),
|
||||
"Building: Conveyor Splitter": ItemData(G.Building | G.AlwaysUseful, 1338642, C.progression),
|
||||
"Building: Conveyor Mk.1": ItemData(G.Building | G.ConveyorMk1, 1338643, C.progression), # unlocked by default
|
||||
"Building: Conveyor Mk.2": ItemData(G.Building | G.ConveyorMk2, 1338644, C.progression),
|
||||
"Building: Conveyor Mk.3": ItemData(G.Building | G.ConveyorMk3, 1338645, C.progression),
|
||||
@@ -564,7 +563,7 @@ class Items:
|
||||
#"Building: Beam Support": ItemData(G.Beams, 1338689, C.filler, 0),
|
||||
#"Building: Beam Connector": ItemData(G.Beams, 1338690, C.filler, 0),
|
||||
#"Building: Beam Connector Double": ItemData(G.Beams, 1338691, C.filler, 0),
|
||||
"Building: Foundation": ItemData(G.Building | G.Foundations | G.BasicNeeds, 1338692, C.progression),
|
||||
"Building: Foundation": ItemData(G.Building | G.Foundations | G.AlwaysUseful, 1338692, C.progression),
|
||||
"Building: Half Foundation": ItemData(G.Foundations, 1338693, C.filler, 0),
|
||||
"Building: Corner Ramp Pack": ItemData(G.Foundations, 1338694, C.filler, 0),
|
||||
"Building: Inverted Ramp Pack": ItemData(G.Foundations, 1338695, C.filler, 0),
|
||||
@@ -725,15 +724,14 @@ class Items:
|
||||
|
||||
if type == C.progression \
|
||||
and instance and instance.critical_path.required_item_names \
|
||||
and (data.category & (G.Recipe | G.Building)) and not (data.category & G.BasicNeeds) \
|
||||
and (data.category & (G.Recipe | G.Building)) and not (data.category & G.AlwaysUseful) \
|
||||
and name not in instance.critical_path.required_item_names:
|
||||
type = C.filler
|
||||
logging.info(f"Dropping... {name}")
|
||||
type = C.useful
|
||||
|
||||
return Item(name, type, data.code, player)
|
||||
|
||||
|
||||
def get_filler_item_name(self, random: Random, options: SatisfactoryOptions) -> str:
|
||||
def get_filler_item_name(self, filler_items: Tuple[str, ...], random: Random, options: SatisfactoryOptions) -> str:
|
||||
trap_chance: int = options.trap_chance.value
|
||||
enabled_traps: List[str] = options.trap_selection_override.value
|
||||
|
||||
@@ -745,6 +743,9 @@ class Items:
|
||||
|
||||
def get_excluded_items(self, multiworld: MultiWorld, options: SatisfactoryOptions) -> Set[str]:
|
||||
excluded_items: Set[str] = set()
|
||||
excluded_items.update("Bundle: "+ part for part in self.critical_path.parts_to_exclude)
|
||||
excluded_items.update(recipe for recipe in self.critical_path.recipes_to_exclude)
|
||||
excluded_items.update("Building: "+ building for building in self.critical_path.buildings_to_exclude)
|
||||
|
||||
for item in multiworld.precollected_items[self.player]:
|
||||
if item.name in self.item_data \
|
||||
@@ -775,9 +776,12 @@ class Items:
|
||||
filler_pool_size: int = number_of_locations - len(pool)
|
||||
if (filler_pool_size < 0):
|
||||
raise Exception(f"Location pool starved, trying to add {len(pool)} items to {number_of_locations} locations")
|
||||
logging.warning(f"Itempool size: {len(pool)}, number of locations: {number_of_locations}, spare: {filler_pool_size}")
|
||||
|
||||
filtered_filler_items = tuple(item for item in self.filler_items if item not in excluded_from_pool)
|
||||
|
||||
for _ in range(filler_pool_size):
|
||||
item = self.create_item(self, self.get_filler_item_name(random, options), self.player)
|
||||
filler_item_name = self.get_filler_item_name(filtered_filler_items, random, options)
|
||||
item = self.create_item(self, filler_item_name, self.player)
|
||||
pool.append(item)
|
||||
|
||||
return pool
|
||||
|
||||
@@ -9,6 +9,7 @@ from math import ceil, floor
|
||||
|
||||
|
||||
class LocationData():
|
||||
__slots__ = ("region", "name", "event_name", "code", "non_progression", "rule")
|
||||
region: str
|
||||
name: str
|
||||
event_name: str
|
||||
@@ -28,7 +29,7 @@ class LocationData():
|
||||
|
||||
class Part(LocationData):
|
||||
@staticmethod
|
||||
def get_parts(state_logic: StateLogic, recipes: Tuple[Recipe, ...], name: str, items: Items,
|
||||
def get_parts(state_logic: StateLogic, recipes: Tuple[Recipe, ...], name: str,
|
||||
final_elevator_tier: int) -> List[LocationData]:
|
||||
recipes_per_region: Dict[str, List[Recipe]] = {}
|
||||
|
||||
@@ -38,15 +39,15 @@ class Part(LocationData):
|
||||
|
||||
recipes_per_region.setdefault(recipe.building or "Overworld", []).append(recipe)
|
||||
|
||||
return [Part(state_logic, region, recipes_for_region, name, items)
|
||||
return [Part(state_logic, region, recipes_for_region, name)
|
||||
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 __init__(self, state_logic: StateLogic, region: str, recipes: Iterable[Recipe], name: str):
|
||||
super().__init__(region, part_event_prefix + name + " in " + region, EventId, part_event_prefix + name,
|
||||
rule = self.can_produce_any_recipe_for_part(state_logic, recipes))
|
||||
|
||||
def can_produce_any_recipe_for_part(self, state_logic: StateLogic, recipes: Iterable[Recipe],
|
||||
name: str, items: Items) -> Callable[[CollectionState], bool]:
|
||||
def can_produce_any_recipe_for_part(self, state_logic: StateLogic, recipes: Iterable[Recipe]) \
|
||||
-> Callable[[CollectionState], bool]:
|
||||
def can_build_by_any_recipe(state: CollectionState) -> bool:
|
||||
return any(state_logic.can_produce_specific_recipe_for_part(state, recipe) for recipe in recipes)
|
||||
|
||||
@@ -374,10 +375,10 @@ class Locations():
|
||||
for index, parts in enumerate(self.game_logic.space_elevator_tiers)
|
||||
if index < self.options.final_elevator_package)
|
||||
location_table.extend(
|
||||
part
|
||||
part
|
||||
for part_name, recipes in self.game_logic.recipes.items()
|
||||
if part_name in self.critical_path.required_parts
|
||||
for part in Part.get_parts(self.state_logic, recipes, part_name, self.items, final_elevator_tier))
|
||||
for part in Part.get_parts(self.state_logic, recipes, part_name, final_elevator_tier))
|
||||
location_table.extend(
|
||||
EventBuilding(self.game_logic, self.state_logic, name, building)
|
||||
for name, building in self.game_logic.buildings.items()
|
||||
|
||||
@@ -3,6 +3,7 @@ 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
|
||||
from schema import Schema, And, Use
|
||||
|
||||
class Placement(IntEnum):
|
||||
starting_inventory = 0
|
||||
@@ -41,7 +42,6 @@ class ChoiceMap(Choice, metaclass=ChoiceMapMeta):
|
||||
if index == self.value:
|
||||
return self.choices[choice]
|
||||
|
||||
|
||||
class ElevatorTier(NamedRange):
|
||||
"""
|
||||
Put these Shipments to Space Elevator packages in logic.
|
||||
@@ -347,6 +347,8 @@ class GoalSelection(OptionSet):
|
||||
# "FICSMAS Tree",
|
||||
}
|
||||
default = {"Space Elevator Tier"}
|
||||
schema = Schema(And(set, len),
|
||||
error = "yaml does not specify a goal, the Satisfactory option `goal_selection` is empty")
|
||||
|
||||
class GoalRequirement(Choice):
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from typing import Dict, List, Set, TextIO, ClassVar, Tuple
|
||||
from BaseClasses import Item, MultiWorld, ItemClassification, CollectionState
|
||||
from typing import Dict, List, Set, TextIO, ClassVar
|
||||
from BaseClasses import Item, ItemClassification, CollectionState
|
||||
from .GameLogic import GameLogic
|
||||
from .Items import Items
|
||||
from .Locations import Locations, LocationData
|
||||
@@ -21,7 +21,6 @@ class SatisfactoryWorld(World):
|
||||
options_dataclass = SatisfactoryOptions
|
||||
options: SatisfactoryOptions
|
||||
topology_present = False
|
||||
data_version = 0
|
||||
web = SatisfactoryWebWorld()
|
||||
origin_region_name = "Overworld"
|
||||
|
||||
@@ -34,20 +33,11 @@ class SatisfactoryWorld(World):
|
||||
items: Items
|
||||
critical_path: CriticalPathCalculator
|
||||
|
||||
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.critical_path = CriticalPathCalculator(self.game_logic, self.random, self.options)
|
||||
self.items = Items(self.player, self.game_logic, self.random, self.options, self.critical_path)
|
||||
|
||||
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:
|
||||
@@ -144,15 +134,15 @@ class SatisfactoryWorld(World):
|
||||
}
|
||||
|
||||
|
||||
def write_spoiler(self, spoiler_handle: TextIO):
|
||||
def write_spoiler(self, spoiler_handle: TextIO) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.items.get_filler_item_name(self.random, self.options)
|
||||
return self.items.get_filler_item_name(self.items.filler_items, self.random, self.options)
|
||||
|
||||
|
||||
def setup_events(self):
|
||||
def setup_events(self) -> None:
|
||||
location: SatisfactoryLocation
|
||||
for location in self.multiworld.get_locations(self.player):
|
||||
if location.address == EventId:
|
||||
|
||||
Reference in New Issue
Block a user