mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Factorio integration
This commit is contained in:
58
worlds/factorio/Mod.py
Normal file
58
worlds/factorio/Mod.py
Normal 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)
|
||||
46
worlds/factorio/Technologies.py
Normal file
46
worlds/factorio/Technologies.py
Normal 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()}
|
||||
59
worlds/factorio/__init__.py
Normal file
59
worlds/factorio/__init__.py
Normal 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)
|
||||
Reference in New Issue
Block a user