Skip to content

Commit

Permalink
Factorio: support 2.0 update
Browse files Browse the repository at this point in the history
  • Loading branch information
Berserker66 committed Oct 27, 2024
1 parent 579abb3 commit db6e758
Show file tree
Hide file tree
Showing 15 changed files with 173 additions and 140 deletions.
6 changes: 3 additions & 3 deletions worlds/factorio/Client.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@ async def factorio_server_watcher(ctx: FactorioContext):
factorio_queue.task_done()

if not ctx.rcon_client and "Starting RCON interface at IP ADDR:" in msg:
ctx.rcon_client = factorio_rcon.RCONClient("localhost", rcon_port, rcon_password)
ctx.rcon_client = factorio_rcon.RCONClient("localhost", rcon_port, rcon_password,
timeout=5)
if not ctx.server:
logger.info("Established bridge to Factorio Server. "
"Ready to connect to Archipelago via /connect")
Expand Down Expand Up @@ -405,8 +406,7 @@ async def get_info(ctx: FactorioContext, rcon_client: factorio_rcon.RCONClient):
info = json.loads(rcon_client.send_command("/ap-rcon-info"))
ctx.auth = info["slot_name"]
ctx.seed_name = info["seed_name"]
# 0.2.0 addition, not present earlier
death_link = bool(info.get("death_link", False))
death_link = info["death_link"]
ctx.energy_link_increment = info.get("energy_link", 0)
logger.debug(f"Energy Link Increment: {ctx.energy_link_increment}")
if ctx.energy_link_increment and ctx.ui:
Expand Down
5 changes: 3 additions & 2 deletions worlds/factorio/Mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
"author": "Berserker",
"homepage": "https://archipelago.gg",
"description": "Integration client for the Archipelago Randomizer",
"factorio_version": "1.1",
"factorio_version": "2.0",
"dependencies": [
"base >= 1.1.0",
"base >= 2.0.11",
"! space-age",
"? science-not-invited",
"? factory-levels"
]
Expand Down
4 changes: 0 additions & 4 deletions worlds/factorio/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,6 @@ class FactorioWorldGen(OptionDict):
# FIXME: do we want default be a rando-optimized default or in-game DS?
value: typing.Dict[str, typing.Dict[str, typing.Any]]
default = {
"terrain_segmentation": 0.5,
"water": 1.5,
"autoplace_controls": {
"coal": {"frequency": 1, "size": 3, "richness": 6},
"copper-ore": {"frequency": 1, "size": 3, "richness": 6},
Expand Down Expand Up @@ -336,8 +334,6 @@ class FactorioWorldGen(OptionDict):
}
schema = Schema({
"basic": {
Optional("terrain_segmentation"): FloatRange(0.166, 6),
Optional("water"): FloatRange(0.166, 6),
Optional("autoplace_controls"): {
str: {
"frequency": FloatRange(0, 6),
Expand Down
88 changes: 60 additions & 28 deletions worlds/factorio/Technologies.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from __future__ import annotations

import orjson
import logging
import os
import string
import functools
import pkgutil
import string
from collections import Counter
from concurrent.futures import ThreadPoolExecutor
from typing import Dict, Set, FrozenSet, Tuple, Union, List, Any

import orjson

import Utils
from . import Options

Expand All @@ -32,8 +33,23 @@ def load_json_data(data_name: str) -> Union[List[str], Dict[str, Any]]:
tech_table: Dict[str, int] = {}
technology_table: Dict[str, Technology] = {}

start_unlocked_recipes = {
"offshore-pump",
"boiler",
"steam-engine",
"automation-science-pack",
"inserter",
"small-electric-pole",
"copper-cable",
"lab",
"electronic-circuit",
"electric-mining-drill",
"pipe",
"pipe-to-ground",
}


def always(state):
def always(state) -> bool:
return True


Expand All @@ -55,12 +71,15 @@ class Technology(FactorioElement): # maybe make subclass of Location?
unlocks: Union[Set[str], bool] # bool case is for progressive technologies

def __init__(self, name: str, ingredients: Set[str], factorio_id: int, progressive: Tuple[str] = (),
has_modifier: bool = False, unlocks: Union[Set[str], bool] = None):
has_modifier: bool = False, unlocks: Union[Set[str], bool] = None, trigger: Dict[str, Any] = None):
self.name = name
self.factorio_id = factorio_id
self.ingredients = ingredients
self.progressive = progressive
self.has_modifier = has_modifier
if not trigger:
trigger = {}
self.trigger = trigger
if unlocks:
self.unlocks = unlocks
else:
Expand Down Expand Up @@ -95,15 +114,20 @@ class CustomTechnology(Technology):
def __init__(self, origin: Technology, world, allowed_packs: Set[str], player: int):
ingredients = origin.ingredients & allowed_packs
military_allowed = "military-science-pack" in allowed_packs \
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")
self.player = player
if origin.name not in world.special_nodes:
if military_allowed:
ingredients.add("military-science-pack")
ingredients = list(ingredients)
ingredients.sort() # deterministic sample
ingredients = world.random.sample(ingredients, world.random.randint(1, len(ingredients)))
if ingredients:
ingredients.sort() # deterministic sample
ingredients = set(world.random.sample(ingredients, world.random.randint(1, len(ingredients))))
else:
ingredients = set()
elif origin.name == "rocket-silo" and military_allowed:
ingredients.add("military-science-pack")
super(CustomTechnology, self).__init__(origin.name, ingredients, origin.factorio_id)
Expand Down Expand Up @@ -149,19 +173,22 @@ def rel_cost(self) -> float:
ingredients = sum(self.ingredients.values())
return min(ingredients / amount for product, amount in self.products.items())

@property
@functools.cached_property
def base_cost(self) -> Dict[str, int]:
ingredients = Counter()
for ingredient, cost in self.ingredients.items():
if ingredient in all_product_sources:
for recipe in all_product_sources[ingredient]:
if recipe.ingredients:
ingredients.update({name: amount * cost / recipe.products[ingredient] for name, amount in
recipe.base_cost.items()})
else:
ingredients[ingredient] += recipe.energy * cost / recipe.products[ingredient]
else:
ingredients[ingredient] += cost
try:
for ingredient, cost in self.ingredients.items():
if ingredient in all_product_sources:
for recipe in all_product_sources[ingredient]:
if recipe.ingredients:
ingredients.update({name: amount * cost / recipe.products[ingredient] for name, amount in
recipe.base_cost.items()})
else:
ingredients[ingredient] += recipe.energy * cost / recipe.products[ingredient]
else:
ingredients[ingredient] += cost
except RecursionError as e:
raise Exception(f"Infinite recursion in ingredients of {self}.") from e
return ingredients

@property
Expand Down Expand Up @@ -193,7 +220,9 @@ def __init__(self, name, categories):
for technology_name, data in sorted(techs_future.result().items()):
current_ingredients = set(data["ingredients"])
technology = Technology(technology_name, current_ingredients, factorio_tech_id,
has_modifier=data["has_modifier"], unlocks=set(data["unlocks"]))
has_modifier=data["has_modifier"],
unlocks=set(data["unlocks"]) - start_unlocked_recipes,
trigger=data.get("trigger", None))
factorio_tech_id += 1
tech_table[technology_name] = technology.factorio_id
technology_table[technology_name] = technology
Expand Down Expand Up @@ -226,11 +255,12 @@ def __init__(self, name, categories):
recipes[recipe_name] = recipe
if set(recipe.products).isdisjoint(
# prevents loop recipes like uranium centrifuging
set(recipe.ingredients)) and ("empty-barrel" not in recipe.products or recipe.name == "empty-barrel") and \
set(recipe.ingredients)) and ("barrel" not in recipe.products or recipe.name == "barrel") and \
not recipe_name.endswith("-reprocessing"):
for product_name in recipe.products:
all_product_sources.setdefault(product_name, set()).add(recipe)

assert all(recipe_name in raw_recipes for recipe_name in start_unlocked_recipes), "Unknown Recipe defined."

machines: Dict[str, Machine] = {}

Expand Down Expand Up @@ -382,15 +412,15 @@ def get_rocket_requirements(silo_recipe: Recipe, part_recipe: Recipe, satellite_
"uranium-processing", "kovarex-enrichment-process", "nuclear-fuel-reprocessing")
progressive_rows["progressive-rocketry"] = ("rocketry", "explosive-rocketry", "atomic-bomb")
progressive_rows["progressive-vehicle"] = ("automobilism", "tank", "spidertron")
progressive_rows["progressive-train-network"] = ("railway", "fluid-wagon",
"automated-rail-transportation", "rail-signals")
progressive_rows["progressive-fluid-handling"] = ("fluid-handling", "fluid-wagon")
progressive_rows["progressive-train-network"] = ("railway", "automated-rail-transportation")
progressive_rows["progressive-engine"] = ("engine", "electric-engine")
progressive_rows["progressive-armor"] = ("heavy-armor", "modular-armor", "power-armor", "power-armor-mk2")
progressive_rows["progressive-personal-battery"] = ("battery-equipment", "battery-mk2-equipment")
progressive_rows["progressive-energy-shield"] = ("energy-shield-equipment", "energy-shield-mk2-equipment")
progressive_rows["progressive-wall"] = ("stone-wall", "gate")
progressive_rows["progressive-follower"] = ("defender", "distractor", "destroyer")
progressive_rows["progressive-inserter"] = ("fast-inserter", "stack-inserter")
progressive_rows["progressive-inserter"] = ("fast-inserter", "bulk-inserter")
progressive_rows["progressive-turret"] = ("gun-turret", "laser-turret")
progressive_rows["progressive-flamethrower"] = ("flamethrower",) # leaving out flammables, as they do nothing
progressive_rows["progressive-personal-roboport-equipment"] = ("personal-roboport-equipment",
Expand All @@ -402,7 +432,7 @@ def get_rocket_requirements(silo_recipe: Recipe, part_recipe: Recipe, satellite_
source_target_mapping: Dict[str, str] = {
"progressive-braking-force": "progressive-train-network",
"progressive-inserter-capacity-bonus": "progressive-inserter",
"progressive-refined-flammables": "progressive-flamethrower"
"progressive-refined-flammables": "progressive-flamethrower",
}

for source, target in source_target_mapping.items():
Expand All @@ -416,12 +446,14 @@ def get_rocket_requirements(silo_recipe: Recipe, part_recipe: Recipe, satellite_

for root in sorted_rows:
progressive = progressive_rows[root]
assert all(tech in tech_table for tech in progressive), "declared a progressive technology without base technology"
assert all(tech in tech_table for tech in progressive), \
(f"Declared a progressive technology ({root}) without base technology. "
f"Missing: f{tuple(tech for tech in progressive if tech not in tech_table)}")
factorio_tech_id += 1
progressive_technology = Technology(root, technology_table[progressive[0]].ingredients, factorio_tech_id,
progressive,
tuple(progressive),
has_modifier=any(technology_table[tech].has_modifier for tech in progressive),
unlocks=any(technology_table[tech].unlocks for tech in progressive))
unlocks=any(technology_table[tech].unlocks for tech in progressive),)
progressive_tech_table[root] = progressive_technology.factorio_id
progressive_technology_table[root] = progressive_technology

Expand Down
10 changes: 7 additions & 3 deletions worlds/factorio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import settings
import typing

from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification
from BaseClasses import Region, Location, Item, Tutorial, ItemClassification
from worlds.AutoWorld import World, WebWorld
from worlds.LauncherComponents import Component, components, Type, launch_subprocess
from worlds.generic import Rules
Expand Down Expand Up @@ -321,9 +321,11 @@ def get_category(category: str, liquids: int) -> str:

def make_quick_recipe(self, original: Recipe, pool: list, allow_liquids: int = 2,
ingredients_offset: int = 0) -> Recipe:
count: int = len(original.ingredients) + ingredients_offset
assert len(pool) >= count, f"Can't pick {count} many items from pool {pool}."
new_ingredients = {}
liquids_used = 0
for _ in range(len(original.ingredients) + ingredients_offset):
for _ in range(count):
new_ingredient = pool.pop()
if new_ingredient in fluids:
while liquids_used == allow_liquids and new_ingredient in fluids:
Expand Down Expand Up @@ -440,7 +442,9 @@ def set_custom_recipes(self):
ingredients_offset = self.options.recipe_ingredients_offset
original_rocket_part = recipes["rocket-part"]
science_pack_pools = get_science_pack_pools()
valid_pool = sorted(science_pack_pools[self.options.max_science_pack.get_max_pack()] & valid_ingredients)
valid_pool = sorted(science_pack_pools[self.options.max_science_pack.get_max_pack()]
& valid_ingredients
- fluids)
self.random.shuffle(valid_pool)
self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category,
{valid_pool[x]: 10 for x in range(3 + ingredients_offset)},
Expand Down
2 changes: 1 addition & 1 deletion worlds/factorio/data/fluids.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
["fluid-unknown","water","crude-oil","steam","heavy-oil","light-oil","petroleum-gas","sulfuric-acid","lubricant"]
["water","steam","crude-oil","petroleum-gas","light-oil","heavy-oil","lubricant","sulfuric-acid","parameter-0","parameter-1","parameter-2","parameter-3","parameter-4","parameter-5","parameter-6","parameter-7","parameter-8","parameter-9","fluid-unknown"]
Loading

0 comments on commit db6e758

Please sign in to comment.