Factorio: add small_diamonds tech tree layout

This commit is contained in:
Fabian Dill 2021-04-10 03:03:46 +02:00
parent e11016b0a2
commit 0586b24579
8 changed files with 53 additions and 15 deletions

View File

@ -60,6 +60,7 @@ class MultiWorld():
for player in range(1, players + 1): for player in range(1, players + 1):
def set_player_attr(attr, val): def set_player_attr(attr, val):
self.__dict__.setdefault(attr, {})[player] = val self.__dict__.setdefault(attr, {})[player] = val
set_player_attr('tech_tree_layout_prerequisites', {})
set_player_attr('_region_cache', {}) set_player_attr('_region_cache', {})
set_player_attr('shuffle', "vanilla") set_player_attr('shuffle', "vanilla")
set_player_attr('logic', "noglitches") set_player_attr('logic', "noglitches")

View File

@ -273,6 +273,7 @@ class FreeSamples(Choice):
class TechTreeLayout(Choice): class TechTreeLayout(Choice):
option_single = 0 option_single = 0
option_small_diamonds = 1
default = 0 default = 0
class Visibility(Choice): class Visibility(Choice):

View File

@ -44,9 +44,14 @@ new_tree_copy.icon_size = table.deepcopy(technologies["{{ item_name }}"].icon_si
new_tree_copy.icon = "__{{ mod_name }}__/graphics/icons/ap.png" new_tree_copy.icon = "__{{ mod_name }}__/graphics/icons/ap.png"
new_tree_copy.icons = nil new_tree_copy.icons = nil
new_tree_copy.icon_size = 512 new_tree_copy.icon_size = 512
{% endif %} {% endif %}
{#- add new technology to game #} {#- connect Technology #}
{%- if original_tech_name in tech_tree_layout_prerequisites %}
{%- for prerequesite in tech_tree_layout_prerequisites[original_tech_name] %}
table.insert(new_tree_copy.prerequisites, "ap-{{ tech_table[prerequesite] }}-")
{% endfor %}
{% endif -%}
{#- add new Technology to game #}
data:extend{new_tree_copy} data:extend{new_tree_copy}
{% endfor %} {% endfor %}

View File

@ -38,6 +38,7 @@ progression_balancing:
# Factorio options: # Factorio options:
tech_tree_layout: tech_tree_layout:
single: 1 single: 1
small_diamonds: 1
max_science_pack: max_science_pack:
automation_science_pack: 0 automation_science_pack: 0
logistic_science_pack: 0 logistic_science_pack: 0

View File

@ -8,28 +8,27 @@ __all__ = {"lookup_any_item_id_to_name",
from .alttp.Items import lookup_id_to_name as alttp from .alttp.Items import lookup_id_to_name as alttp
from .hk.Items import lookup_id_to_name as hk from .hk.Items import lookup_id_to_name as hk
from .factorio import Technologies from .factorio import Technologies
lookup_any_item_id_to_name = {**alttp, **hk, **Technologies.lookup_id_to_name} lookup_any_item_id_to_name = {**alttp, **hk, **Technologies.lookup_id_to_name}
assert len(alttp) + len(hk) + len(Technologies.lookup_id_to_name) == len(lookup_any_item_id_to_name) assert len(alttp) + len(hk) + len(Technologies.lookup_id_to_name) == len(lookup_any_item_id_to_name)
lookup_any_item_name_to_id = {name: id for id, name in lookup_any_item_id_to_name.items()} lookup_any_item_name_to_id = {name: id for id, name in lookup_any_item_id_to_name.items()}
from .alttp import Regions from .alttp import Regions
from .hk import Locations from .hk import Locations
lookup_any_location_id_to_name = {**Regions.lookup_id_to_name, **Locations.lookup_id_to_name, lookup_any_location_id_to_name = {**Regions.lookup_id_to_name, **Locations.lookup_id_to_name,
**Technologies.lookup_id_to_name} **Technologies.lookup_id_to_name}
assert len(Regions.lookup_id_to_name) + len(Locations.lookup_id_to_name) + len(Technologies.lookup_id_to_name) == \ assert len(Regions.lookup_id_to_name) + len(Locations.lookup_id_to_name) + len(Technologies.lookup_id_to_name) == \
len(lookup_any_location_id_to_name) len(lookup_any_location_id_to_name)
lookup_any_location_name_to_id = {name: id for id, name in lookup_any_location_id_to_name.items()} lookup_any_location_name_to_id = {name: id for id, name in lookup_any_location_id_to_name.items()}
network_data_package = {"lookup_any_location_id_to_name": lookup_any_location_id_to_name, network_data_package = {"lookup_any_location_id_to_name": lookup_any_location_id_to_name,
"lookup_any_item_id_to_name": lookup_any_item_id_to_name, "lookup_any_item_id_to_name": lookup_any_item_id_to_name,
"version": 3} "version": 4}
@enum.unique @enum.unique
class Games(str, enum.Enum): class Games(str, enum.Enum):
HK = "Hollow Knight" HK = "Hollow Knight"
LTTP = "A Link to the Past" LTTP = "A Link to the Past"
Factorio = "Factorio" Factorio = "Factorio"

View File

@ -51,7 +51,8 @@ def generate_mod(world: MultiWorld, player: int):
6: 10}[world.tech_cost[player].value] 6: 10}[world.tech_cost[player].value]
template_data = {"locations": locations, "player_names" : player_names, "tech_table": tech_table, template_data = {"locations": locations, "player_names" : player_names, "tech_table": tech_table,
"mod_name": mod_name, "allowed_science_packs": world.max_science_pack[player].get_allowed_packs(), "mod_name": mod_name, "allowed_science_packs": world.max_science_pack[player].get_allowed_packs(),
"tech_cost_scale": tech_cost} "tech_cost_scale": tech_cost,
"tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites[player]}
for factorio_option in Options.factorio_options: for factorio_option in Options.factorio_options:
template_data[factorio_option] = getattr(world, factorio_option)[player].value template_data[factorio_option] = getattr(world, factorio_option)[player].value
control_code = control_template.render(**template_data) control_code = control_template.render(**template_data)

View File

@ -1,3 +1,4 @@
from __future__ import annotations
# Factorio technologies are imported from a .json document in /data # Factorio technologies are imported from a .json document in /data
from typing import Dict, Set, FrozenSet from typing import Dict, Set, FrozenSet
import json import json
@ -16,6 +17,7 @@ technology_table = {}
always = lambda state: True always = lambda state: True
class Technology(): # maybe make subclass of Location? class Technology(): # maybe make subclass of Location?
def __init__(self, name, ingredients): def __init__(self, name, ingredients):
self.name = name self.name = name
@ -42,6 +44,14 @@ class Technology(): # maybe make subclass of Location?
return always return always
def get_prior_technologies(self, allowed_packs) -> Set[Technology]:
"""Get Technologies that have to precede this one to resolve tree connections."""
technologies = set()
for ingredient in self.ingredients:
if ingredient in allowed_packs:
technologies |= required_technologies[ingredient] # technologies that unlock the recipes
return technologies
def __hash__(self): def __hash__(self):
return self.factorio_id return self.factorio_id
@ -68,11 +78,7 @@ class Recipe():
# recipes and technologies can share names in Factorio # recipes and technologies can share names in Factorio
for technology_name in sorted(raw): for technology_name in sorted(raw):
data = raw[technology_name] data = raw[technology_name]
factorio_id += 1 factorio_id += 1
# not used yet
# if data["requires"]:
# requirements[technology] = set(data["requires"])
current_ingredients = set(data["ingredients"]) current_ingredients = set(data["ingredients"])
technology = Technology(technology_name, current_ingredients) technology = Technology(technology_name, current_ingredients)
tech_table[technology_name] = technology.factorio_id tech_table[technology_name] = technology.factorio_id
@ -87,7 +93,6 @@ for technology, data in raw.items():
del (raw) del (raw)
lookup_id_to_name: Dict[int, str] = {item_id: item_name for item_name, item_id in tech_table.items()} lookup_id_to_name: Dict[int, str] = {item_id: item_name for item_name, item_id in tech_table.items()}
all_recipes: Dict[str, Recipe] = {}
all_product_sources: Dict[str, Recipe] = {} all_product_sources: Dict[str, Recipe] = {}
for recipe_name, recipe_data in raw_recipes.items(): for recipe_name, recipe_data in raw_recipes.items():
# example: # example:
@ -95,7 +100,6 @@ for recipe_name, recipe_data in raw_recipes.items():
recipe = Recipe(recipe_name, recipe_data["category"], set(recipe_data["ingredients"]), set(recipe_data["products"])) recipe = Recipe(recipe_name, recipe_data["category"], set(recipe_data["ingredients"]), set(recipe_data["products"]))
if recipe.products != recipe.ingredients: # prevents loop recipes like uranium centrifuging if recipe.products != recipe.ingredients: # prevents loop recipes like uranium centrifuging
all_recipes[recipe_name] = recipe
for product_name in recipe.products: for product_name in recipe.products:
all_product_sources[product_name] = recipe all_product_sources[product_name] = recipe

View File

@ -1,7 +1,8 @@
import logging import logging
from typing import List, Dict
from BaseClasses import Region, Entrance, Location, MultiWorld, Item from BaseClasses import Region, Entrance, Location, MultiWorld, Item
from Options import TechTreeLayout
from .Technologies import tech_table, recipe_sources, technology_table, advancement_technologies, required_technologies from .Technologies import tech_table, recipe_sources, technology_table, advancement_technologies, required_technologies
static_nodes = {"automation", "logistics"} static_nodes = {"automation", "logistics"}
@ -35,7 +36,26 @@ def factorio_create_regions(world: MultiWorld, player: int):
world.regions += [menu, nauvis] world.regions += [menu, nauvis]
def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
prerequisites = {}
if world.tech_tree_layout[player].value == TechTreeLayout.option_small_diamonds:
tech_names: List[str] = list(set(technology_table)-static_nodes)
tech_names.sort()
world.random.shuffle(tech_names)
while len(tech_names) > 4:
diamond_0, diamond_1, diamond_2, diamond_3 = tech_names[:4]
tech_names = tech_names[4:]
# 0 |
# 1 2 |
# 3 V
prerequisites[diamond_3] = [diamond_1, diamond_2]
prerequisites[diamond_2] = prerequisites[diamond_1] = [diamond_0]
world.tech_tree_layout_prerequisites[player] = prerequisites
return prerequisites
def set_rules(world: MultiWorld, player: int): def set_rules(world: MultiWorld, player: int):
shapes = get_shapes(world, player)
if world.logic[player] != 'nologic': if world.logic[player] != 'nologic':
from worlds.generic import Rules from worlds.generic import Rules
allowed_packs = world.max_science_pack[player].get_allowed_packs() allowed_packs = world.max_science_pack[player].get_allowed_packs()
@ -43,6 +63,12 @@ def set_rules(world: MultiWorld, player: int):
# loose nodes # loose nodes
location = world.get_location(tech_name, player) location = world.get_location(tech_name, player)
Rules.set_rule(location, technology.build_rule(allowed_packs, player)) Rules.set_rule(location, technology.build_rule(allowed_packs, player))
prequisites = shapes.get(tech_name)
if prequisites:
locations = [world.get_location(requisite, player) for requisite in prequisites]
Rules.add_rule(location, lambda state,
locations=locations: all(state.can_reach(loc) for loc in locations))
# get all technologies # get all technologies
world.completion_condition[player] = lambda state: all(state.has(technology, player) world.completion_condition[player] = lambda state: all(state.has(technology, player)