Factorio: add Traps
This commit is contained in:
		
							parent
							
								
									16701249b4
								
							
						
					
					
						commit
						9408557f03
					
				|  | @ -16,9 +16,9 @@ from MultiServer import mark_raw | ||||||
| 
 | 
 | ||||||
| import Utils | import Utils | ||||||
| import random | import random | ||||||
| from NetUtils import RawJSONtoTextParser, NetworkItem, ClientStatus, JSONtoTextParser, JSONMessagePart | from NetUtils import NetworkItem, ClientStatus, JSONtoTextParser, JSONMessagePart | ||||||
| 
 | 
 | ||||||
| from worlds.factorio.Technologies import lookup_id_to_name | from worlds.factorio import Factorio | ||||||
| 
 | 
 | ||||||
| os.makedirs("logs", exist_ok=True) | os.makedirs("logs", exist_ok=True) | ||||||
| 
 | 
 | ||||||
|  | @ -196,10 +196,10 @@ async def factorio_server_watcher(ctx: FactorioContext): | ||||||
|                     transfer_item: NetworkItem = ctx.items_received[ctx.send_index] |                     transfer_item: NetworkItem = ctx.items_received[ctx.send_index] | ||||||
|                     item_id = transfer_item.item |                     item_id = transfer_item.item | ||||||
|                     player_name = ctx.player_names[transfer_item.player] |                     player_name = ctx.player_names[transfer_item.player] | ||||||
|                     if item_id not in lookup_id_to_name: |                     if item_id not in Factorio.item_id_to_name: | ||||||
|                         logging.error(f"Cannot send unknown item ID: {item_id}") |                         factorio_server_logger.error(f"Cannot send unknown item ID: {item_id}") | ||||||
|                     else: |                     else: | ||||||
|                         item_name = lookup_id_to_name[item_id] |                         item_name = Factorio.item_id_to_name[item_id] | ||||||
|                         factorio_server_logger.info(f"Sending {item_name} to Nauvis from {player_name}.") |                         factorio_server_logger.info(f"Sending {item_name} to Nauvis from {player_name}.") | ||||||
|                         ctx.rcon_client.send_command(f'/ap-get-technology {item_name}\t{ctx.send_index}\t{player_name}') |                         ctx.rcon_client.send_command(f'/ap-get-technology {item_name}\t{ctx.send_index}\t{player_name}') | ||||||
|                     ctx.send_index += 1 |                     ctx.send_index += 1 | ||||||
|  |  | ||||||
|  | @ -117,6 +117,40 @@ Factorio: | ||||||
|   starting_items: |   starting_items: | ||||||
|     burner-mining-drill: 19 |     burner-mining-drill: 19 | ||||||
|     stone-furnace: 19 |     stone-furnace: 19 | ||||||
|  |   # Note: Total amount of traps cannot exceed 4, if the sum of them is higher it will get automatically capped. | ||||||
|  |   evolution_traps: | ||||||
|  |     # Trap items that when received increase the enemy evolution. | ||||||
|  |     0: 1 | ||||||
|  |     1: 0 | ||||||
|  |     2: 0 | ||||||
|  |     3: 0 | ||||||
|  |     4: 0 | ||||||
|  |     random: 0 | ||||||
|  |     random-low: 0 | ||||||
|  |     random-middle: 0 | ||||||
|  |     random-high: 0 | ||||||
|  |   evolution_trap_increase: | ||||||
|  |     # If present, % increase of Evolution with each trap received. | ||||||
|  |     5: 0 | ||||||
|  |     10: 1 | ||||||
|  |     15: 0 | ||||||
|  |     20: 0 | ||||||
|  |     100: 0 | ||||||
|  |     random: 0 | ||||||
|  |     random-low: 0 | ||||||
|  |     random-middle: 0 | ||||||
|  |     random-high: 0 | ||||||
|  |   attack_traps: | ||||||
|  |     # Trap items that when received trigger an attack on your base. | ||||||
|  |     0: 1 | ||||||
|  |     1: 0 | ||||||
|  |     2: 0 | ||||||
|  |     3: 0 | ||||||
|  |     4: 0 | ||||||
|  |     random: 0 | ||||||
|  |     random-low: 0 | ||||||
|  |     random-middle: 0 | ||||||
|  |     random-high: 0 | ||||||
|   world_gen: |   world_gen: | ||||||
|     # frequency, size, richness, terrain segmentation, starting area and water are all of https://wiki.factorio.com/Types/MapGenSize |     # frequency, size, richness, terrain segmentation, starting area and water are all of https://wiki.factorio.com/Types/MapGenSize | ||||||
|     # inverse of water scale |     # inverse of water scale | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
| import typing | import typing | ||||||
| 
 | 
 | ||||||
| from Options import Choice, OptionDict, Option, DefaultOnToggle | from Options import Choice, OptionDict, Option, DefaultOnToggle, Range | ||||||
| from schema import Schema, Optional, And, Or | from schema import Schema, Optional, And, Or | ||||||
| 
 | 
 | ||||||
| # schema helpers | # schema helpers | ||||||
|  | @ -125,6 +125,27 @@ class FactorioStartItems(OptionDict): | ||||||
|     default = {"burner-mining-drill": 19, "stone-furnace": 19} |     default = {"burner-mining-drill": 19, "stone-furnace": 19} | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class TrapCount(Range): | ||||||
|  |     range_end = 4 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class AttackTrapCount(TrapCount): | ||||||
|  |     """Trap items that when received trigger an attack on your base.""" | ||||||
|  |     displayname = "Attack Traps" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class EvolutionTrapCount(TrapCount): | ||||||
|  |     """Trap items that when received increase the enemy evolution.""" | ||||||
|  |     displayname = "Evolution Traps" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class EvolutionTrapIncrease(Range): | ||||||
|  |     displayname = "Evolution Trap % Effect" | ||||||
|  |     range_start = 1 | ||||||
|  |     default = 10 | ||||||
|  |     range_end = 100 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class FactorioWorldGen(OptionDict): | class FactorioWorldGen(OptionDict): | ||||||
|     displayname = "World Generation" |     displayname = "World Generation" | ||||||
|     # FIXME: do we want default be a rando-optimized default or in-game DS? |     # FIXME: do we want default be a rando-optimized default or in-game DS? | ||||||
|  | @ -257,9 +278,11 @@ class FactorioWorldGen(OptionDict): | ||||||
|         else: |         else: | ||||||
|             raise NotImplementedError(f"Cannot Convert from non-dictionary, got {type(data)}") |             raise NotImplementedError(f"Cannot Convert from non-dictionary, got {type(data)}") | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class ImportedBlueprint(DefaultOnToggle): | class ImportedBlueprint(DefaultOnToggle): | ||||||
|     displayname = "Blueprints" |     displayname = "Blueprints" | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| factorio_options: typing.Dict[str, type(Option)] = { | factorio_options: typing.Dict[str, type(Option)] = { | ||||||
|     "max_science_pack": MaxSciencePack, |     "max_science_pack": MaxSciencePack, | ||||||
|     "tech_tree_layout": TechTreeLayout, |     "tech_tree_layout": TechTreeLayout, | ||||||
|  | @ -272,5 +295,8 @@ factorio_options: typing.Dict[str, type(Option)] = { | ||||||
|     "recipe_ingredients": RecipeIngredients, |     "recipe_ingredients": RecipeIngredients, | ||||||
|     "imported_blueprints": ImportedBlueprint, |     "imported_blueprints": ImportedBlueprint, | ||||||
|     "world_gen": FactorioWorldGen, |     "world_gen": FactorioWorldGen, | ||||||
|     "progressive": Progressive |     "progressive": Progressive, | ||||||
|  |     "evolution_traps": EvolutionTrapCount, | ||||||
|  |     "attack_traps": AttackTrapCount, | ||||||
|  |     "evolution_trap_increase": EvolutionTrapIncrease, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,11 +8,10 @@ import string | ||||||
| 
 | 
 | ||||||
| import Utils | import Utils | ||||||
| import logging | import logging | ||||||
| import functools |  | ||||||
| 
 | 
 | ||||||
| from . import Options | from . import Options | ||||||
| 
 | 
 | ||||||
| factorio_id = 2 ** 17 | factorio_id = factorio_base_id = 2 ** 17 | ||||||
| source_folder = os.path.join(os.path.dirname(__file__), "data") | source_folder = os.path.join(os.path.dirname(__file__), "data") | ||||||
| 
 | 
 | ||||||
| with open(os.path.join(source_folder, "techs.json")) as f: | with open(os.path.join(source_folder, "techs.json")) as f: | ||||||
|  | @ -38,11 +37,24 @@ class FactorioElement(): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Technology(FactorioElement):  # maybe make subclass of Location? | class Technology(FactorioElement):  # maybe make subclass of Location? | ||||||
|     def __init__(self, name: str, ingredients: Set[str], factorio_id: int, progressive: Tuple[str] = ()): |     has_modifier: bool | ||||||
|  |     factorio_id: int | ||||||
|  |     name: str | ||||||
|  |     ingredients: Set[str] | ||||||
|  |     progressive: Tuple[str] | ||||||
|  |     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): | ||||||
|         self.name = name |         self.name = name | ||||||
|         self.factorio_id = factorio_id |         self.factorio_id = factorio_id | ||||||
|         self.ingredients = ingredients |         self.ingredients = ingredients | ||||||
|         self.progressive = progressive |         self.progressive = progressive | ||||||
|  |         self.has_modifier = has_modifier | ||||||
|  |         if unlocks: | ||||||
|  |             self.unlocks = unlocks | ||||||
|  |         else: | ||||||
|  |             self.unlocks = set() | ||||||
| 
 | 
 | ||||||
|     def build_rule(self, player: int): |     def build_rule(self, player: int): | ||||||
|         logging.debug(f"Building rules for {self.name}") |         logging.debug(f"Building rules for {self.name}") | ||||||
|  | @ -63,6 +75,9 @@ class Technology(FactorioElement):  # maybe make subclass of Location? | ||||||
|     def get_custom(self, world, allowed_packs: Set[str], player: int) -> CustomTechnology: |     def get_custom(self, world, allowed_packs: Set[str], player: int) -> CustomTechnology: | ||||||
|         return CustomTechnology(self, world, allowed_packs, player) |         return CustomTechnology(self, world, allowed_packs, player) | ||||||
| 
 | 
 | ||||||
|  |     def useful(self) -> bool: | ||||||
|  |         return self.has_modifier or self.unlocks | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class CustomTechnology(Technology): | class CustomTechnology(Technology): | ||||||
|     """A particularly configured Technology for a world.""" |     """A particularly configured Technology for a world.""" | ||||||
|  | @ -114,7 +129,7 @@ class Recipe(FactorioElement): | ||||||
|     @property |     @property | ||||||
|     def rel_cost(self) -> float: |     def rel_cost(self) -> float: | ||||||
|         ingredients = sum(self.ingredients.values()) |         ingredients = sum(self.ingredients.values()) | ||||||
|         return min(ingredients/amount for product, amount in self.products.items()) |         return min(ingredients / amount for product, amount in self.products.items()) | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def base_cost(self) -> Dict[str, int]: |     def base_cost(self) -> Dict[str, int]: | ||||||
|  | @ -122,7 +137,8 @@ class Recipe(FactorioElement): | ||||||
|         for ingredient, cost in self.ingredients.items(): |         for ingredient, cost in self.ingredients.items(): | ||||||
|             if ingredient in all_product_sources: |             if ingredient in all_product_sources: | ||||||
|                 for recipe in all_product_sources[ingredient]: |                 for recipe in all_product_sources[ingredient]: | ||||||
|                     ingredients.update({name: amount*cost/recipe.products[ingredient] for name, amount in recipe.base_cost.items()}) |                     ingredients.update({name: amount * cost / recipe.products[ingredient] for name, amount in | ||||||
|  |                                         recipe.base_cost.items()}) | ||||||
|             else: |             else: | ||||||
|                 ingredients[ingredient] += cost |                 ingredients[ingredient] += cost | ||||||
|         return ingredients |         return ingredients | ||||||
|  | @ -134,39 +150,40 @@ class Recipe(FactorioElement): | ||||||
|         total_energy = self.energy |         total_energy = self.energy | ||||||
|         for ingredient, cost in self.ingredients.items(): |         for ingredient, cost in self.ingredients.items(): | ||||||
|             if ingredient in all_product_sources: |             if ingredient in all_product_sources: | ||||||
|                 for ingredient_recipe in all_product_sources[ingredient]: # FIXME: this may select the wrong recipe |                 for ingredient_recipe in all_product_sources[ingredient]:  # FIXME: this may select the wrong recipe | ||||||
|                     craft_count = max((n for name, n in ingredient_recipe.products.items() if name == ingredient)) |                     craft_count = max((n for name, n in ingredient_recipe.products.items() if name == ingredient)) | ||||||
|                     total_energy += ingredient_recipe.total_energy / craft_count * cost |                     total_energy += ingredient_recipe.total_energy / craft_count * cost | ||||||
|                     break |                     break | ||||||
|         return total_energy |         return total_energy | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class Machine(FactorioElement): | class Machine(FactorioElement): | ||||||
|     def __init__(self, name, categories): |     def __init__(self, name, categories): | ||||||
|         self.name: str = name |         self.name: str = name | ||||||
|         self.categories: set = categories |         self.categories: set = categories | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | recipe_sources: Dict[str, Set[str]] = {}  # recipe_name -> technology source | ||||||
|  | 
 | ||||||
| # 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] | ||||||
|     current_ingredients = set(data["ingredients"]) |     current_ingredients = set(data["ingredients"]) | ||||||
|     technology = Technology(technology_name, current_ingredients, factorio_id) |     technology = Technology(technology_name, current_ingredients, factorio_id, | ||||||
|  |                             has_modifier=data["has_modifier"], unlocks=set(data["unlocks"])) | ||||||
|     factorio_id += 1 |     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 | ||||||
| 
 |     for recipe_name in technology.unlocks: | ||||||
| recipe_sources: Dict[str, str] = {}  # recipe_name -> technology source |         recipe_sources.setdefault(recipe_name, set()).add(technology_name) | ||||||
| 
 |  | ||||||
| for technology, data in raw.items(): |  | ||||||
|     for recipe_name in data["unlocks"]: |  | ||||||
|         recipe_sources.setdefault(recipe_name, set()).add(technology) |  | ||||||
| 
 | 
 | ||||||
| del (raw) | del (raw) | ||||||
| 
 | 
 | ||||||
| recipes = {} | recipes = {} | ||||||
| all_product_sources: Dict[str, Set[Recipe]] = {"character": set()} | all_product_sources: Dict[str, Set[Recipe]] = {"character": set()} | ||||||
| # add uranium mining to logic graph. TODO: add to automatic extractor for mod support | # add uranium mining to logic graph. TODO: add to automatic extractor for mod support | ||||||
| raw_recipes["uranium-ore"] = {"ingredients": {"sulfuric-acid": 1}, "products": {"uranium-ore": 1}, "category": "mining", "energy": 2} | raw_recipes["uranium-ore"] = {"ingredients": {"sulfuric-acid": 1}, "products": {"uranium-ore": 1}, "category": "mining", | ||||||
|  |                               "energy": 2} | ||||||
| 
 | 
 | ||||||
| for recipe_name, recipe_data in raw_recipes.items(): | for recipe_name, recipe_data in raw_recipes.items(): | ||||||
|     # example: |     # example: | ||||||
|  | @ -193,7 +210,7 @@ for name, categories in raw_machines.items(): | ||||||
| # add electric mining drill as a crafting machine to resolve uranium-ore | # add electric mining drill as a crafting machine to resolve uranium-ore | ||||||
| machines["electric-mining-drill"] = Machine("electric-mining-drill", {"mining"}) | machines["electric-mining-drill"] = Machine("electric-mining-drill", {"mining"}) | ||||||
| machines["assembling-machine-1"].categories.add("crafting-with-fluid")  # mod enables this | machines["assembling-machine-1"].categories.add("crafting-with-fluid")  # mod enables this | ||||||
| machines["character"].categories.add("basic-crafting") # somehow this is implied and not exported | machines["character"].categories.add("basic-crafting")  # somehow this is implied and not exported | ||||||
| del (raw_machines) | del (raw_machines) | ||||||
| 
 | 
 | ||||||
| # build requirements graph for all technology ingredients | # build requirements graph for all technology ingredients | ||||||
|  | @ -265,10 +282,9 @@ for category_name, machine_name in machine_per_category.items(): | ||||||
|     techs |= recursively_get_unlocking_technologies(machine_name) |     techs |= recursively_get_unlocking_technologies(machine_name) | ||||||
|     required_category_technologies[category_name] = frozenset(techs) |     required_category_technologies[category_name] = frozenset(techs) | ||||||
| 
 | 
 | ||||||
| required_technologies: Dict[str, FrozenSet[Technology]] = Utils.KeyedDefaultDict(lambda ingredient_name : frozenset( | required_technologies: Dict[str, FrozenSet[Technology]] = Utils.KeyedDefaultDict(lambda ingredient_name: frozenset( | ||||||
|     recursively_get_unlocking_technologies(ingredient_name, unlock_func=unlock))) |     recursively_get_unlocking_technologies(ingredient_name, unlock_func=unlock))) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| advancement_technologies: Set[str] = set() | advancement_technologies: Set[str] = set() | ||||||
| for ingredient_name in all_ingredient_names: | for ingredient_name in all_ingredient_names: | ||||||
|     technologies = required_technologies[ingredient_name] |     technologies = required_technologies[ingredient_name] | ||||||
|  | @ -317,17 +333,17 @@ for tech_name in tech_table: | ||||||
|         progressive_incs.add(tech_name) |         progressive_incs.add(tech_name) | ||||||
| 
 | 
 | ||||||
| for root, progressive in progressive_rows.items(): | for root, progressive in progressive_rows.items(): | ||||||
|     seeking = root[:-1]+str(int(root[-1])+1) |     seeking = root[:-1] + str(int(root[-1]) + 1) | ||||||
|     while seeking in progressive_incs: |     while seeking in progressive_incs: | ||||||
|         progressive.append(seeking) |         progressive.append(seeking) | ||||||
|         progressive_incs.remove(seeking) |         progressive_incs.remove(seeking) | ||||||
|         seeking = seeking[:-1]+str(int(seeking[-1])+1) |         seeking = seeking[:-1] + str(int(seeking[-1]) + 1) | ||||||
| 
 | 
 | ||||||
| # make root entry the progressive name | # make root entry the progressive name | ||||||
| for old_name in set(progressive_rows): | for old_name in set(progressive_rows): | ||||||
|     prog_name = "progressive-" + old_name.rsplit("-", 1)[0] |     prog_name = "progressive-" + old_name.rsplit("-", 1)[0] | ||||||
|     progressive_rows[prog_name] = tuple([old_name] + progressive_rows[old_name]) |     progressive_rows[prog_name] = tuple([old_name] + progressive_rows[old_name]) | ||||||
|     del(progressive_rows[old_name]) |     del (progressive_rows[old_name]) | ||||||
| 
 | 
 | ||||||
| # no -1 start | # no -1 start | ||||||
| base_starts = set() | base_starts = set() | ||||||
|  | @ -336,12 +352,12 @@ for remnant in progressive_incs: | ||||||
|         base_starts.add(remnant[:-2]) |         base_starts.add(remnant[:-2]) | ||||||
| 
 | 
 | ||||||
| for root in base_starts: | for root in base_starts: | ||||||
|     seeking = root+"-2" |     seeking = root + "-2" | ||||||
|     progressive = [root] |     progressive = [root] | ||||||
|     while seeking in progressive_incs: |     while seeking in progressive_incs: | ||||||
|         progressive.append(seeking) |         progressive.append(seeking) | ||||||
|         seeking = seeking[:-1]+str(int(seeking[-1])+1) |         seeking = seeking[:-1] + str(int(seeking[-1]) + 1) | ||||||
|     progressive_rows["progressive-"+root] = tuple(progressive) |     progressive_rows["progressive-" + root] = tuple(progressive) | ||||||
| 
 | 
 | ||||||
| # science packs | # science packs | ||||||
| progressive_rows["progressive-science-pack"] = tuple(Options.MaxSciencePack.get_ordered_science_packs())[1:] | progressive_rows["progressive-science-pack"] = tuple(Options.MaxSciencePack.get_ordered_science_packs())[1:] | ||||||
|  | @ -381,7 +397,6 @@ source_target_mapping: Dict[str, str] = { | ||||||
| for source, target in source_target_mapping.items(): | for source, target in source_target_mapping.items(): | ||||||
|     progressive_rows[target] += progressive_rows[source] |     progressive_rows[target] += progressive_rows[source] | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| base_tech_table = tech_table.copy()  # without progressive techs | base_tech_table = tech_table.copy()  # without progressive techs | ||||||
| base_technology_table = technology_table.copy() | base_technology_table = technology_table.copy() | ||||||
| 
 | 
 | ||||||
|  | @ -393,7 +408,9 @@ for root in sorted_rows: | ||||||
|     assert all(tech in tech_table for tech in progressive) |     assert all(tech in tech_table for tech in progressive) | ||||||
|     factorio_id += 1 |     factorio_id += 1 | ||||||
|     progressive_technology = Technology(root, technology_table[progressive_rows[root][0]].ingredients, factorio_id, |     progressive_technology = Technology(root, technology_table[progressive_rows[root][0]].ingredients, factorio_id, | ||||||
|                                         progressive) |                                         progressive, | ||||||
|  |                                         has_modifier=any(technology_table[tech].has_modifier for tech in progressive), | ||||||
|  |                                         unlocks=any(technology_table[tech].unlocks for tech in progressive)) | ||||||
|     progressive_tech_table[root] = progressive_technology.factorio_id |     progressive_tech_table[root] = progressive_technology.factorio_id | ||||||
|     progressive_technology_table[root] = progressive_technology |     progressive_technology_table[root] = progressive_technology | ||||||
|     if any(tech in advancement_technologies for tech in progressive): |     if any(tech in advancement_technologies for tech in progressive): | ||||||
|  | @ -412,10 +429,13 @@ technology_table.update(progressive_technology_table) | ||||||
| common_tech_table: Dict[str, int] = {tech_name: tech_id for tech_name, tech_id in base_tech_table.items() | common_tech_table: Dict[str, int] = {tech_name: tech_id for tech_name, tech_id in base_tech_table.items() | ||||||
|                                      if tech_name not in progressive_tech_table} |                                      if tech_name not in progressive_tech_table} | ||||||
| 
 | 
 | ||||||
|  | useless_technologies: Set[str] = {tech_name for tech_name in common_tech_table | ||||||
|  |                                   if not technology_table[tech_name].useful()} | ||||||
|  | 
 | ||||||
| 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()} | ||||||
| 
 | 
 | ||||||
| rel_cost = { | rel_cost = { | ||||||
|     "wood" : 10000, |     "wood": 10000, | ||||||
|     "iron-ore": 1, |     "iron-ore": 1, | ||||||
|     "copper-ore": 1, |     "copper-ore": 1, | ||||||
|     "stone": 1, |     "stone": 1, | ||||||
|  | @ -431,6 +451,7 @@ rel_cost = { | ||||||
| blacklist: Set[str] = all_ingredient_names | {"rocket-part", "crude-oil", "water", "sulfuric-acid", "petroleum-gas", | blacklist: Set[str] = all_ingredient_names | {"rocket-part", "crude-oil", "water", "sulfuric-acid", "petroleum-gas", | ||||||
|                                               "light-oil", "heavy-oil", "lubricant", "steam"} |                                               "light-oil", "heavy-oil", "lubricant", "steam"} | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| @Utils.cache_argsless | @Utils.cache_argsless | ||||||
| def get_science_pack_pools() -> Dict[str, Set[str]]: | def get_science_pack_pools() -> Dict[str, Set[str]]: | ||||||
|     def get_estimated_difficulty(recipe: Recipe): |     def get_estimated_difficulty(recipe: Recipe): | ||||||
|  | @ -441,7 +462,6 @@ def get_science_pack_pools() -> Dict[str, Set[str]]: | ||||||
|             cost += rel_cost.get(ingredient_name, 1) * amount |             cost += rel_cost.get(ingredient_name, 1) * amount | ||||||
|         return cost |         return cost | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     science_pack_pools = {} |     science_pack_pools = {} | ||||||
|     already_taken = blacklist.copy() |     already_taken = blacklist.copy() | ||||||
|     current_difficulty = 5 |     current_difficulty = 5 | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ from BaseClasses import Region, Entrance, Location, Item | ||||||
| from .Technologies import base_tech_table, recipe_sources, base_technology_table, advancement_technologies, \ | from .Technologies import base_tech_table, recipe_sources, base_technology_table, advancement_technologies, \ | ||||||
|     all_ingredient_names, all_product_sources, required_technologies, get_rocket_requirements, rocket_recipes, \ |     all_ingredient_names, all_product_sources, required_technologies, get_rocket_requirements, rocket_recipes, \ | ||||||
|     progressive_technology_table, common_tech_table, tech_to_progressive_lookup, progressive_tech_table, \ |     progressive_technology_table, common_tech_table, tech_to_progressive_lookup, progressive_tech_table, \ | ||||||
|     get_science_pack_pools, Recipe, recipes, technology_table, tech_table |     get_science_pack_pools, Recipe, recipes, technology_table, tech_table, factorio_base_id, useless_technologies | ||||||
| from .Shapes import get_shapes | from .Shapes import get_shapes | ||||||
| from .Mod import generate_mod | from .Mod import generate_mod | ||||||
| from .Options import factorio_options, Silo | from .Options import factorio_options, Silo | ||||||
|  | @ -18,35 +18,48 @@ class FactorioItem(Item): | ||||||
|     game = "Factorio" |     game = "Factorio" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | all_items = tech_table.copy() | ||||||
|  | all_items["Attack Trap"] = factorio_base_id - 1 | ||||||
|  | all_items["Evolution Trap"] = factorio_base_id - 2 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class Factorio(World): | class Factorio(World): | ||||||
|     game: str = "Factorio" |     game: str = "Factorio" | ||||||
|     static_nodes = {"automation", "logistics", "rocket-silo"} |     static_nodes = {"automation", "logistics", "rocket-silo"} | ||||||
|     custom_recipes = {} |     custom_recipes = {} | ||||||
|     additional_advancement_technologies = set() |     additional_advancement_technologies = set() | ||||||
| 
 | 
 | ||||||
|     item_name_to_id = tech_table |     item_name_to_id = all_items | ||||||
|     location_name_to_id = base_tech_table |     location_name_to_id = base_tech_table | ||||||
| 
 | 
 | ||||||
|     data_version = 3 |     data_version = 4 | ||||||
| 
 | 
 | ||||||
|     def generate_basic(self): |     def generate_basic(self): | ||||||
|         want_progressives = collections.defaultdict(lambda: self.world.progressive[self.player]. |         player = self.player | ||||||
|  |         want_progressives = collections.defaultdict(lambda: self.world.progressive[player]. | ||||||
|                                                     want_progressives(self.world.random)) |                                                     want_progressives(self.world.random)) | ||||||
|         skip_silo = self.world.silo[self.player].value == Silo.option_spawn |         skip_silo = self.world.silo[player].value == Silo.option_spawn | ||||||
|  |         evolution_traps_wanted = self.world.evolution_traps[player].value | ||||||
|  |         attack_traps_wanted = self.world.attack_traps[player].value | ||||||
|  |         traps_wanted = ["Evolution Trap"] * evolution_traps_wanted + ["Attack Trap"] * attack_traps_wanted | ||||||
|  |         self.world.random.shuffle(traps_wanted) | ||||||
|         for tech_name in base_tech_table: |         for tech_name in base_tech_table: | ||||||
|             if skip_silo and tech_name == "rocket-silo": |             if traps_wanted and tech_name in useless_technologies: | ||||||
|                 continue |                 self.world.itempool.append(self.create_item(traps_wanted.pop())) | ||||||
|             progressive_item_name = tech_to_progressive_lookup.get(tech_name, tech_name) |             elif skip_silo and tech_name == "rocket-silo": | ||||||
|             want_progressive = want_progressives[progressive_item_name] |                 pass | ||||||
|             item_name = progressive_item_name if want_progressive else tech_name |  | ||||||
|             tech_item = self.create_item(item_name) |  | ||||||
|             if tech_name in self.static_nodes: |  | ||||||
|                 self.world.get_location(tech_name, self.player).place_locked_item(tech_item) |  | ||||||
|             else: |             else: | ||||||
|                 self.world.itempool.append(tech_item) |                 progressive_item_name = tech_to_progressive_lookup.get(tech_name, tech_name) | ||||||
|         map_basic_settings = self.world.world_gen[self.player].value["basic"] |                 want_progressive = want_progressives[progressive_item_name] | ||||||
|  |                 item_name = progressive_item_name if want_progressive else tech_name | ||||||
|  |                 tech_item = self.create_item(item_name) | ||||||
|  |                 if tech_name in self.static_nodes: | ||||||
|  |                     self.world.get_location(tech_name, player).place_locked_item(tech_item) | ||||||
|  |                 else: | ||||||
|  |                     self.world.itempool.append(tech_item) | ||||||
|  |         map_basic_settings = self.world.world_gen[player].value["basic"] | ||||||
|         if map_basic_settings.get("seed", None) is None:  # allow seed 0 |         if map_basic_settings.get("seed", None) is None:  # allow seed 0 | ||||||
|             map_basic_settings["seed"] = self.world.slot_seeds[self.player].randint(0, 2**32-1)  # 32 bit uint |             map_basic_settings["seed"] = self.world.slot_seeds[player].randint(0, 2 ** 32 - 1)  # 32 bit uint | ||||||
| 
 | 
 | ||||||
|     generate_output = generate_mod |     generate_output = generate_mod | ||||||
| 
 | 
 | ||||||
|  | @ -263,7 +276,9 @@ class Factorio(World): | ||||||
|         self.additional_advancement_technologies |= prog_add |         self.additional_advancement_technologies |= prog_add | ||||||
| 
 | 
 | ||||||
|     def create_item(self, name: str) -> Item: |     def create_item(self, name: str) -> Item: | ||||||
|         assert name in tech_table |         if name in tech_table: | ||||||
|         return FactorioItem(name, name in advancement_technologies or |             return FactorioItem(name, name in advancement_technologies or | ||||||
|                             name in self.additional_advancement_technologies, |                                 name in self.additional_advancement_technologies, | ||||||
|                             tech_table[name], self.player) |                                 tech_table[name], self.player) | ||||||
|  |         elif name in all_items: | ||||||
|  |             return FactorioItem(name, False, all_items[name], self.player) | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ FREE_SAMPLES = {{ free_samples }} | ||||||
| SLOT_NAME = "{{ slot_name }}" | SLOT_NAME = "{{ slot_name }}" | ||||||
| SEED_NAME = "{{ seed_name }}" | SEED_NAME = "{{ seed_name }}" | ||||||
| FREE_SAMPLE_BLACKLIST = {{ dict_to_lua(free_sample_blacklist) }} | FREE_SAMPLE_BLACKLIST = {{ dict_to_lua(free_sample_blacklist) }} | ||||||
|  | TRAP_EVO_FACTOR = {{ evolution_trap_increase }} / 100 | ||||||
| 
 | 
 | ||||||
| {% if not imported_blueprints -%} | {% if not imported_blueprints -%} | ||||||
| function set_permissions() | function set_permissions() | ||||||
|  | @ -369,15 +370,15 @@ commands.add_command("ap-get-technology", "Grant a technology, used by the Archi | ||||||
|     local tech |     local tech | ||||||
|     local force = game.forces["player"] |     local force = game.forces["player"] | ||||||
|     chunks = split(call.parameter, "\t") |     chunks = split(call.parameter, "\t") | ||||||
|     local tech_name = chunks[1] |     local item_name = chunks[1] | ||||||
|     local index = chunks[2] |     local index = chunks[2] | ||||||
|     local source = chunks[3] or "Archipelago" |     local source = chunks[3] or "Archipelago" | ||||||
|     if progressive_technologies[tech_name] ~= nil then |     if progressive_technologies[item_name] ~= nil then | ||||||
|         if global.index_sync[index] == nil then -- not yet received prog item |         if global.index_sync[index] == nil then -- not yet received prog item | ||||||
|             global.index_sync[index] = tech_name |             global.index_sync[index] = item_name | ||||||
|             local tech_stack = progressive_technologies[tech_name] |             local tech_stack = progressive_technologies[item_name] | ||||||
|             for _, tech_name in ipairs(tech_stack) do |             for _, item_name in ipairs(tech_stack) do | ||||||
|                 tech = force.technologies[tech_name] |                 tech = force.technologies[item_name] | ||||||
|                 if tech.researched ~= true then |                 if tech.researched ~= true then | ||||||
|                     game.print({"", "Received [technology=" .. tech.name .. "] from ", source}) |                     game.print({"", "Received [technology=" .. tech.name .. "] from ", source}) | ||||||
|                     game.play_sound({path="utility/research_completed"}) |                     game.play_sound({path="utility/research_completed"}) | ||||||
|  | @ -386,8 +387,8 @@ commands.add_command("ap-get-technology", "Grant a technology, used by the Archi | ||||||
|                 end |                 end | ||||||
|             end |             end | ||||||
|         end |         end | ||||||
|     elseif force.technologies[tech_name] ~= nil then |     elseif force.technologies[item_name] ~= nil then | ||||||
|         tech = force.technologies[tech_name] |         tech = force.technologies[item_name] | ||||||
|         if tech ~= nil then |         if tech ~= nil then | ||||||
|             if global.index_sync[index] ~= nil and global.index_sync[index] ~= tech then |             if global.index_sync[index] ~= nil and global.index_sync[index] ~= tech then | ||||||
|                 game.print("Warning: Desync Detected. Duplicate/Missing items may occur.") |                 game.print("Warning: Desync Detected. Duplicate/Missing items may occur.") | ||||||
|  | @ -399,8 +400,21 @@ commands.add_command("ap-get-technology", "Grant a technology, used by the Archi | ||||||
|                 tech.researched = true |                 tech.researched = true | ||||||
|             end |             end | ||||||
|         end |         end | ||||||
|  |     elseif item_name == "Attack Trap" then | ||||||
|  |         if global.index_sync[index] == nil then -- not yet received trap | ||||||
|  |             game.print({"", "Received Attack Trap from ", source}) | ||||||
|  |             global.index_sync[index] = item_name | ||||||
|  |             local spawn_position = force.get_spawn_position(game.get_surface(1)) | ||||||
|  |             game.surfaces["nauvis"].build_enemy_base(spawn_position, 25) | ||||||
|  |         end | ||||||
|  |     elseif item_name == "Evolution Trap" then | ||||||
|  |         if global.index_sync[index] == nil then -- not yet received trap | ||||||
|  |             global.index_sync[index] = item_name | ||||||
|  |             game.forces["enemy"].evolution_factor = game.forces["enemy"].evolution_factor + TRAP_EVO_FACTOR | ||||||
|  |             game.print({"", "Received Evolution Trap from ", source, ". New factor:", game.forces["enemy"].evolution_factor}) | ||||||
|  |         end | ||||||
|     else |     else | ||||||
|         game.print("Unknown Technology " .. tech_name) |         game.print("Unknown Item " .. item_name) | ||||||
|     end |     end | ||||||
| end) | end) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
		Reference in New Issue