Factorio: update API use (#3760)
--------- Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
This commit is contained in:
parent
debb936618
commit
6fac83b84c
|
@ -1,5 +1,6 @@
|
||||||
"""Outputs a Factorio Mod to facilitate integration with Archipelago"""
|
"""Outputs a Factorio Mod to facilitate integration with Archipelago"""
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -88,6 +89,8 @@ class FactorioModFile(worlds.Files.APContainer):
|
||||||
def generate_mod(world: "Factorio", output_directory: str):
|
def generate_mod(world: "Factorio", output_directory: str):
|
||||||
player = world.player
|
player = world.player
|
||||||
multiworld = world.multiworld
|
multiworld = world.multiworld
|
||||||
|
random = world.random
|
||||||
|
|
||||||
global data_final_template, locale_template, control_template, data_template, settings_template
|
global data_final_template, locale_template, control_template, data_template, settings_template
|
||||||
with template_load_lock:
|
with template_load_lock:
|
||||||
if not data_final_template:
|
if not data_final_template:
|
||||||
|
@ -110,8 +113,6 @@ def generate_mod(world: "Factorio", output_directory: str):
|
||||||
mod_name = f"AP-{multiworld.seed_name}-P{player}-{multiworld.get_file_safe_player_name(player)}"
|
mod_name = f"AP-{multiworld.seed_name}-P{player}-{multiworld.get_file_safe_player_name(player)}"
|
||||||
versioned_mod_name = mod_name + "_" + Utils.__version__
|
versioned_mod_name = mod_name + "_" + Utils.__version__
|
||||||
|
|
||||||
random = multiworld.per_slot_randoms[player]
|
|
||||||
|
|
||||||
def flop_random(low, high, base=None):
|
def flop_random(low, high, base=None):
|
||||||
"""Guarantees 50% below base and 50% above base, uniform distribution in each direction."""
|
"""Guarantees 50% below base and 50% above base, uniform distribution in each direction."""
|
||||||
if base:
|
if base:
|
||||||
|
@ -129,43 +130,43 @@ def generate_mod(world: "Factorio", output_directory: str):
|
||||||
"base_tech_table": base_tech_table,
|
"base_tech_table": base_tech_table,
|
||||||
"tech_to_progressive_lookup": tech_to_progressive_lookup,
|
"tech_to_progressive_lookup": tech_to_progressive_lookup,
|
||||||
"mod_name": mod_name,
|
"mod_name": mod_name,
|
||||||
"allowed_science_packs": multiworld.max_science_pack[player].get_allowed_packs(),
|
"allowed_science_packs": world.options.max_science_pack.get_allowed_packs(),
|
||||||
"custom_technologies": multiworld.worlds[player].custom_technologies,
|
"custom_technologies": world.custom_technologies,
|
||||||
"tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites,
|
"tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites,
|
||||||
"slot_name": multiworld.player_name[player], "seed_name": multiworld.seed_name,
|
"slot_name": world.player_name, "seed_name": multiworld.seed_name,
|
||||||
"slot_player": player,
|
"slot_player": player,
|
||||||
"starting_items": multiworld.starting_items[player], "recipes": recipes,
|
"starting_items": world.options.starting_items, "recipes": recipes,
|
||||||
"random": random, "flop_random": flop_random,
|
"random": random, "flop_random": flop_random,
|
||||||
"recipe_time_scale": recipe_time_scales.get(multiworld.recipe_time[player].value, None),
|
"recipe_time_scale": recipe_time_scales.get(world.options.recipe_time.value, None),
|
||||||
"recipe_time_range": recipe_time_ranges.get(multiworld.recipe_time[player].value, None),
|
"recipe_time_range": recipe_time_ranges.get(world.options.recipe_time.value, None),
|
||||||
"free_sample_blacklist": {item: 1 for item in free_sample_exclusions},
|
"free_sample_blacklist": {item: 1 for item in free_sample_exclusions},
|
||||||
"progressive_technology_table": {tech.name: tech.progressive for tech in
|
"progressive_technology_table": {tech.name: tech.progressive for tech in
|
||||||
progressive_technology_table.values()},
|
progressive_technology_table.values()},
|
||||||
"custom_recipes": world.custom_recipes,
|
"custom_recipes": world.custom_recipes,
|
||||||
"max_science_pack": multiworld.max_science_pack[player].value,
|
"max_science_pack": world.options.max_science_pack.value,
|
||||||
"liquids": fluids,
|
"liquids": fluids,
|
||||||
"goal": multiworld.goal[player].value,
|
"goal": world.options.goal.value,
|
||||||
"energy_link": multiworld.energy_link[player].value,
|
"energy_link": world.options.energy_link.value,
|
||||||
"useless_technologies": useless_technologies,
|
"useless_technologies": useless_technologies,
|
||||||
"chunk_shuffle": multiworld.chunk_shuffle[player].value if hasattr(multiworld, "chunk_shuffle") else 0,
|
"chunk_shuffle": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
for factorio_option in Options.factorio_options:
|
for factorio_option, factorio_option_instance in dataclasses.asdict(world.options).items():
|
||||||
if factorio_option in ["free_sample_blacklist", "free_sample_whitelist"]:
|
if factorio_option in ["free_sample_blacklist", "free_sample_whitelist"]:
|
||||||
continue
|
continue
|
||||||
template_data[factorio_option] = getattr(multiworld, factorio_option)[player].value
|
template_data[factorio_option] = factorio_option_instance.value
|
||||||
|
|
||||||
if getattr(multiworld, "silo")[player].value == Options.Silo.option_randomize_recipe:
|
if world.options.silo == Options.Silo.option_randomize_recipe:
|
||||||
template_data["free_sample_blacklist"]["rocket-silo"] = 1
|
template_data["free_sample_blacklist"]["rocket-silo"] = 1
|
||||||
|
|
||||||
if getattr(multiworld, "satellite")[player].value == Options.Satellite.option_randomize_recipe:
|
if world.options.satellite == Options.Satellite.option_randomize_recipe:
|
||||||
template_data["free_sample_blacklist"]["satellite"] = 1
|
template_data["free_sample_blacklist"]["satellite"] = 1
|
||||||
|
|
||||||
template_data["free_sample_blacklist"].update({item: 1 for item in multiworld.free_sample_blacklist[player].value})
|
template_data["free_sample_blacklist"].update({item: 1 for item in world.options.free_sample_blacklist.value})
|
||||||
template_data["free_sample_blacklist"].update({item: 0 for item in multiworld.free_sample_whitelist[player].value})
|
template_data["free_sample_blacklist"].update({item: 0 for item in world.options.free_sample_whitelist.value})
|
||||||
|
|
||||||
zf_path = os.path.join(output_directory, versioned_mod_name + ".zip")
|
zf_path = os.path.join(output_directory, versioned_mod_name + ".zip")
|
||||||
mod = FactorioModFile(zf_path, player=player, player_name=multiworld.player_name[player])
|
mod = FactorioModFile(zf_path, player=player, player_name=world.player_name)
|
||||||
|
|
||||||
if world.zip_path:
|
if world.zip_path:
|
||||||
with zipfile.ZipFile(world.zip_path) as zf:
|
with zipfile.ZipFile(world.zip_path) as zf:
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import typing
|
|
||||||
|
from dataclasses import dataclass
|
||||||
import datetime
|
import datetime
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from schema import Schema, Optional, And, Or
|
||||||
|
|
||||||
from Options import Choice, OptionDict, OptionSet, Option, DefaultOnToggle, Range, DeathLink, Toggle, \
|
from Options import Choice, OptionDict, OptionSet, Option, DefaultOnToggle, Range, DeathLink, Toggle, \
|
||||||
StartInventoryPool
|
StartInventoryPool, PerGameCommonOptions
|
||||||
from schema import Schema, Optional, And, Or
|
|
||||||
|
|
||||||
# schema helpers
|
# schema helpers
|
||||||
FloatRange = lambda low, high: And(Or(int, float), lambda f: low <= f <= high)
|
FloatRange = lambda low, high: And(Or(int, float), lambda f: low <= f <= high)
|
||||||
|
@ -422,50 +425,37 @@ class EnergyLink(Toggle):
|
||||||
display_name = "EnergyLink"
|
display_name = "EnergyLink"
|
||||||
|
|
||||||
|
|
||||||
factorio_options: typing.Dict[str, type(Option)] = {
|
@dataclass
|
||||||
"max_science_pack": MaxSciencePack,
|
class FactorioOptions(PerGameCommonOptions):
|
||||||
"goal": Goal,
|
max_science_pack: MaxSciencePack
|
||||||
"tech_tree_layout": TechTreeLayout,
|
goal: Goal
|
||||||
"min_tech_cost": MinTechCost,
|
tech_tree_layout: TechTreeLayout
|
||||||
"max_tech_cost": MaxTechCost,
|
min_tech_cost: MinTechCost
|
||||||
"tech_cost_distribution": TechCostDistribution,
|
max_tech_cost: MaxTechCost
|
||||||
"tech_cost_mix": TechCostMix,
|
tech_cost_distribution: TechCostDistribution
|
||||||
"ramping_tech_costs": RampingTechCosts,
|
tech_cost_mix: TechCostMix
|
||||||
"silo": Silo,
|
ramping_tech_costs: RampingTechCosts
|
||||||
"satellite": Satellite,
|
silo: Silo
|
||||||
"free_samples": FreeSamples,
|
satellite: Satellite
|
||||||
"tech_tree_information": TechTreeInformation,
|
free_samples: FreeSamples
|
||||||
"starting_items": FactorioStartItems,
|
tech_tree_information: TechTreeInformation
|
||||||
"free_sample_blacklist": FactorioFreeSampleBlacklist,
|
starting_items: FactorioStartItems
|
||||||
"free_sample_whitelist": FactorioFreeSampleWhitelist,
|
free_sample_blacklist: FactorioFreeSampleBlacklist
|
||||||
"recipe_time": RecipeTime,
|
free_sample_whitelist: FactorioFreeSampleWhitelist
|
||||||
"recipe_ingredients": RecipeIngredients,
|
recipe_time: RecipeTime
|
||||||
"recipe_ingredients_offset": RecipeIngredientsOffset,
|
recipe_ingredients: RecipeIngredients
|
||||||
"imported_blueprints": ImportedBlueprint,
|
recipe_ingredients_offset: RecipeIngredientsOffset
|
||||||
"world_gen": FactorioWorldGen,
|
imported_blueprints: ImportedBlueprint
|
||||||
"progressive": Progressive,
|
world_gen: FactorioWorldGen
|
||||||
"teleport_traps": TeleportTrapCount,
|
progressive: Progressive
|
||||||
"grenade_traps": GrenadeTrapCount,
|
teleport_traps: TeleportTrapCount
|
||||||
"cluster_grenade_traps": ClusterGrenadeTrapCount,
|
grenade_traps: GrenadeTrapCount
|
||||||
"artillery_traps": ArtilleryTrapCount,
|
cluster_grenade_traps: ClusterGrenadeTrapCount
|
||||||
"atomic_rocket_traps": AtomicRocketTrapCount,
|
artillery_traps: ArtilleryTrapCount
|
||||||
"attack_traps": AttackTrapCount,
|
atomic_rocket_traps: AtomicRocketTrapCount
|
||||||
"evolution_traps": EvolutionTrapCount,
|
attack_traps: AttackTrapCount
|
||||||
"evolution_trap_increase": EvolutionTrapIncrease,
|
evolution_traps: EvolutionTrapCount
|
||||||
"death_link": DeathLink,
|
evolution_trap_increase: EvolutionTrapIncrease
|
||||||
"energy_link": EnergyLink,
|
death_link: DeathLink
|
||||||
"start_inventory_from_pool": StartInventoryPool,
|
energy_link: EnergyLink
|
||||||
}
|
start_inventory_from_pool: StartInventoryPool
|
||||||
|
|
||||||
# spoilers below. If you spoil it for yourself, please at least don't spoil it for anyone else.
|
|
||||||
if datetime.datetime.today().month == 4:
|
|
||||||
|
|
||||||
class ChunkShuffle(Toggle):
|
|
||||||
"""Entrance Randomizer."""
|
|
||||||
display_name = "Chunk Shuffle"
|
|
||||||
|
|
||||||
|
|
||||||
if datetime.datetime.today().day > 1:
|
|
||||||
ChunkShuffle.__doc__ += """
|
|
||||||
2023 April Fool's option. Shuffles chunk border transitions."""
|
|
||||||
factorio_options["chunk_shuffle"] = ChunkShuffle
|
|
||||||
|
|
|
@ -19,12 +19,10 @@ def _sorter(location: "FactorioScienceLocation"):
|
||||||
return location.complexity, location.rel_cost
|
return location.complexity, location.rel_cost
|
||||||
|
|
||||||
|
|
||||||
def get_shapes(factorio_world: "Factorio") -> Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]]:
|
def get_shapes(world: "Factorio") -> Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]]:
|
||||||
world = factorio_world.multiworld
|
|
||||||
player = factorio_world.player
|
|
||||||
prerequisites: Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]] = {}
|
prerequisites: Dict["FactorioScienceLocation", Set["FactorioScienceLocation"]] = {}
|
||||||
layout = world.tech_tree_layout[player].value
|
layout = world.options.tech_tree_layout.value
|
||||||
locations: List["FactorioScienceLocation"] = sorted(factorio_world.science_locations, key=lambda loc: loc.name)
|
locations: List["FactorioScienceLocation"] = sorted(world.science_locations, key=lambda loc: loc.name)
|
||||||
world.random.shuffle(locations)
|
world.random.shuffle(locations)
|
||||||
|
|
||||||
if layout == TechTreeLayout.option_single:
|
if layout == TechTreeLayout.option_single:
|
||||||
|
@ -247,5 +245,5 @@ def get_shapes(factorio_world: "Factorio") -> Dict["FactorioScienceLocation", Se
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"Layout {layout} is not implemented.")
|
raise NotImplementedError(f"Layout {layout} is not implemented.")
|
||||||
|
|
||||||
factorio_world.tech_tree_layout_prerequisites = prerequisites
|
world.tech_tree_layout_prerequisites = prerequisites
|
||||||
return prerequisites
|
return prerequisites
|
||||||
|
|
|
@ -13,12 +13,11 @@ import Utils
|
||||||
from . import Options
|
from . import Options
|
||||||
|
|
||||||
factorio_tech_id = factorio_base_id = 2 ** 17
|
factorio_tech_id = factorio_base_id = 2 ** 17
|
||||||
# Factorio technologies are imported from a .json document in /data
|
|
||||||
source_folder = os.path.join(os.path.dirname(__file__), "data")
|
|
||||||
|
|
||||||
pool = ThreadPoolExecutor(1)
|
pool = ThreadPoolExecutor(1)
|
||||||
|
|
||||||
|
|
||||||
|
# Factorio technologies are imported from a .json document in /data
|
||||||
def load_json_data(data_name: str) -> Union[List[str], Dict[str, Any]]:
|
def load_json_data(data_name: str) -> Union[List[str], Dict[str, Any]]:
|
||||||
return orjson.loads(pkgutil.get_data(__name__, "data/" + data_name + ".json"))
|
return orjson.loads(pkgutil.get_data(__name__, "data/" + data_name + ".json"))
|
||||||
|
|
||||||
|
@ -99,7 +98,7 @@ class CustomTechnology(Technology):
|
||||||
and ((ingredients & {"chemical-science-pack", "production-science-pack", "utility-science-pack"})
|
and ((ingredients & {"chemical-science-pack", "production-science-pack", "utility-science-pack"})
|
||||||
or origin.name == "rocket-silo")
|
or origin.name == "rocket-silo")
|
||||||
self.player = player
|
self.player = player
|
||||||
if origin.name not in world.worlds[player].special_nodes:
|
if origin.name not in world.special_nodes:
|
||||||
if military_allowed:
|
if military_allowed:
|
||||||
ingredients.add("military-science-pack")
|
ingredients.add("military-science-pack")
|
||||||
ingredients = list(ingredients)
|
ingredients = list(ingredients)
|
||||||
|
|
|
@ -11,7 +11,7 @@ from worlds.LauncherComponents import Component, components, Type, launch_subpro
|
||||||
from worlds.generic import Rules
|
from worlds.generic import Rules
|
||||||
from .Locations import location_pools, location_table
|
from .Locations import location_pools, location_table
|
||||||
from .Mod import generate_mod
|
from .Mod import generate_mod
|
||||||
from .Options import factorio_options, MaxSciencePack, Silo, Satellite, TechTreeInformation, Goal, TechCostDistribution
|
from .Options import FactorioOptions, MaxSciencePack, Silo, Satellite, TechTreeInformation, Goal, TechCostDistribution
|
||||||
from .Shapes import get_shapes
|
from .Shapes import get_shapes
|
||||||
from .Technologies import base_tech_table, recipe_sources, base_technology_table, \
|
from .Technologies import base_tech_table, recipe_sources, base_technology_table, \
|
||||||
all_ingredient_names, all_product_sources, required_technologies, get_rocket_requirements, \
|
all_ingredient_names, all_product_sources, required_technologies, get_rocket_requirements, \
|
||||||
|
@ -89,13 +89,15 @@ class Factorio(World):
|
||||||
advancement_technologies: typing.Set[str]
|
advancement_technologies: typing.Set[str]
|
||||||
|
|
||||||
web = FactorioWeb()
|
web = FactorioWeb()
|
||||||
|
options_dataclass = FactorioOptions
|
||||||
|
options: FactorioOptions
|
||||||
|
|
||||||
item_name_to_id = all_items
|
item_name_to_id = all_items
|
||||||
location_name_to_id = location_table
|
location_name_to_id = location_table
|
||||||
item_name_groups = {
|
item_name_groups = {
|
||||||
"Progressive": set(progressive_tech_table.keys()),
|
"Progressive": set(progressive_tech_table.keys()),
|
||||||
}
|
}
|
||||||
required_client_version = (0, 4, 2)
|
required_client_version = (0, 5, 0)
|
||||||
|
|
||||||
ordered_science_packs: typing.List[str] = MaxSciencePack.get_ordered_science_packs()
|
ordered_science_packs: typing.List[str] = MaxSciencePack.get_ordered_science_packs()
|
||||||
tech_tree_layout_prerequisites: typing.Dict[FactorioScienceLocation, typing.Set[FactorioScienceLocation]]
|
tech_tree_layout_prerequisites: typing.Dict[FactorioScienceLocation, typing.Set[FactorioScienceLocation]]
|
||||||
|
@ -117,32 +119,32 @@ class Factorio(World):
|
||||||
|
|
||||||
def generate_early(self) -> None:
|
def generate_early(self) -> None:
|
||||||
# if max < min, then swap max and min
|
# if max < min, then swap max and min
|
||||||
if self.multiworld.max_tech_cost[self.player] < self.multiworld.min_tech_cost[self.player]:
|
if self.options.max_tech_cost < self.options.min_tech_cost:
|
||||||
self.multiworld.min_tech_cost[self.player].value, self.multiworld.max_tech_cost[self.player].value = \
|
self.options.min_tech_cost.value, self.options.max_tech_cost.value = \
|
||||||
self.multiworld.max_tech_cost[self.player].value, self.multiworld.min_tech_cost[self.player].value
|
self.options.max_tech_cost.value, self.options.min_tech_cost.value
|
||||||
self.tech_mix = self.multiworld.tech_cost_mix[self.player]
|
self.tech_mix = self.options.tech_cost_mix.value
|
||||||
self.skip_silo = self.multiworld.silo[self.player].value == Silo.option_spawn
|
self.skip_silo = self.options.silo.value == Silo.option_spawn
|
||||||
|
|
||||||
def create_regions(self):
|
def create_regions(self):
|
||||||
player = self.player
|
player = self.player
|
||||||
random = self.multiworld.random
|
random = self.random
|
||||||
nauvis = Region("Nauvis", player, self.multiworld)
|
nauvis = Region("Nauvis", player, self.multiworld)
|
||||||
|
|
||||||
location_count = len(base_tech_table) - len(useless_technologies) - self.skip_silo + \
|
location_count = len(base_tech_table) - len(useless_technologies) - self.skip_silo + \
|
||||||
self.multiworld.evolution_traps[player] + \
|
self.options.evolution_traps + \
|
||||||
self.multiworld.attack_traps[player] + \
|
self.options.attack_traps + \
|
||||||
self.multiworld.teleport_traps[player] + \
|
self.options.teleport_traps + \
|
||||||
self.multiworld.grenade_traps[player] + \
|
self.options.grenade_traps + \
|
||||||
self.multiworld.cluster_grenade_traps[player] + \
|
self.options.cluster_grenade_traps + \
|
||||||
self.multiworld.atomic_rocket_traps[player] + \
|
self.options.atomic_rocket_traps + \
|
||||||
self.multiworld.artillery_traps[player]
|
self.options.artillery_traps
|
||||||
|
|
||||||
location_pool = []
|
location_pool = []
|
||||||
|
|
||||||
for pack in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
|
for pack in sorted(self.options.max_science_pack.get_allowed_packs()):
|
||||||
location_pool.extend(location_pools[pack])
|
location_pool.extend(location_pools[pack])
|
||||||
try:
|
try:
|
||||||
location_names = self.multiworld.random.sample(location_pool, location_count)
|
location_names = random.sample(location_pool, location_count)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
# should be "ValueError: Sample larger than population or is negative"
|
# should be "ValueError: Sample larger than population or is negative"
|
||||||
raise Exception("Too many traps for too few locations. Either decrease the trap count, "
|
raise Exception("Too many traps for too few locations. Either decrease the trap count, "
|
||||||
|
@ -150,9 +152,9 @@ class Factorio(World):
|
||||||
|
|
||||||
self.science_locations = [FactorioScienceLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis)
|
self.science_locations = [FactorioScienceLocation(player, loc_name, self.location_name_to_id[loc_name], nauvis)
|
||||||
for loc_name in location_names]
|
for loc_name in location_names]
|
||||||
distribution: TechCostDistribution = self.multiworld.tech_cost_distribution[self.player]
|
distribution: TechCostDistribution = self.options.tech_cost_distribution
|
||||||
min_cost = self.multiworld.min_tech_cost[self.player]
|
min_cost = self.options.min_tech_cost.value
|
||||||
max_cost = self.multiworld.max_tech_cost[self.player]
|
max_cost = self.options.max_tech_cost.value
|
||||||
if distribution == distribution.option_even:
|
if distribution == distribution.option_even:
|
||||||
rand_values = (random.randint(min_cost, max_cost) for _ in self.science_locations)
|
rand_values = (random.randint(min_cost, max_cost) for _ in self.science_locations)
|
||||||
else:
|
else:
|
||||||
|
@ -161,7 +163,7 @@ class Factorio(World):
|
||||||
distribution.option_high: max_cost}[distribution.value]
|
distribution.option_high: max_cost}[distribution.value]
|
||||||
rand_values = (random.triangular(min_cost, max_cost, mode) for _ in self.science_locations)
|
rand_values = (random.triangular(min_cost, max_cost, mode) for _ in self.science_locations)
|
||||||
rand_values = sorted(rand_values)
|
rand_values = sorted(rand_values)
|
||||||
if self.multiworld.ramping_tech_costs[self.player]:
|
if self.options.ramping_tech_costs:
|
||||||
def sorter(loc: FactorioScienceLocation):
|
def sorter(loc: FactorioScienceLocation):
|
||||||
return loc.complexity, loc.rel_cost
|
return loc.complexity, loc.rel_cost
|
||||||
else:
|
else:
|
||||||
|
@ -176,7 +178,7 @@ class Factorio(World):
|
||||||
event = FactorioItem("Victory", ItemClassification.progression, None, player)
|
event = FactorioItem("Victory", ItemClassification.progression, None, player)
|
||||||
location.place_locked_item(event)
|
location.place_locked_item(event)
|
||||||
|
|
||||||
for ingredient in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
|
for ingredient in sorted(self.options.max_science_pack.get_allowed_packs()):
|
||||||
location = FactorioLocation(player, f"Automate {ingredient}", None, nauvis)
|
location = FactorioLocation(player, f"Automate {ingredient}", None, nauvis)
|
||||||
nauvis.locations.append(location)
|
nauvis.locations.append(location)
|
||||||
event = FactorioItem(f"Automated {ingredient}", ItemClassification.progression, None, player)
|
event = FactorioItem(f"Automated {ingredient}", ItemClassification.progression, None, player)
|
||||||
|
@ -185,24 +187,23 @@ class Factorio(World):
|
||||||
self.multiworld.regions.append(nauvis)
|
self.multiworld.regions.append(nauvis)
|
||||||
|
|
||||||
def create_items(self) -> None:
|
def create_items(self) -> None:
|
||||||
player = self.player
|
|
||||||
self.custom_technologies = self.set_custom_technologies()
|
self.custom_technologies = self.set_custom_technologies()
|
||||||
self.set_custom_recipes()
|
self.set_custom_recipes()
|
||||||
traps = ("Evolution", "Attack", "Teleport", "Grenade", "Cluster Grenade", "Artillery", "Atomic Rocket")
|
traps = ("Evolution", "Attack", "Teleport", "Grenade", "Cluster Grenade", "Artillery", "Atomic Rocket")
|
||||||
for trap_name in traps:
|
for trap_name in traps:
|
||||||
self.multiworld.itempool.extend(self.create_item(f"{trap_name} Trap") for _ in
|
self.multiworld.itempool.extend(self.create_item(f"{trap_name} Trap") for _ in
|
||||||
range(getattr(self.multiworld,
|
range(getattr(self.options,
|
||||||
f"{trap_name.lower().replace(' ', '_')}_traps")[player]))
|
f"{trap_name.lower().replace(' ', '_')}_traps")))
|
||||||
|
|
||||||
want_progressives = collections.defaultdict(lambda: self.multiworld.progressive[player].
|
want_progressives = collections.defaultdict(lambda: self.options.progressive.
|
||||||
want_progressives(self.multiworld.random))
|
want_progressives(self.random))
|
||||||
|
|
||||||
cost_sorted_locations = sorted(self.science_locations, key=lambda location: location.name)
|
cost_sorted_locations = sorted(self.science_locations, key=lambda location: location.name)
|
||||||
special_index = {"automation": 0,
|
special_index = {"automation": 0,
|
||||||
"logistics": 1,
|
"logistics": 1,
|
||||||
"rocket-silo": -1}
|
"rocket-silo": -1}
|
||||||
loc: FactorioScienceLocation
|
loc: FactorioScienceLocation
|
||||||
if self.multiworld.tech_tree_information[player] == TechTreeInformation.option_full:
|
if self.options.tech_tree_information == TechTreeInformation.option_full:
|
||||||
# mark all locations as pre-hinted
|
# mark all locations as pre-hinted
|
||||||
for loc in self.science_locations:
|
for loc in self.science_locations:
|
||||||
loc.revealed = True
|
loc.revealed = True
|
||||||
|
@ -229,14 +230,13 @@ class Factorio(World):
|
||||||
loc.revealed = True
|
loc.revealed = True
|
||||||
|
|
||||||
def set_rules(self):
|
def set_rules(self):
|
||||||
world = self.multiworld
|
|
||||||
player = self.player
|
player = self.player
|
||||||
shapes = get_shapes(self)
|
shapes = get_shapes(self)
|
||||||
|
|
||||||
for ingredient in self.multiworld.max_science_pack[self.player].get_allowed_packs():
|
for ingredient in self.options.max_science_pack.get_allowed_packs():
|
||||||
location = world.get_location(f"Automate {ingredient}", player)
|
location = self.get_location(f"Automate {ingredient}")
|
||||||
|
|
||||||
if self.multiworld.recipe_ingredients[self.player]:
|
if self.options.recipe_ingredients:
|
||||||
custom_recipe = self.custom_recipes[ingredient]
|
custom_recipe = self.custom_recipes[ingredient]
|
||||||
|
|
||||||
location.access_rule = lambda state, ingredient=ingredient, custom_recipe=custom_recipe: \
|
location.access_rule = lambda state, ingredient=ingredient, custom_recipe=custom_recipe: \
|
||||||
|
@ -257,30 +257,30 @@ class Factorio(World):
|
||||||
prerequisites: all(state.can_reach(loc) for loc in locations))
|
prerequisites: all(state.can_reach(loc) for loc in locations))
|
||||||
|
|
||||||
silo_recipe = None
|
silo_recipe = None
|
||||||
if self.multiworld.silo[self.player] == Silo.option_spawn:
|
if self.options.silo == Silo.option_spawn:
|
||||||
silo_recipe = self.custom_recipes["rocket-silo"] if "rocket-silo" in self.custom_recipes \
|
silo_recipe = self.custom_recipes["rocket-silo"] if "rocket-silo" in self.custom_recipes \
|
||||||
else next(iter(all_product_sources.get("rocket-silo")))
|
else next(iter(all_product_sources.get("rocket-silo")))
|
||||||
part_recipe = self.custom_recipes["rocket-part"]
|
part_recipe = self.custom_recipes["rocket-part"]
|
||||||
satellite_recipe = None
|
satellite_recipe = None
|
||||||
if self.multiworld.goal[self.player] == Goal.option_satellite:
|
if self.options.goal == Goal.option_satellite:
|
||||||
satellite_recipe = self.custom_recipes["satellite"] if "satellite" in self.custom_recipes \
|
satellite_recipe = self.custom_recipes["satellite"] if "satellite" in self.custom_recipes \
|
||||||
else next(iter(all_product_sources.get("satellite")))
|
else next(iter(all_product_sources.get("satellite")))
|
||||||
victory_tech_names = get_rocket_requirements(silo_recipe, part_recipe, satellite_recipe)
|
victory_tech_names = get_rocket_requirements(silo_recipe, part_recipe, satellite_recipe)
|
||||||
if self.multiworld.silo[self.player] != Silo.option_spawn:
|
if self.options.silo != Silo.option_spawn:
|
||||||
victory_tech_names.add("rocket-silo")
|
victory_tech_names.add("rocket-silo")
|
||||||
world.get_location("Rocket Launch", player).access_rule = lambda state: all(state.has(technology, player)
|
self.get_location("Rocket Launch").access_rule = lambda state: all(state.has(technology, player)
|
||||||
for technology in
|
for technology in
|
||||||
victory_tech_names)
|
victory_tech_names)
|
||||||
|
|
||||||
world.completion_condition[player] = lambda state: state.has('Victory', player)
|
self.multiworld.completion_condition[player] = lambda state: state.has('Victory', player)
|
||||||
|
|
||||||
def generate_basic(self):
|
def generate_basic(self):
|
||||||
map_basic_settings = self.multiworld.world_gen[self.player].value["basic"]
|
map_basic_settings = self.options.world_gen.value["basic"]
|
||||||
if map_basic_settings.get("seed", None) is None: # allow seed 0
|
if map_basic_settings.get("seed", None) is None: # allow seed 0
|
||||||
# 32 bit uint
|
# 32 bit uint
|
||||||
map_basic_settings["seed"] = self.multiworld.per_slot_randoms[self.player].randint(0, 2 ** 32 - 1)
|
map_basic_settings["seed"] = self.random.randint(0, 2 ** 32 - 1)
|
||||||
|
|
||||||
start_location_hints: typing.Set[str] = self.multiworld.start_location_hints[self.player].value
|
start_location_hints: typing.Set[str] = self.options.start_location_hints.value
|
||||||
|
|
||||||
for loc in self.science_locations:
|
for loc in self.science_locations:
|
||||||
# show start_location_hints ingame
|
# show start_location_hints ingame
|
||||||
|
@ -304,8 +304,6 @@ class Factorio(World):
|
||||||
|
|
||||||
return super(Factorio, self).collect_item(state, item, remove)
|
return super(Factorio, self).collect_item(state, item, remove)
|
||||||
|
|
||||||
option_definitions = factorio_options
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def stage_write_spoiler(cls, world, spoiler_handle):
|
def stage_write_spoiler(cls, world, spoiler_handle):
|
||||||
factorio_players = world.get_game_players(cls.game)
|
factorio_players = world.get_game_players(cls.game)
|
||||||
|
@ -345,7 +343,7 @@ class Factorio(World):
|
||||||
# have to first sort for determinism, while filtering out non-stacking items
|
# have to first sort for determinism, while filtering out non-stacking items
|
||||||
pool: typing.List[str] = sorted(pool & valid_ingredients)
|
pool: typing.List[str] = sorted(pool & valid_ingredients)
|
||||||
# then sort with random data to shuffle
|
# then sort with random data to shuffle
|
||||||
self.multiworld.random.shuffle(pool)
|
self.random.shuffle(pool)
|
||||||
target_raw = int(sum((count for ingredient, count in original.base_cost.items())) * factor)
|
target_raw = int(sum((count for ingredient, count in original.base_cost.items())) * factor)
|
||||||
target_energy = original.total_energy * factor
|
target_energy = original.total_energy * factor
|
||||||
target_num_ingredients = len(original.ingredients) + ingredients_offset
|
target_num_ingredients = len(original.ingredients) + ingredients_offset
|
||||||
|
@ -389,7 +387,7 @@ class Factorio(World):
|
||||||
if min_num > max_num:
|
if min_num > max_num:
|
||||||
fallback_pool.append(ingredient)
|
fallback_pool.append(ingredient)
|
||||||
continue # can't use that ingredient
|
continue # can't use that ingredient
|
||||||
num = self.multiworld.random.randint(min_num, max_num)
|
num = self.random.randint(min_num, max_num)
|
||||||
new_ingredients[ingredient] = num
|
new_ingredients[ingredient] = num
|
||||||
remaining_raw -= num * ingredient_raw
|
remaining_raw -= num * ingredient_raw
|
||||||
remaining_energy -= num * ingredient_energy
|
remaining_energy -= num * ingredient_energy
|
||||||
|
@ -433,66 +431,66 @@ class Factorio(World):
|
||||||
|
|
||||||
def set_custom_technologies(self):
|
def set_custom_technologies(self):
|
||||||
custom_technologies = {}
|
custom_technologies = {}
|
||||||
allowed_packs = self.multiworld.max_science_pack[self.player].get_allowed_packs()
|
allowed_packs = self.options.max_science_pack.get_allowed_packs()
|
||||||
for technology_name, technology in base_technology_table.items():
|
for technology_name, technology in base_technology_table.items():
|
||||||
custom_technologies[technology_name] = technology.get_custom(self.multiworld, allowed_packs, self.player)
|
custom_technologies[technology_name] = technology.get_custom(self, allowed_packs, self.player)
|
||||||
return custom_technologies
|
return custom_technologies
|
||||||
|
|
||||||
def set_custom_recipes(self):
|
def set_custom_recipes(self):
|
||||||
ingredients_offset = self.multiworld.recipe_ingredients_offset[self.player]
|
ingredients_offset = self.options.recipe_ingredients_offset
|
||||||
original_rocket_part = recipes["rocket-part"]
|
original_rocket_part = recipes["rocket-part"]
|
||||||
science_pack_pools = get_science_pack_pools()
|
science_pack_pools = get_science_pack_pools()
|
||||||
valid_pool = sorted(science_pack_pools[self.multiworld.max_science_pack[self.player].get_max_pack()] & valid_ingredients)
|
valid_pool = sorted(science_pack_pools[self.options.max_science_pack.get_max_pack()] & valid_ingredients)
|
||||||
self.multiworld.random.shuffle(valid_pool)
|
self.random.shuffle(valid_pool)
|
||||||
self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category,
|
self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category,
|
||||||
{valid_pool[x]: 10 for x in range(3 + ingredients_offset)},
|
{valid_pool[x]: 10 for x in range(3 + ingredients_offset)},
|
||||||
original_rocket_part.products,
|
original_rocket_part.products,
|
||||||
original_rocket_part.energy)}
|
original_rocket_part.energy)}
|
||||||
|
|
||||||
if self.multiworld.recipe_ingredients[self.player]:
|
if self.options.recipe_ingredients:
|
||||||
valid_pool = []
|
valid_pool = []
|
||||||
for pack in self.multiworld.max_science_pack[self.player].get_ordered_science_packs():
|
for pack in self.options.max_science_pack.get_ordered_science_packs():
|
||||||
valid_pool += sorted(science_pack_pools[pack])
|
valid_pool += sorted(science_pack_pools[pack])
|
||||||
self.multiworld.random.shuffle(valid_pool)
|
self.random.shuffle(valid_pool)
|
||||||
if pack in recipes: # skips over space science pack
|
if pack in recipes: # skips over space science pack
|
||||||
new_recipe = self.make_quick_recipe(recipes[pack], valid_pool, ingredients_offset=
|
new_recipe = self.make_quick_recipe(recipes[pack], valid_pool, ingredients_offset=
|
||||||
ingredients_offset)
|
ingredients_offset.value)
|
||||||
self.custom_recipes[pack] = new_recipe
|
self.custom_recipes[pack] = new_recipe
|
||||||
|
|
||||||
if self.multiworld.silo[self.player].value == Silo.option_randomize_recipe \
|
if self.options.silo.value == Silo.option_randomize_recipe \
|
||||||
or self.multiworld.satellite[self.player].value == Satellite.option_randomize_recipe:
|
or self.options.satellite.value == Satellite.option_randomize_recipe:
|
||||||
valid_pool = set()
|
valid_pool = set()
|
||||||
for pack in sorted(self.multiworld.max_science_pack[self.player].get_allowed_packs()):
|
for pack in sorted(self.options.max_science_pack.get_allowed_packs()):
|
||||||
valid_pool |= science_pack_pools[pack]
|
valid_pool |= science_pack_pools[pack]
|
||||||
|
|
||||||
if self.multiworld.silo[self.player].value == Silo.option_randomize_recipe:
|
if self.options.silo.value == Silo.option_randomize_recipe:
|
||||||
new_recipe = self.make_balanced_recipe(
|
new_recipe = self.make_balanced_recipe(
|
||||||
recipes["rocket-silo"], valid_pool,
|
recipes["rocket-silo"], valid_pool,
|
||||||
factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7,
|
factor=(self.options.max_science_pack.value + 1) / 7,
|
||||||
ingredients_offset=ingredients_offset)
|
ingredients_offset=ingredients_offset.value)
|
||||||
self.custom_recipes["rocket-silo"] = new_recipe
|
self.custom_recipes["rocket-silo"] = new_recipe
|
||||||
|
|
||||||
if self.multiworld.satellite[self.player].value == Satellite.option_randomize_recipe:
|
if self.options.satellite.value == Satellite.option_randomize_recipe:
|
||||||
new_recipe = self.make_balanced_recipe(
|
new_recipe = self.make_balanced_recipe(
|
||||||
recipes["satellite"], valid_pool,
|
recipes["satellite"], valid_pool,
|
||||||
factor=(self.multiworld.max_science_pack[self.player].value + 1) / 7,
|
factor=(self.options.max_science_pack.value + 1) / 7,
|
||||||
ingredients_offset=ingredients_offset)
|
ingredients_offset=ingredients_offset.value)
|
||||||
self.custom_recipes["satellite"] = new_recipe
|
self.custom_recipes["satellite"] = new_recipe
|
||||||
bridge = "ap-energy-bridge"
|
bridge = "ap-energy-bridge"
|
||||||
new_recipe = self.make_quick_recipe(
|
new_recipe = self.make_quick_recipe(
|
||||||
Recipe(bridge, "crafting", {"replace_1": 1, "replace_2": 1, "replace_3": 1,
|
Recipe(bridge, "crafting", {"replace_1": 1, "replace_2": 1, "replace_3": 1,
|
||||||
"replace_4": 1, "replace_5": 1, "replace_6": 1},
|
"replace_4": 1, "replace_5": 1, "replace_6": 1},
|
||||||
{bridge: 1}, 10),
|
{bridge: 1}, 10),
|
||||||
sorted(science_pack_pools[self.multiworld.max_science_pack[self.player].get_ordered_science_packs()[0]]),
|
sorted(science_pack_pools[self.options.max_science_pack.get_ordered_science_packs()[0]]),
|
||||||
ingredients_offset=ingredients_offset)
|
ingredients_offset=ingredients_offset.value)
|
||||||
for ingredient_name in new_recipe.ingredients:
|
for ingredient_name in new_recipe.ingredients:
|
||||||
new_recipe.ingredients[ingredient_name] = self.multiworld.random.randint(50, 500)
|
new_recipe.ingredients[ingredient_name] = self.random.randint(50, 500)
|
||||||
self.custom_recipes[bridge] = new_recipe
|
self.custom_recipes[bridge] = new_recipe
|
||||||
|
|
||||||
needed_recipes = self.multiworld.max_science_pack[self.player].get_allowed_packs() | {"rocket-part"}
|
needed_recipes = self.options.max_science_pack.get_allowed_packs() | {"rocket-part"}
|
||||||
if self.multiworld.silo[self.player] != Silo.option_spawn:
|
if self.options.silo != Silo.option_spawn:
|
||||||
needed_recipes |= {"rocket-silo"}
|
needed_recipes |= {"rocket-silo"}
|
||||||
if self.multiworld.goal[self.player].value == Goal.option_satellite:
|
if self.options.goal.value == Goal.option_satellite:
|
||||||
needed_recipes |= {"satellite"}
|
needed_recipes |= {"satellite"}
|
||||||
|
|
||||||
for recipe in needed_recipes:
|
for recipe in needed_recipes:
|
||||||
|
@ -542,7 +540,8 @@ class FactorioScienceLocation(FactorioLocation):
|
||||||
|
|
||||||
self.ingredients = {Factorio.ordered_science_packs[self.complexity]: 1}
|
self.ingredients = {Factorio.ordered_science_packs[self.complexity]: 1}
|
||||||
for complexity in range(self.complexity):
|
for complexity in range(self.complexity):
|
||||||
if parent.multiworld.tech_cost_mix[self.player] > parent.multiworld.random.randint(0, 99):
|
if (parent.multiworld.worlds[self.player].options.tech_cost_mix >
|
||||||
|
parent.multiworld.worlds[self.player].random.randint(0, 99)):
|
||||||
self.ingredients[Factorio.ordered_science_packs[complexity]] = 1
|
self.ingredients[Factorio.ordered_science_packs[complexity]] = 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
Loading…
Reference in New Issue