Factorio: add option: random tech ingredients
This commit is contained in:
parent
73ed18c11d
commit
569e0e3004
|
@ -143,7 +143,9 @@ class MultiWorld():
|
||||||
import Options
|
import Options
|
||||||
for hk_option in Options.hollow_knight_options:
|
for hk_option in Options.hollow_knight_options:
|
||||||
set_player_attr(hk_option, False)
|
set_player_attr(hk_option, False)
|
||||||
|
self.custom_data = {}
|
||||||
|
for player in range(1, players+1):
|
||||||
|
self.custom_data[player] = {}
|
||||||
# self.worlds = []
|
# self.worlds = []
|
||||||
# for i in range(players):
|
# for i in range(players):
|
||||||
# self.worlds.append(worlds.alttp.ALTTPWorld({}, i))
|
# self.worlds.append(worlds.alttp.ALTTPWorld({}, i))
|
||||||
|
|
|
@ -293,7 +293,8 @@ factorio_options: typing.Dict[str, type(Option)] = {"max_science_pack": MaxScien
|
||||||
"tech_tree_layout": TechTreeLayout,
|
"tech_tree_layout": TechTreeLayout,
|
||||||
"tech_cost": TechCost,
|
"tech_cost": TechCost,
|
||||||
"free_samples": FreeSamples,
|
"free_samples": FreeSamples,
|
||||||
"visibility": Visibility}
|
"visibility": Visibility,
|
||||||
|
"random_tech_ingredients": Toggle}
|
||||||
|
|
||||||
minecraft_options: typing.Dict[str, type(Option)] = {}
|
minecraft_options: typing.Dict[str, type(Option)] = {}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
function filter_ingredients(ingredients)
|
function filter_ingredients(ingredients, ingredient_filter)
|
||||||
local new_ingredient_list = {}
|
local new_ingredient_list = {}
|
||||||
for _, ingredient_table in pairs(ingredients) do
|
for _, ingredient_table in pairs(ingredients) do
|
||||||
if allowed_ingredients[ingredient_table[1]] then -- name of ingredient_table
|
if ingredient_filter[ingredient_table[1]] then -- name of ingredient_table
|
||||||
table.insert(new_ingredient_list, ingredient_table)
|
table.insert(new_ingredient_list, ingredient_table)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,8 +5,12 @@ local technologies = data.raw["technology"]
|
||||||
local original_tech
|
local original_tech
|
||||||
local new_tree_copy
|
local new_tree_copy
|
||||||
allowed_ingredients = {}
|
allowed_ingredients = {}
|
||||||
{%- for ingredient in allowed_science_packs %}
|
{%- for tech_name, technology in custom_data["custom_technologies"].items() %}
|
||||||
allowed_ingredients["{{ingredient}}"]= 1
|
allowed_ingredients["{{ tech_name }}"] = {
|
||||||
|
{%- for ingredient in technology.ingredients %}
|
||||||
|
["{{ingredient}}"] = 1,
|
||||||
|
{%- endfor %}
|
||||||
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
local template_tech = table.deepcopy(technologies["automation"])
|
local template_tech = table.deepcopy(technologies["automation"])
|
||||||
{#- ensure the copy unlocks nothing #}
|
{#- ensure the copy unlocks nothing #}
|
||||||
|
@ -18,7 +22,10 @@ template_tech.prerequisites = {}
|
||||||
function prep_copy(new_copy, old_tech)
|
function prep_copy(new_copy, old_tech)
|
||||||
old_tech.enabled = false
|
old_tech.enabled = false
|
||||||
new_copy.unit = table.deepcopy(old_tech.unit)
|
new_copy.unit = table.deepcopy(old_tech.unit)
|
||||||
new_copy.unit.ingredients = filter_ingredients(new_copy.unit.ingredients)
|
local ingredient_filter = allowed_ingredients[old_tech.name]
|
||||||
|
if ingredient_filter ~= nil then
|
||||||
|
new_copy.unit.ingredients = filter_ingredients(new_copy.unit.ingredients, ingredient_filter)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,9 @@ free_samples:
|
||||||
visibility:
|
visibility:
|
||||||
none: 0
|
none: 0
|
||||||
sending: 1
|
sending: 1
|
||||||
|
random_tech_ingredients:
|
||||||
|
on: 1
|
||||||
|
off: 0
|
||||||
# A Link to the Past options:
|
# A Link to the Past options:
|
||||||
### Logic Section ###
|
### Logic Section ###
|
||||||
# Warning: overworld_glitches is not available and minor_glitches is only partially implemented on the door-rando version
|
# Warning: overworld_glitches is not available and minor_glitches is only partially implemented on the door-rando version
|
||||||
|
|
|
@ -51,7 +51,7 @@ def generate_mod(world: MultiWorld, player: int, seedname: str):
|
||||||
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, "custom_data": world.custom_data[player],
|
||||||
"tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites[player]}
|
"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
|
||||||
|
|
|
@ -7,15 +7,16 @@ from worlds.factorio.Technologies import technology_table
|
||||||
def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
|
def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
|
||||||
prerequisites: Dict[str, Set[str]] = {}
|
prerequisites: Dict[str, Set[str]] = {}
|
||||||
layout = world.tech_tree_layout[player].value
|
layout = world.tech_tree_layout[player].value
|
||||||
|
custom_technologies = world.custom_data[player]["custom_technologies"]
|
||||||
if layout == TechTreeLayout.option_small_diamonds:
|
if layout == TechTreeLayout.option_small_diamonds:
|
||||||
slice_size = 4
|
slice_size = 4
|
||||||
tech_names: List[str] = list(set(technology_table) - world._static_nodes)
|
tech_names: List[str] = list(set(custom_technologies) - world._static_nodes)
|
||||||
tech_names.sort()
|
tech_names.sort()
|
||||||
world.random.shuffle(tech_names)
|
world.random.shuffle(tech_names)
|
||||||
while len(tech_names) > slice_size:
|
while len(tech_names) > slice_size:
|
||||||
slice = tech_names[:slice_size]
|
slice = tech_names[:slice_size]
|
||||||
tech_names = tech_names[slice_size:]
|
tech_names = tech_names[slice_size:]
|
||||||
slice.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients))
|
slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients))
|
||||||
diamond_0, diamond_1, diamond_2, diamond_3 = slice
|
diamond_0, diamond_1, diamond_2, diamond_3 = slice
|
||||||
|
|
||||||
# 0 |
|
# 0 |
|
||||||
|
@ -25,13 +26,13 @@ def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
|
||||||
prerequisites[diamond_2] = prerequisites[diamond_1] = {diamond_0}
|
prerequisites[diamond_2] = prerequisites[diamond_1] = {diamond_0}
|
||||||
elif layout == TechTreeLayout.option_medium_diamonds:
|
elif layout == TechTreeLayout.option_medium_diamonds:
|
||||||
slice_size = 9
|
slice_size = 9
|
||||||
tech_names: List[str] = list(set(technology_table) - world._static_nodes)
|
tech_names: List[str] = list(set(custom_technologies) - world._static_nodes)
|
||||||
tech_names.sort()
|
tech_names.sort()
|
||||||
world.random.shuffle(tech_names)
|
world.random.shuffle(tech_names)
|
||||||
while len(tech_names) > slice_size:
|
while len(tech_names) > slice_size:
|
||||||
slice = tech_names[:slice_size]
|
slice = tech_names[:slice_size]
|
||||||
tech_names = tech_names[slice_size:]
|
tech_names = tech_names[slice_size:]
|
||||||
slice.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients))
|
slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients))
|
||||||
|
|
||||||
# 0 |
|
# 0 |
|
||||||
# 1 2 |
|
# 1 2 |
|
||||||
|
@ -53,10 +54,10 @@ def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
|
||||||
|
|
||||||
elif layout == TechTreeLayout.option_pyramid:
|
elif layout == TechTreeLayout.option_pyramid:
|
||||||
slice_size = 1
|
slice_size = 1
|
||||||
tech_names: List[str] = list(set(technology_table) - world._static_nodes)
|
tech_names: List[str] = list(set(custom_technologies) - world._static_nodes)
|
||||||
tech_names.sort()
|
tech_names.sort()
|
||||||
world.random.shuffle(tech_names)
|
world.random.shuffle(tech_names)
|
||||||
tech_names.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients))
|
tech_names.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients))
|
||||||
previous_slice = []
|
previous_slice = []
|
||||||
while len(tech_names) > slice_size:
|
while len(tech_names) > slice_size:
|
||||||
slice = tech_names[:slice_size]
|
slice = tech_names[:slice_size]
|
||||||
|
@ -71,14 +72,14 @@ def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]:
|
||||||
elif layout == TechTreeLayout.option_funnel:
|
elif layout == TechTreeLayout.option_funnel:
|
||||||
|
|
||||||
|
|
||||||
tech_names: List[str] = list(set(technology_table) - world._static_nodes)
|
tech_names: List[str] = list(set(custom_technologies) - world._static_nodes)
|
||||||
# find largest inverse pyramid
|
# find largest inverse pyramid
|
||||||
# https://www.wolframalpha.com/input/?i=x+=+1/2+(n++++1)+(2++++n)+solve+for+n
|
# https://www.wolframalpha.com/input/?i=x+=+1/2+(n++++1)+(2++++n)+solve+for+n
|
||||||
import math
|
import math
|
||||||
slice_size = int(0.5*(math.sqrt(8*len(tech_names)+1)-3))
|
slice_size = int(0.5*(math.sqrt(8*len(tech_names)+1)-3))
|
||||||
tech_names.sort()
|
tech_names.sort()
|
||||||
world.random.shuffle(tech_names)
|
world.random.shuffle(tech_names)
|
||||||
tech_names.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients))
|
tech_names.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients))
|
||||||
previous_slice = []
|
previous_slice = []
|
||||||
while slice_size:
|
while slice_size:
|
||||||
slice = tech_names[:slice_size]
|
slice = tech_names[:slice_size]
|
||||||
|
|
|
@ -13,31 +13,28 @@ with open(source_file) as f:
|
||||||
with open(recipe_source_file) as f:
|
with open(recipe_source_file) as f:
|
||||||
raw_recipes = json.load(f)
|
raw_recipes = json.load(f)
|
||||||
tech_table = {}
|
tech_table = {}
|
||||||
technology_table:Dict[str, Technology] = {}
|
technology_table: Dict[str, Technology] = {}
|
||||||
|
|
||||||
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, factorio_id):
|
||||||
self.name = name
|
self.name = name
|
||||||
global factorio_id
|
|
||||||
self.factorio_id = factorio_id
|
self.factorio_id = factorio_id
|
||||||
factorio_id += 1
|
|
||||||
self.ingredients = ingredients
|
self.ingredients = ingredients
|
||||||
|
|
||||||
def build_rule(self, allowed_packs, player: int):
|
def build_rule(self, player: int):
|
||||||
logging.debug(f"Building rules for {self.name}")
|
logging.debug(f"Building rules for {self.name}")
|
||||||
ingredient_rules = []
|
ingredient_rules = []
|
||||||
for ingredient in self.ingredients:
|
for ingredient in self.ingredients:
|
||||||
if ingredient in allowed_packs:
|
logging.debug(f"Building rules for ingredient {ingredient}")
|
||||||
logging.debug(f"Building rules for ingredient {ingredient}")
|
technologies = required_technologies[ingredient] # technologies that unlock the recipes
|
||||||
technologies = required_technologies[ingredient] # technologies that unlock the recipes
|
if technologies:
|
||||||
if technologies:
|
logging.debug(f"Required Technologies: {technologies}")
|
||||||
logging.debug(f"Required Technologies: {technologies}")
|
ingredient_rules.append(
|
||||||
ingredient_rules.append(
|
lambda state, technologies=technologies: all(state.has(technology.name, player)
|
||||||
lambda state, technologies=technologies: all(state.has(technology.name, player)
|
for technology in technologies))
|
||||||
for technology in technologies))
|
|
||||||
if ingredient_rules:
|
if ingredient_rules:
|
||||||
ingredient_rules = frozenset(ingredient_rules)
|
ingredient_rules = frozenset(ingredient_rules)
|
||||||
return lambda state: all(rule(state) for rule in ingredient_rules)
|
return lambda state: all(rule(state) for rule in ingredient_rules)
|
||||||
|
@ -58,6 +55,23 @@ class Technology(): # maybe make subclass of Location?
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"{self.__class__.__name__}({self.name})"
|
return f"{self.__class__.__name__}({self.name})"
|
||||||
|
|
||||||
|
def get_custom(self, world, allowed_packs: Set[str], player: int) -> CustomTechnology:
|
||||||
|
return CustomTechnology(self, world, allowed_packs, player)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomTechnology(Technology):
|
||||||
|
"""A particularly configured Technology for a world."""
|
||||||
|
|
||||||
|
def __init__(self, origin: Technology, world, allowed_packs: Set[str], player: int):
|
||||||
|
ingredients = origin.ingredients & allowed_packs
|
||||||
|
self.player = player
|
||||||
|
if world.random_tech_ingredients[player]:
|
||||||
|
ingredients = list(ingredients)
|
||||||
|
ingredients.sort() # deterministic sample
|
||||||
|
ingredients = world.random.sample(ingredients, world.random.randint(1, len(ingredients)))
|
||||||
|
super(CustomTechnology, self).__init__(origin.name, ingredients, origin.factorio_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Recipe():
|
class Recipe():
|
||||||
def __init__(self, name, category, ingredients, products):
|
def __init__(self, name, category, ingredients, products):
|
||||||
|
@ -80,7 +94,8 @@ for technology_name in sorted(raw):
|
||||||
data = raw[technology_name]
|
data = raw[technology_name]
|
||||||
factorio_id += 1
|
factorio_id += 1
|
||||||
current_ingredients = set(data["ingredients"])
|
current_ingredients = set(data["ingredients"])
|
||||||
technology = Technology(technology_name, current_ingredients)
|
technology = Technology(technology_name, current_ingredients, factorio_id)
|
||||||
|
factorio_id += 1
|
||||||
tech_table[technology_name] = technology.factorio_id
|
tech_table[technology_name] = technology.factorio_id
|
||||||
technology_table[technology_name] = technology
|
technology_table[technology_name] = technology
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,8 @@ def gen_factorio(world: MultiWorld, player: int):
|
||||||
loc.event = tech_item.advancement
|
loc.event = tech_item.advancement
|
||||||
else:
|
else:
|
||||||
world.itempool.append(tech_item)
|
world.itempool.append(tech_item)
|
||||||
set_rules(world, player)
|
world.custom_data[player]["custom_technologies"] = custom_technologies = set_custom_technologies(world, player)
|
||||||
|
set_rules(world, player, custom_technologies)
|
||||||
|
|
||||||
|
|
||||||
def factorio_create_regions(world: MultiWorld, player: int):
|
def factorio_create_regions(world: MultiWorld, player: int):
|
||||||
|
@ -31,22 +32,30 @@ def factorio_create_regions(world: MultiWorld, player: int):
|
||||||
crash.connect(nauvis)
|
crash.connect(nauvis)
|
||||||
world.regions += [menu, nauvis]
|
world.regions += [menu, nauvis]
|
||||||
|
|
||||||
|
def set_custom_technologies(world: MultiWorld, player: int):
|
||||||
|
custom_technologies = {}
|
||||||
|
world_custom = getattr(world, "_custom_technologies", {})
|
||||||
|
world_custom[player] = custom_technologies
|
||||||
|
world._custom_technologies = world_custom
|
||||||
|
allowed_packs = world.max_science_pack[player].get_allowed_packs()
|
||||||
|
for technology_name, technology in technology_table.items():
|
||||||
|
custom_technologies[technology_name] = technology.get_custom(world, allowed_packs, player)
|
||||||
|
return custom_technologies
|
||||||
|
|
||||||
def set_rules(world: MultiWorld, player: int):
|
def set_rules(world: MultiWorld, player: int, custom_technologies):
|
||||||
shapes = get_shapes(world, player)
|
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()
|
|
||||||
for tech_name, technology in technology_table.items():
|
for tech_name, technology in custom_technologies.items():
|
||||||
# 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(player))
|
||||||
prequisites = shapes.get(tech_name)
|
prequisites = shapes.get(tech_name)
|
||||||
if prequisites:
|
if prequisites:
|
||||||
locations = {world.get_location(requisite, player) for requisite in prequisites}
|
locations = {world.get_location(requisite, player) for requisite in prequisites}
|
||||||
Rules.add_rule(location, lambda state,
|
Rules.add_rule(location, lambda state,
|
||||||
locations=locations: all(state.can_reach(loc) for loc in locations))
|
locations=locations: all(state.can_reach(loc) for loc in locations))
|
||||||
|
|
||||||
# get all technologies
|
# get all science pack technologies (but not the ability to craft them)
|
||||||
world.completion_condition[player] = lambda state: all(state.has(technology, player)
|
world.completion_condition[player] = lambda state: all(state.has(technology, player)
|
||||||
for technology in advancement_technologies)
|
for technology in advancement_technologies)
|
||||||
|
|
Loading…
Reference in New Issue