Factorio integration

This commit is contained in:
Fabian Dill
2021-04-01 11:40:58 +02:00
parent 1f5bcb6273
commit dc73fa0f33
36 changed files with 1154 additions and 551 deletions

58
worlds/factorio/Mod.py Normal file
View File

@@ -0,0 +1,58 @@
"""Outputs a Factorio Mod to facilitate integration with Archipelago"""
import os
from typing import Optional
import threading
import json
import jinja2
import Utils
import shutil
from BaseClasses import MultiWorld
from .Technologies import tech_table
template: Optional[jinja2.Template] = None
locale_template: Optional[jinja2.Template] = None
template_load_lock = threading.Lock()
base_info = {
"version": Utils.__version__,
"title": "Archipelago",
"author": "Berserker",
"homepage": "https://archipelago.gg",
"description": "Integration client for the Archipelago Randomizer",
"factorio_version": "1.1"
}
def generate_mod(world: MultiWorld, player: int):
global template, locale_template
with template_load_lock:
if not template:
template = jinja2.Template(open(Utils.local_path("data", "factorio", "mod_template", "data-final-fixes.lua")).read())
locale_template = jinja2.Template(open(Utils.local_path("data", "factorio", "mod_template", "locale", "en", "locale.cfg")).read())
# get data for templates
player_names = {x: world.player_names[x][0] for x in world.player_ids}
locations = []
for location in world.get_filled_locations(player):
if not location.name.startswith("recipe-"): # introduce this a new location property?
locations.append((location.name, location.item.name, location.item.player))
mod_name = f"archipelago-client-{world.seed}-{player}"
template_data = {"locations": locations, "player_names" : player_names, "tech_table": tech_table,
"mod_name": mod_name}
mod_code = template.render(**template_data)
mod_dir = Utils.output_path(mod_name)
en_locale_dir = os.path.join(mod_dir, "locale", "en")
os.makedirs(en_locale_dir, exist_ok=True)
shutil.copytree(Utils.local_path("data", "factorio", "mod"), mod_dir, dirs_exist_ok=True)
with open(os.path.join(mod_dir, "data-final-fixes.lua"), "wt") as f:
f.write(mod_code)
locale_content = locale_template.render(**template_data)
with open(os.path.join(en_locale_dir, "locale.cfg"), "wt") as f:
f.write(locale_content)
info = base_info.copy()
info["name"] = mod_name
with open(os.path.join(mod_dir, "info.json"), "wt") as f:
json.dump(info, f, indent=4)

View File

@@ -0,0 +1,46 @@
# Factorio technologies are imported from a .json document in /data
from typing import Dict
import os
import json
import Utils
factorio_id = 2**17
source_file = Utils.local_path("data", "factorio", "techs.json")
with open(source_file) as f:
raw = json.load(f)
tech_table = {}
requirements = {}
ingredients = {}
all_ingredients = set()
# TODO: export this dynamically, or filter it during export
starting_ingredient_recipes = {"automation-science-pack"}
# recipes and technologies can share names in Factorio
for technology in sorted(raw):
data = raw[technology]
tech_table[technology] = factorio_id
factorio_id += 1
if data["requires"]:
requirements[technology] = set(data["requires"])
current_ingredients = set(data["ingredients"])-starting_ingredient_recipes
if current_ingredients:
all_ingredients |= current_ingredients
current_ingredients = {"recipe-"+ingredient for ingredient in current_ingredients}
ingredients[technology] = current_ingredients
recipe_sources = {}
for technology, data in raw.items():
recipe_source = all_ingredients & set(data["unlocks"])
for recipe in recipe_source:
recipe_sources["recipe-"+recipe] = technology
all_ingredients = {"recipe-"+ingredient for ingredient in all_ingredients}
del(raw)
lookup_id_to_name: Dict[int, str] = {item_id: item_name for item_name, item_id in tech_table.items()}

View File

@@ -0,0 +1,59 @@
import logging
from BaseClasses import Region, Entrance, Location, MultiWorld, Item
from .Technologies import tech_table, requirements, ingredients, all_ingredients, recipe_sources
static_nodes = {"automation", "logistics"}
def gen_factorio(world: MultiWorld, player: int):
for tech_name, tech_id in tech_table.items():
tech_item = Item(tech_name, True, tech_id, player)
tech_item.game = "Factorio"
if tech_name in static_nodes:
loc = world.get_location(tech_name, player)
loc.item = tech_item
loc.locked = loc.event = True
else:
world.itempool.append(tech_item)
set_rules(world, player)
def factorio_create_regions(world: MultiWorld, player: int):
menu = Region("Menu", None, "Menu", player)
crash = Entrance(player, "Crash Land", menu)
menu.exits.append(crash)
nauvis = Region("Nauvis", None, "Nauvis", player)
nauvis.world = menu.world = world
for tech_name, tech_id in tech_table.items():
tech = Location(player, tech_name, tech_id, nauvis)
nauvis.locations.append(tech)
tech.game = "Factorio"
for ingredient in all_ingredients: # register science packs as events
ingredient_location = Location(player, ingredient, 0, nauvis)
ingredient_location.item = Item(ingredient, True, 0, player)
ingredient_location.event = ingredient_location.locked = True
menu.locations.append(ingredient_location)
crash.connect(nauvis)
world.regions += [menu, nauvis]
def set_rules(world: MultiWorld, player: int):
if world.logic[player] != 'nologic':
from worlds.generic import Rules
for tech_name in tech_table:
# vanilla layout, to be implemented
# rules = requirements.get(tech_name, set()) | ingredients.get(tech_name, set())
# loose nodes
rules = ingredients.get(tech_name, set())
if rules:
location = world.get_location(tech_name, player)
Rules.set_rule(location, lambda state, rules=rules: all(state.has(rule, player) for rule in rules))
for recipe, technology in recipe_sources.items():
Rules.set_rule(world.get_location(recipe, player), lambda state, tech=technology: state.has(tech, player))
world.completion_condition[player] = lambda state: all(state.has(ingredient, player)
for ingredient in all_ingredients)