From 2c4c8991794ed9ffa40b276a02ce21d4fa75777d Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 11 Jun 2021 18:02:48 +0200 Subject: [PATCH] move more Factorio stuff around --- BaseClasses.py | 15 +++- Main.py | 7 +- .../mod_template/data-final-fixes.lua | 2 +- worlds/AutoWorld.py | 21 +++-- worlds/factorio/Mod.py | 2 +- worlds/factorio/Shapes.py | 6 +- worlds/factorio/__init__.py | 82 +++++++++---------- 7 files changed, 75 insertions(+), 60 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 1b4cfcc7..ebb43e87 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -23,6 +23,7 @@ class MultiWorld(): plando_items: List[PlandoItem] plando_connections: List[PlandoConnection] er_seeds: Dict[int, str] + worlds: Dict[int, "AutoWorld.World"] class AttributeProxy(): def __init__(self, rule): @@ -32,8 +33,6 @@ class MultiWorld(): return self.rule(player) def __init__(self, players: int): - from worlds import AutoWorld - self.random = random.Random() # world-local random state is saved for multiple generations running concurrently self.players = players self.teams = 1 @@ -140,9 +139,17 @@ class MultiWorld(): set_player_attr('completion_condition', lambda state: True) self.custom_data = {} self.worlds = {} - for player in range(1, players+1): + + + def set_options(self, args): + import Options + from worlds import AutoWorld + for option_set in Options.option_sets: + for option in option_set: + setattr(self, option, getattr(args, option, {})) + for player in self.player_ids: self.custom_data[player] = {} - self.worlds[player] = AutoWorld.AutoWorldRegister.world_types[self.game[player]](player) + self.worlds[player] = AutoWorld.AutoWorldRegister.world_types[self.game[player]](self, player) def secure(self): self.random = secrets.SystemRandom() diff --git a/Main.py b/Main.py index 9b957f2f..4a7a66d0 100644 --- a/Main.py +++ b/Main.py @@ -124,10 +124,7 @@ def main(args, seed=None): world.restrict_dungeon_item_on_boss = args.restrict_dungeon_item_on_boss.copy() world.required_medallions = args.required_medallions.copy() world.game = args.game.copy() - import Options - for option_set in Options.option_sets: - for option in option_set: - setattr(world, option, getattr(args, option, {})) + world.set_options(args) world.glitch_triforce = args.glitch_triforce # This is enabled/disabled globally, no per player option. world.slot_seeds = {player: random.Random(world.random.randint(0, 999999999)) for player in @@ -262,6 +259,8 @@ def main(args, seed=None): for player in world.player_ids: locality_rules(world, player) + AutoWorld.call_all(world, "set_rules") + for player in world.alttp_player_ids: set_rules(world, player) diff --git a/data/factorio/mod_template/data-final-fixes.lua b/data/factorio/mod_template/data-final-fixes.lua index d6a96ee1..335eca34 100644 --- a/data/factorio/mod_template/data-final-fixes.lua +++ b/data/factorio/mod_template/data-final-fixes.lua @@ -8,7 +8,7 @@ local technologies = data.raw["technology"] local original_tech local new_tree_copy allowed_ingredients = {} -{%- for tech_name, technology in custom_data["custom_technologies"].items() %} +{%- for tech_name, technology in custom_technologies.items() %} allowed_ingredients["{{ tech_name }}"] = { {%- for ingredient in technology.ingredients %} ["{{ingredient}}"] = 1, diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index c5644bcd..d27b324b 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -1,5 +1,6 @@ from BaseClasses import MultiWorld + class AutoWorldRegister(type): world_types = {} @@ -12,7 +13,7 @@ class AutoWorldRegister(type): def call_single(world: MultiWorld, method_name: str, player: int): method = getattr(world.worlds[player], method_name) - return method(world, player) + return method() def call_all(world: MultiWorld, method_name: str): @@ -24,14 +25,22 @@ class World(metaclass=AutoWorldRegister): """A World object encompasses a game's Items, Locations, Rules and additional data or functionality required. A Game should have its own subclass of World in which it defines the required data structures.""" - def __init__(self, player: int): - self.player = int + world: MultiWorld + player: int - def generate_basic(self, world: MultiWorld, player: int): + def __init__(self, world: MultiWorld, player: int): + self.world = world + self.player = player + + # overwritable methods that get called by Main.py + def generate_basic(self): pass - def generate_output(self, world: MultiWorld, player: int): + def set_rules(self): pass - def create_regions(self, world: MultiWorld, player: int): + def create_regions(self): + pass + + def generate_output(self): pass diff --git a/worlds/factorio/Mod.py b/worlds/factorio/Mod.py index b438cb9c..7fb6e17b 100644 --- a/worlds/factorio/Mod.py +++ b/worlds/factorio/Mod.py @@ -69,7 +69,7 @@ def generate_mod(world: MultiWorld, player: int): 6: 10}[world.tech_cost[player].value] 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(), - "tech_cost_scale": tech_cost, "custom_data": world.custom_data[player], + "tech_cost_scale": tech_cost, "custom_technologies": world.worlds[player].custom_technologies, "tech_tree_layout_prerequisites": world.tech_tree_layout_prerequisites[player], "rocket_recipe": rocket_recipes[world.max_science_pack[player].value], "slot_name": world.player_names[player][0], "seed_name": world.seed_name, diff --git a/worlds/factorio/Shapes.py b/worlds/factorio/Shapes.py index fea4e848..617ba163 100644 --- a/worlds/factorio/Shapes.py +++ b/worlds/factorio/Shapes.py @@ -11,10 +11,12 @@ funnel_slice_sizes = {TechTreeLayout.option_small_funnels: 6, TechTreeLayout.option_medium_funnels: 10, TechTreeLayout.option_large_funnels: 15} -def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]: +def get_shapes(factorio_world) -> Dict[str, List[str]]: + world = factorio_world.world + player = factorio_world.player prerequisites: Dict[str, Set[str]] = {} layout = world.tech_tree_layout[player].value - custom_technologies = world.custom_data[player]["custom_technologies"] + custom_technologies = factorio_world.custom_technologies tech_names: List[str] = list(set(custom_technologies) - world.worlds[player].static_nodes) tech_names.sort() world.random.shuffle(tech_names) diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py index a1e806bb..e6858a21 100644 --- a/worlds/factorio/__init__.py +++ b/worlds/factorio/__init__.py @@ -11,28 +11,28 @@ class Factorio(World): game: str = "Factorio" static_nodes = {"automation", "logistics"} - def generate_basic(self, world: MultiWorld, player: int): - victory_tech_names = get_rocket_requirements(frozenset(rocket_recipes[world.max_science_pack[player].value])) + def generate_basic(self): + victory_tech_names = get_rocket_requirements( + frozenset(rocket_recipes[self.world.max_science_pack[self.player].value])) for tech_name, tech_id in tech_table.items(): tech_item = Item(tech_name, tech_name in advancement_technologies or tech_name in victory_tech_names, - tech_id, player) + tech_id, self.player) tech_item.game = "Factorio" if tech_name in self.static_nodes: - world.get_location(tech_name, player).place_locked_item(tech_item) + self.world.get_location(tech_name, self.player).place_locked_item(tech_item) else: - world.itempool.append(tech_item) - world.custom_data[player]["custom_technologies"] = custom_technologies = set_custom_technologies(world, player) - set_rules(world, player, custom_technologies) + self.world.itempool.append(tech_item) - def generate_output(self, world: MultiWorld, player: int): - generate_mod(world, player) + def generate_output(self): + generate_mod(self.world, self.player) - def create_regions(self, world: MultiWorld, player: int): + def create_regions(self): + player = self.player 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 + nauvis.world = menu.world = self.world for tech_name, tech_id in tech_table.items(): tech = Location(player, tech_name, tech_id, nauvis) @@ -41,49 +41,47 @@ class Factorio(World): location = Location(player, "Rocket Launch", None, nauvis) nauvis.locations.append(location) event = Item("Victory", True, None, player) - world.push_item(location, event, False) + self.world.push_item(location, event, False) location.event = location.locked = True for ingredient in all_ingredient_names: location = Location(player, f"Automate {ingredient}", None, nauvis) nauvis.locations.append(location) event = Item(f"Automated {ingredient}", True, None, player) - world.push_item(location, event, False) + self.world.push_item(location, event, False) location.event = location.locked = True crash.connect(nauvis) - world.regions += [menu, nauvis] + self.world.regions += [menu, nauvis] + def set_rules(self): + world = self.world + player = self.player + self.custom_technologies = set_custom_technologies(self.world, self.player) + shapes = get_shapes(self) + if world.logic[player] != 'nologic': + from worlds.generic import Rules + for ingredient in all_ingredient_names: + location = world.get_location(f"Automate {ingredient}", player) + location.access_rule = lambda state, ingredient=ingredient: \ + all(state.has(technology.name, player) for technology in required_technologies[ingredient]) + for tech_name, technology in self.custom_technologies.items(): + location = world.get_location(tech_name, player) + Rules.set_rule(location, technology.build_rule(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 science pack technologies (but not the ability to craft them) + victory_tech_names = get_rocket_requirements(frozenset(rocket_recipes[world.max_science_pack[player].value])) + world.get_location("Rocket Launch", player).access_rule = lambda state: all(state.has(technology, player) + for technology in + victory_tech_names) + + world.completion_condition[player] = lambda state: state.has('Victory', player) 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, custom_technologies): - shapes = get_shapes(world, player) - if world.logic[player] != 'nologic': - from worlds.generic import Rules - for ingredient in all_ingredient_names: - location = world.get_location(f"Automate {ingredient}", player) - location.access_rule = lambda state, ingredient=ingredient: \ - all(state.has(technology.name, player) for technology in required_technologies[ingredient]) - for tech_name, technology in custom_technologies.items(): - location = world.get_location(tech_name, player) - Rules.set_rule(location, technology.build_rule(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 science pack technologies (but not the ability to craft them) - victory_tech_names = get_rocket_requirements(frozenset(rocket_recipes[world.max_science_pack[player].value])) - world.get_location("Rocket Launch", player).access_rule = lambda state: all(state.has(technology, player) - for technology in - victory_tech_names) - - world.completion_condition[player] = lambda state: state.has('Victory', player)