mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-23 20:43:18 -07:00
Added support for Universal Tracker
Put more things in the never exclude pool for a more familiar gameplay
This commit is contained in:
@@ -3,12 +3,12 @@ from typing import Optional
|
||||
from collections.abc import Iterable
|
||||
from .GameLogic import GameLogic, Recipe
|
||||
from .Options import SatisfactoryOptions
|
||||
from .Options import SatisfactoryOptions
|
||||
|
||||
class CriticalPathCalculator:
|
||||
logic: GameLogic
|
||||
random: Random
|
||||
options: SatisfactoryOptions
|
||||
final_elevator_package: int
|
||||
randomize_starter_recipes: bool
|
||||
|
||||
required_parts: set[str]
|
||||
required_buildings: set[str]
|
||||
@@ -25,11 +25,13 @@ class CriticalPathCalculator:
|
||||
handcraftable_parts: dict[str, list[Recipe]]
|
||||
tier_0_recipes: set[str]
|
||||
|
||||
def __init__(self, logic: GameLogic, random: Random, options: SatisfactoryOptions):
|
||||
def __init__(self, logic: GameLogic, seed: float, options: SatisfactoryOptions):
|
||||
self.logic = logic
|
||||
self.random = random
|
||||
self.options = options
|
||||
self.random = Random(seed)
|
||||
self.final_elevator_package = options.final_elevator_package.value
|
||||
self.randomize_starter_recipes = bool(options.randomize_starter_recipes.value)
|
||||
|
||||
def calculate(self) -> None:
|
||||
self.required_parts = set()
|
||||
self.required_buildings = set()
|
||||
self.required_power_level: int = 1
|
||||
@@ -38,13 +40,14 @@ class CriticalPathCalculator:
|
||||
|
||||
self.configure_implicitly_unlocked_and_handcraftable_parts()
|
||||
|
||||
self.select_minimal_required_parts_for(self.logic.space_elevator_tiers[options.final_elevator_package-1].keys())
|
||||
self.select_minimal_required_parts_for(
|
||||
self.logic.space_elevator_tiers[self.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:
|
||||
if node.minimal_tier > options.final_elevator_package:
|
||||
if node.minimal_tier > self.final_elevator_package:
|
||||
continue
|
||||
|
||||
self.select_minimal_required_parts_for(node.unlock_cost.keys())
|
||||
@@ -64,15 +67,15 @@ 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.logic.recipes["Uranium"][0].minimal_tier <= options.final_elevator_package:
|
||||
|
||||
if self.logic.recipes["Uranium"][0].minimal_tier <= self.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}")
|
||||
|
||||
for i in range(1, self.required_power_level + 1):
|
||||
power_recipe = random.choice(self.logic.requirement_per_powerlevel[i])
|
||||
power_recipe = self.random.choice(self.logic.requirement_per_powerlevel[i])
|
||||
self.select_minimal_required_parts_for(power_recipe.inputs)
|
||||
self.select_minimal_required_parts_for_building(power_recipe.building)
|
||||
|
||||
@@ -80,7 +83,7 @@ class CriticalPathCalculator:
|
||||
recipe.name
|
||||
for part in self.required_parts
|
||||
for recipe in self.logic.recipes[part]
|
||||
if recipe.minimal_tier <= self.options.final_elevator_package
|
||||
if recipe.minimal_tier <= self.final_elevator_package
|
||||
}
|
||||
self.required_item_names.update({"Building: "+ building for building in self.required_buildings})
|
||||
|
||||
@@ -104,7 +107,7 @@ class CriticalPathCalculator:
|
||||
self.required_parts.add(part)
|
||||
|
||||
for recipe in self.logic.recipes[part]:
|
||||
if recipe.minimal_tier > self.options.final_elevator_package:
|
||||
if recipe.minimal_tier > self.final_elevator_package:
|
||||
continue
|
||||
|
||||
self.__potential_required_belt_speed = \
|
||||
@@ -129,7 +132,7 @@ class CriticalPathCalculator:
|
||||
recipe.name
|
||||
for part in self.logic.recipes
|
||||
for recipe in self.logic.recipes[part]
|
||||
if recipe.minimal_tier > self.options.final_elevator_package
|
||||
if recipe.minimal_tier > self.final_elevator_package
|
||||
}
|
||||
|
||||
excluded_count = len(self.recipes_to_exclude)
|
||||
@@ -191,7 +194,7 @@ class CriticalPathCalculator:
|
||||
|
||||
def select_starter_recipes(self) -> None:
|
||||
# cable is left unaffected as all its alternative recipes require refinery
|
||||
if not self.options.randomize_starter_recipes:
|
||||
if not self.randomize_starter_recipes:
|
||||
self.tier_0_recipes = {
|
||||
"Recipe: Iron Ingot",
|
||||
"Recipe: Iron Plate",
|
||||
|
||||
@@ -31,7 +31,7 @@ class ItemGroups(IntFlag):
|
||||
Vehicles = 1 << 26
|
||||
Customizer = 1 << 27
|
||||
ConveyorMk6 = 1 << 28
|
||||
AlwaysUseful = 1 << 29
|
||||
NeverExclude = 1 << 29
|
||||
|
||||
|
||||
class ItemData(NamedTuple):
|
||||
|
||||
@@ -466,7 +466,7 @@ class Items:
|
||||
"Recipe: Charcoal": ItemData(G.Recipe, 1338461, C.useful),
|
||||
"Recipe: Sloppy Alumina": ItemData(G.Recipe, 1338462, C.progression),
|
||||
"Recipe: Hoverpack": ItemData(G.Recipe, 1338463, C.useful),
|
||||
"Recipe: Jetpack": ItemData(G.Recipe, 1338464, C.useful),
|
||||
"Recipe: Jetpack": ItemData(G.Recipe | G.NeverExclude, 1338464, C.useful),
|
||||
"Recipe: Nobelisk Detonator": ItemData(G.Recipe, 1338465, C.progression),
|
||||
"Recipe: Portable Miner": ItemData(G.Recipe, 1338466, C.progression),
|
||||
#
|
||||
@@ -494,28 +494,28 @@ 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.AlwaysUseful, 1338621, C.progression),
|
||||
"Building: AWESOME Shop": ItemData(G.Building | G.AlwaysUseful, 1338622, C.progression),
|
||||
"Building: AWESOME Sink": ItemData(G.Building | G.NeverExclude, 1338621, C.progression),
|
||||
"Building: AWESOME Shop": ItemData(G.Building | G.NeverExclude, 1338622, C.progression),
|
||||
"Building: Structural Beam Pack": 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: Fluid Buffer": ItemData(G.Building | G.NeverExclude, 1338625, C.useful),
|
||||
"Building: Industrial Fluid Buffer": ItemData(G.Building | G.NeverExclude, 1338626, C.useful),
|
||||
"Building: Jump Pad": ItemData(G.Building, 1338627, C.filler),
|
||||
"Building: Ladder": ItemData(G.Building, 1338628, C.filler),
|
||||
"Building: MAM": ItemData(G.Building | G.AlwaysUseful, 1338629, C.progression),
|
||||
"Building: MAM": ItemData(G.Building | G.NeverExclude, 1338629, C.progression),
|
||||
"Building: Personal Storage Box": ItemData(G.Building, 1338630, C.filler),
|
||||
"Building: Power Storage": ItemData(G.Building | G.AlwaysUseful, 1338631, C.progression),
|
||||
"Building: Power Storage": ItemData(G.Building | G.NeverExclude, 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),
|
||||
"Building: Power Switch": ItemData(G.Building | G.NeverExclude, 1338633, C.useful),
|
||||
"Building: Priority Power Switch": ItemData(G.Building | G.NeverExclude, 1338634, C.useful),
|
||||
"Building: Storage Container": ItemData(G.Building, 1338635, C.useful, 0),
|
||||
"Building: Lookout Tower": ItemData(G.Building, 1338636, C.filler),
|
||||
#"Building: Power Pole Mk.1": ItemData(G.Building, 1338637, C.progression), # unlocked by default
|
||||
"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.AlwaysUseful, 1338641, C.progression),
|
||||
"Building: Conveyor Splitter": ItemData(G.Building | G.AlwaysUseful, 1338642, C.progression),
|
||||
"Building: Power Pole Mk.2": ItemData(G.Building | G.NeverExclude, 1338638, C.useful),
|
||||
"Building: Power Pole Mk.3": ItemData(G.Building | G.NeverExclude, 1338639, C.useful),
|
||||
"Building: Industrial Storage Container": ItemData(G.Building | G.NeverExclude, 1338640, C.useful),
|
||||
"Building: Conveyor Merger": ItemData(G.Building | G.NeverExclude, 1338641, C.progression),
|
||||
"Building: Conveyor Splitter": ItemData(G.Building | G.NeverExclude, 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),
|
||||
@@ -546,11 +546,11 @@ class Items:
|
||||
"Building: Street Light": ItemData(G.Building | G.Lights, 1338670, C.filler, 0),
|
||||
"Building: Flood Light Tower": ItemData(G.Building | G.Lights, 1338671, C.filler, 0),
|
||||
"Building: Ceiling Light": ItemData(G.Building | G.Lights, 1338672, C.filler, 0),
|
||||
"Building: Power Tower": ItemData(G.Building, 1338673, C.useful),
|
||||
"Building: Power Tower": ItemData(G.Building | G.NeverExclude, 1338673, C.useful),
|
||||
"Building: Walls Orange": ItemData(G.Building | G.Walls, 1338674, C.progression),
|
||||
"Building: Radar Tower": ItemData(G.Building, 1338675, C.useful),
|
||||
"Building: Smart Splitter": ItemData(G.Building, 1338676, C.useful),
|
||||
"Building: Programmable Splitter": ItemData(G.Building, 1338677, C.useful),
|
||||
"Building: Smart Splitter": ItemData(G.Building | G.NeverExclude, 1338676, C.useful),
|
||||
"Building: Programmable Splitter": ItemData(G.Building | G.NeverExclude, 1338677, C.useful),
|
||||
"Building: Label Sign Bundle": ItemData(G.Building | G.Signs, 1338678, C.filler, 0),
|
||||
"Building: Display Sign Bundle": ItemData(G.Building | G.Signs, 1338679, C.filler, 0),
|
||||
"Building: Billboard Set": ItemData(G.Building | G.Signs, 1338680, C.filler, 0),
|
||||
@@ -559,7 +559,7 @@ class Items:
|
||||
"Building: Concrete Pillar": ItemData(G.Pilars, 1338683, C.filler, 0),
|
||||
"Building: Frame Pillar": ItemData(G.Pilars, 1338684, C.filler, 0),
|
||||
#1338685 - 1338691 Moved to cosmetics - 1.1
|
||||
"Building: Foundation": ItemData(G.Building | G.Foundations | G.AlwaysUseful, 1338692, C.progression),
|
||||
"Building: Foundation": ItemData(G.Building | G.Foundations | G.NeverExclude, 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),
|
||||
@@ -591,13 +591,13 @@ class Items:
|
||||
"Building: Roof Corners": ItemData(G.Walls, 1338721, C.filler, 0),
|
||||
"Building: Converter": ItemData(G.Building, 1338722, C.progression),
|
||||
"Building: Quantum Encoder": ItemData(G.Building, 1338723, C.progression),
|
||||
"Building: Portal": ItemData(G.Building, 1338724, C.filler),
|
||||
"Building: Portal": ItemData(G.Building, 1338724, C.useful),
|
||||
"Building: Conveyor Mk.6": ItemData(G.Building | G.ConveyorMk6, 1338725, C.progression),
|
||||
"Building: Conveyor Lift Mk.6": ItemData(G.Building | G.ConveyorMk6, 1338726, C.useful),
|
||||
"Building: Alien Power Augmenter": ItemData(G.Building, 1338727, C.progression),
|
||||
"Building: Dimensional Depot Uploader": ItemData(G.Building, 1338728, C.useful),
|
||||
# Added in 1.1
|
||||
"Building: Priority Merger": ItemData(G.Building, 1338729, C.useful),
|
||||
"Building: Priority Merger": ItemData(G.Building | G.NeverExclude, 1338729, C.useful),
|
||||
"Building: Conveyor Wall Hole": ItemData(G.Building, 1338730, C.useful),
|
||||
"Building: Conveyor Throughput Monitor": ItemData(G.Building, 1338731, C.useful),
|
||||
"Building: Basic Shelf Unit": ItemData(G.Building, 1338732, C.useful),
|
||||
@@ -873,7 +873,8 @@ class Items:
|
||||
|
||||
for name, data in cls.item_data.items():
|
||||
for category in data.category:
|
||||
groups.setdefault(category.name, set()).add(name)
|
||||
if category != G.NeverExclude:
|
||||
groups.setdefault(category.name, set()).add(name)
|
||||
|
||||
return groups
|
||||
|
||||
@@ -888,6 +889,7 @@ class Items:
|
||||
self.logic = logic
|
||||
self.random = random
|
||||
self.critical_path = critical_path
|
||||
self.options = options
|
||||
|
||||
|
||||
@classmethod
|
||||
@@ -895,18 +897,18 @@ class Items:
|
||||
data: ItemData = cls.item_data[name]
|
||||
type = data.type
|
||||
|
||||
if type == C.progression \
|
||||
if (type == C.progression or type == C.useful) \
|
||||
and instance and instance.critical_path.required_item_names \
|
||||
and (data.category & (G.Recipe | G.Building)) and not (data.category & G.AlwaysUseful) \
|
||||
and (data.category & (G.Recipe | G.Building)) and not (data.category & G.NeverExclude) \
|
||||
and name not in instance.critical_path.required_item_names:
|
||||
type = C.useful
|
||||
|
||||
return Item(name, type, data.code, player)
|
||||
|
||||
|
||||
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] = list(options.trap_selection_override.value)
|
||||
def get_filler_item_name(self, filler_items: tuple[str, ...], random: Random) -> str:
|
||||
trap_chance: int = self.options.trap_chance.value
|
||||
enabled_traps: list[str] = list(self.options.trap_selection_override.value)
|
||||
|
||||
if enabled_traps and random.random() < (trap_chance / 100):
|
||||
return random.choice(enabled_traps)
|
||||
@@ -914,7 +916,7 @@ class Items:
|
||||
return random.choice(filler_items)
|
||||
|
||||
|
||||
def get_excluded_items(self, multiworld: MultiWorld, options: SatisfactoryOptions) -> set[str]:
|
||||
def get_excluded_items(self, multiworld: MultiWorld) -> 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)
|
||||
@@ -923,16 +925,15 @@ class Items:
|
||||
for item in multiworld.precollected_items[self.player]:
|
||||
if item.name in self.item_data \
|
||||
and not (self.item_data[item.name].category & self.non_unique_item_categories) \
|
||||
and item.name not in options.start_inventory_from_pool:
|
||||
and item.name not in self.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) \
|
||||
def build_item_pool(self, random: Random, multiworld: MultiWorld, number_of_locations: int) -> list[Item]:
|
||||
excluded_from_pool: set[str] = self.get_excluded_items(multiworld) \
|
||||
.union(self.critical_path.implicitly_unlocked)
|
||||
pool: list[Item] = []
|
||||
|
||||
@@ -953,7 +954,7 @@ class Items:
|
||||
|
||||
filtered_filler_items = tuple(item for item in self.filler_items if item not in excluded_from_pool)
|
||||
pool += [
|
||||
self.create_item(self, self.get_filler_item_name(filtered_filler_items, random, options), self.player)
|
||||
self.create_item(self, self.get_filler_item_name(filtered_filler_items, random), self.player)
|
||||
for _ in range(filler_pool_size)
|
||||
]
|
||||
|
||||
|
||||
@@ -94,13 +94,15 @@ def create_regions_and_return_locations(world: MultiWorld, options: Satisfactory
|
||||
if options.final_elevator_package == 1:
|
||||
super_early_game_buildings.extend(early_game_buildings)
|
||||
|
||||
is_ut = getattr(world, "generation_is_fake", False)
|
||||
|
||||
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))
|
||||
lambda state: is_ut or state_logic.can_build_all(state, super_early_game_buildings))
|
||||
|
||||
if options.final_elevator_package >= 2:
|
||||
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))
|
||||
and (is_ut or state_logic.can_build_all(state, early_game_buildings)))
|
||||
connect(regions, "Hub Tier 3", "Hub Tier 4")
|
||||
if options.final_elevator_package >= 3:
|
||||
connect(regions, "Hub Tier 4", "Hub Tier 5", lambda state: state.has("Elevator Tier 2", player))
|
||||
|
||||
@@ -80,9 +80,12 @@ class StateLogic:
|
||||
and self.can_produce_all(state, recipe.inputs)
|
||||
|
||||
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)
|
||||
limited_phase = min(self.options.final_elevator_package - 1, phase)
|
||||
|
||||
if limited_phase != 0:
|
||||
return state.has(f"Elevator Tier {limited_phase}", self.player)
|
||||
else:
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def to_part_event(part: str) -> str:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import TextIO, ClassVar
|
||||
from typing import TextIO, ClassVar, Any
|
||||
from BaseClasses import Item, ItemClassification, CollectionState
|
||||
from .GameLogic import GameLogic
|
||||
from .Items import Items
|
||||
@@ -24,18 +24,27 @@ class SatisfactoryWorld(World):
|
||||
web = SatisfactoryWebWorld()
|
||||
origin_region_name = "Overworld"
|
||||
required_client_version = (0, 6, 0)
|
||||
ut_can_gen_without_yaml = True
|
||||
|
||||
game_logic: ClassVar[GameLogic] = GameLogic()
|
||||
state_logic: StateLogic
|
||||
items: Items
|
||||
critical_path: CriticalPathCalculator
|
||||
critical_path_seed: float | None = None
|
||||
|
||||
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)
|
||||
|
||||
def generate_early(self) -> None:
|
||||
self.critical_path = CriticalPathCalculator(self.game_logic, self.random, self.options)
|
||||
self.interpret_slot_data(None)
|
||||
|
||||
if self.critical_path_seed == None:
|
||||
self.critical_path_seed = self.random.random()
|
||||
|
||||
self.critical_path = CriticalPathCalculator(self.game_logic, self.critical_path_seed, self.options)
|
||||
self.critical_path.calculate()
|
||||
|
||||
self.state_logic = StateLogic(self.player, self.options, self.critical_path)
|
||||
self.items = Items(self.player, self.game_logic, self.random, self.options, self.critical_path)
|
||||
|
||||
@@ -71,7 +80,7 @@ class SatisfactoryWorld(World):
|
||||
|
||||
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)
|
||||
self.items.build_item_pool(self.random, self.multiworld, number_of_locations)
|
||||
|
||||
|
||||
def set_rules(self) -> None:
|
||||
@@ -120,7 +129,6 @@ class SatisfactoryWorld(World):
|
||||
return {
|
||||
"Data": {
|
||||
"HubLayout": slot_hub_layout,
|
||||
"SlotsPerMilestone": self.game_logic.slots_per_milestone,
|
||||
"ExplorationCosts": {
|
||||
self.item_id_str("Mercer Sphere"): int(self.options.goal_exploration_collectables_amount.value * 2),
|
||||
self.item_id_str("Somersloop"): self.options.goal_exploration_collectables_amount.value,
|
||||
@@ -135,7 +143,6 @@ class SatisfactoryWorld(World):
|
||||
"FinalElevatorTier": self.options.final_elevator_package.value,
|
||||
"FinalResourceSinkPointsTotal": self.options.goal_awesome_sink_points_total.value,
|
||||
"FinalResourceSinkPointsPerMinute": self.options.goal_awesome_sink_points_per_minute.value,
|
||||
"FinalExplorationCollectionAmount": self.options.goal_exploration_collectables_amount.value,
|
||||
"FreeSampleEquipment": self.options.free_sample_equipment.value,
|
||||
"FreeSampleBuildings": self.options.free_sample_buildings.value,
|
||||
"FreeSampleParts": self.options.free_sample_parts.value,
|
||||
@@ -143,11 +150,52 @@ class SatisfactoryWorld(World):
|
||||
"EnergyLink": bool(self.options.energy_link),
|
||||
"StartingRecipies": starting_recipes
|
||||
},
|
||||
"SlotDataVersion": 1
|
||||
"SlotDataVersion": 1,
|
||||
"UT": {
|
||||
"Seed": self.critical_path_seed,
|
||||
"RandomizeTier0": bool(self.options.randomize_starter_recipes)
|
||||
}
|
||||
},
|
||||
"DeathLink": bool(self.options.death_link)
|
||||
}
|
||||
|
||||
def interpret_slot_data(self, slot_data: dict[str, Any] | None) -> dict[str, Any] | None:
|
||||
"""Used by Universal Tracker to correctly rebuild state"""
|
||||
|
||||
if not slot_data \
|
||||
and hasattr(self.multiworld, "re_gen_passthrough") \
|
||||
and isinstance(self.multiworld.re_gen_passthrough, dict) \
|
||||
and "Satisfactory" in self.multiworld.re_gen_passthrough:
|
||||
slot_data = self.multiworld.re_gen_passthrough["Satisfactory"]
|
||||
|
||||
if not slot_data:
|
||||
return None
|
||||
|
||||
if (slot_data["Data"]["SlotDataVersion"] != 1):
|
||||
raise Exception("The slot_data version mismatch, the UT's Satisfactory .apworld is different from the one used during generation")
|
||||
|
||||
self.options.goal_selection.value = slot_data["Data"]["Options"]["GoalSelection"]
|
||||
self.options.goal_requirement.value = slot_data["Data"]["Options"]["GoalRequirement"]
|
||||
self.options.final_elevator_package.value = slot_data["Data"]["Options"]["FinalElevatorTier"]
|
||||
self.options.goal_awesome_sink_points_total.value = slot_data["Data"]["Options"]["FinalResourceSinkPointsTotal"]
|
||||
self.options.goal_awesome_sink_points_per_minute.value = \
|
||||
slot_data["Data"]["Options"]["FinalResourceSinkPointsPerMinute"]
|
||||
self.options.free_sample_equipment.value = slot_data["Data"]["Options"]["FreeSampleEquipment"]
|
||||
self.options.free_sample_buildings.value = slot_data["Data"]["Options"]["FreeSampleBuildings"]
|
||||
self.options.free_sample_parts.value = slot_data["Data"]["Options"]["FreeSampleParts"]
|
||||
self.options.free_sample_radioactive.value = int(slot_data["Data"]["Options"]["FreeSampleRadioactive"])
|
||||
self.options.energy_link.value = int(slot_data["Data"]["Options"]["EnergyLink"])
|
||||
|
||||
self.options.milestone_cost_multiplier.value = 100 * \
|
||||
(slot_data["Data"]["HubLayout"][0][0][self.item_id_str("Concrete")]
|
||||
/ self.game_logic.hub_layout[0][0]["Concrete"])
|
||||
self.options.goal_exploration_collectables_amount.value = \
|
||||
slot_data["Data"]["ExplorationCosts"][self.item_id_str("Somersloop")]
|
||||
|
||||
self.critical_path_seed = slot_data["Data"]["UT"]["Seed"]
|
||||
self.options.randomize_starter_recipes.value = slot_data["Data"]["UT"]["RandomizeTier0"]
|
||||
|
||||
return slot_data
|
||||
|
||||
def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
|
||||
if self.options.randomize_starter_recipes:
|
||||
@@ -155,7 +203,7 @@ class SatisfactoryWorld(World):
|
||||
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.items.get_filler_item_name(self.items.filler_items, self.random, self.options)
|
||||
return self.items.get_filler_item_name(self.items.filler_items, self.random)
|
||||
|
||||
|
||||
def setup_events(self) -> None:
|
||||
@@ -178,6 +226,7 @@ class SatisfactoryWorld(World):
|
||||
item = self.create_item(item_name)
|
||||
self.multiworld.push_precollected(item)
|
||||
|
||||
|
||||
def item_id_str(self, item_name: str) -> str:
|
||||
# ItemIDs of bundles are shared with their component item
|
||||
bundled_name = f"Bundle: {item_name}"
|
||||
|
||||
Reference in New Issue
Block a user