diff --git a/worlds/satisfactory/CriticalPathCalculator.py b/worlds/satisfactory/CriticalPathCalculator.py index dac04c46c1..1f165487e4 100644 --- a/worlds/satisfactory/CriticalPathCalculator.py +++ b/worlds/satisfactory/CriticalPathCalculator.py @@ -37,21 +37,8 @@ class CriticalPathCalculator: selected_power_infrastructure: dict[int, Recipe] = {} - self.implicitly_unlocked: set[str] = { - recipe.name - for recipes_per_part in logic.recipes.values() - for recipe in recipes_per_part if recipe.implicitly_unlocked - } - self.implicitly_unlocked.update({ - building.name - for building in logic.buildings.values() if building.implicitly_unlocked - }) - self.handcraftable_parts: dict[str, list[Recipe]] = {} - for part, recipes_per_part in logic.recipes.items(): - for recipe in recipes_per_part: - if recipe.handcraftable: - self.handcraftable_parts.setdefault(part, list()).append(recipe) + self.configure_implicitly_unlocked_and_handcraftable_parts() self.select_minimal_required_parts_for(self.logic.space_elevator_tiers[options.final_elevator_package-1].keys()) @@ -100,6 +87,44 @@ class CriticalPathCalculator: } self.required_item_names.update({"Building: "+ building for building in self.required_buildings}) + self.calculate_excluded_things() + + + 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) + + + 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.required_parts: + continue + + self.required_parts.add(part) + + for recipe in self.logic.recipes[part]: + if recipe.minimal_tier > self.options.final_elevator_package: + continue + + 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.building: + self.select_minimal_required_parts_for(self.logic.buildings[recipe.building].inputs) + self.required_buildings.add(recipe.building) + + if self.logic.buildings[recipe.building].power_requirement: + self.required_power_level = \ + max(self.required_power_level, + self.logic.buildings[recipe.building].power_requirement) + + + def calculate_excluded_things(self) -> None: self.parts_to_exclude = set() self.buildings_to_exclude = set() self.recipes_to_exclude = { @@ -148,35 +173,24 @@ class CriticalPathCalculator: excluded_count = new_length - 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) + def configure_implicitly_unlocked_and_handcraftable_parts(self) -> None: + self.implicitly_unlocked: set[str] = { + recipe.name + for recipes_per_part in self.logic.recipes.values() + for recipe in recipes_per_part if recipe.implicitly_unlocked + } + self.implicitly_unlocked.update({ + building.name + for building in self.logic.buildings.values() if building.implicitly_unlocked + }) + self.handcraftable_parts: dict[str, list[Recipe]] = {} + for part, recipes_per_part in self.logic.recipes.items(): + for recipe in recipes_per_part: + if recipe.handcraftable: + self.handcraftable_parts.setdefault(part, []).append(recipe) - 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.required_parts: - continue - - self.required_parts.add(part) - - for recipe in self.logic.recipes[part]: - if recipe.minimal_tier > self.options.final_elevator_package: - continue - - 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.building: - self.select_minimal_required_parts_for(self.logic.buildings[recipe.building].inputs) - self.required_buildings.add(recipe.building) - - if self.logic.buildings[recipe.building].power_requirement: - self.required_power_level = \ - max(self.required_power_level, - self.logic.buildings[recipe.building].power_requirement) \ No newline at end of file + if self.options.randomize_tier_0: + pass + #decide what to re-randomize, like smelter / iron ingot etc + #self.implicitly_unlocked.remove("") \ No newline at end of file diff --git a/worlds/satisfactory/Items.py b/worlds/satisfactory/Items.py index f14723f4a0..48a3d3b5a2 100644 --- a/worlds/satisfactory/Items.py +++ b/worlds/satisfactory/Items.py @@ -864,14 +864,18 @@ class Items: @classmethod - def get_item_names_per_category(cls) -> dict[str, set[str]]: - categories: dict[str, set[str]] = {} + def get_item_names_per_category(cls, game_logic: GameLogic) -> dict[str, set[str]]: + groups: dict[str, set[str]] = {} + + # To allow hinting for first part recipe in logic + for part, recipes in game_logic.recipes.items(): + groups[part] = {recipe.name for recipe in recipes} for name, data in cls.item_data.items(): for category in data.category: - categories.setdefault(category.name, set()).add(name) + groups.setdefault(category.name, set()).add(name) - return categories + return groups player: int logic: GameLogic @@ -902,7 +906,7 @@ class Items: 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) + enabled_traps: list[str] = list(options.trap_selection_override.value) if enabled_traps and random.random() < (trap_chance / 100): return random.choice(enabled_traps) diff --git a/worlds/satisfactory/__init__.py b/worlds/satisfactory/__init__.py index 6102d2e900..bf9a7e5ed0 100644 --- a/worlds/satisfactory/__init__.py +++ b/worlds/satisfactory/__init__.py @@ -24,15 +24,15 @@ class SatisfactoryWorld(World): 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() - item_name_groups = Items.get_item_names_per_category() - game_logic: ClassVar[GameLogic] = GameLogic() state_logic: StateLogic items: Items critical_path: CriticalPathCalculator + 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.state_logic = StateLogic(self.player, self.options, self.critical_path)