From cd4cd97e744421f305a982b7aacfc86d10558a78 Mon Sep 17 00:00:00 2001 From: CaitSith2 Date: Wed, 21 Sep 2022 02:57:08 -0700 Subject: [PATCH 1/2] Changes * Dumps mod information. These will get listed as required in AP seeds generated using this data. * extract initially unlocked recipes before unlocking the technologies. * extract fluid box and speed information for crafting machines. * extract mining machines including fluid box information. * extract lab information. * extract offshore-pumps and what fluids are pumped out of the ground for free. (water) * extract boiler information regarding conversion of input fluid to output fluid. (water to steam) * extract burner entities that have at least one burnt result inventory slot. (used-up-uranium-fuel-cell) * Extract fuel value/category for items that have a burnt result (used-up-uranium-fuel-cell) * extract rocket launch products from items (raw-fish, space-science-pack) * extract actual item_stackable information. (note that spidertron-remote prototype incorrectly reports stackable as true unless it is explicitly set false during prototype stage.) * extract temperature and heat capacity information for fluids. (needed for calculating how much water to steam is made per second in a given boiler) * Disallow extraction while AP is running. The PR on AP side in fact marks this mod as incompatible, but this code is for older instances of AP that are NOT marked incompatible. --- archipelago-extractor/control.lua | 219 +++++++++++++++++++++++------- 1 file changed, 168 insertions(+), 51 deletions(-) diff --git a/archipelago-extractor/control.lua b/archipelago-extractor/control.lua index 500190c..ba304b7 100644 --- a/archipelago-extractor/control.lua +++ b/archipelago-extractor/control.lua @@ -1,3 +1,40 @@ +function dumpModInfo() + game.write_file("mods.json", game.table_to_json(game.active_mods), false) + game.print("Exported Mod List") +end + +recipe_data_collection = {} + +function dumpRecipe(recipe, unlocked_at_start) + if recipe_data_collection[recipe] ~= nil then + -- recipe is already known. + return + end + local recipe_data = {} + recipe_data["ingredients"] = {} + recipe_data["products"] = {} + recipe_data["category"] = recipe.category + recipe_data["energy"] = recipe.energy + recipe_data["unlocked_at_start"] = unlocked_at_start + for _, ingredient in pairs(recipe.ingredients) do + recipe_data["ingredients"][ingredient.name] = ingredient.amount + end + for _, product in pairs(recipe.products) do + recipe_data["products"][product.name] = product.amount + end + recipe_data_collection[recipe.name] = recipe_data +end + +function dumpRecipeInfo(force) + data_collection = {} + for recipe_name, recipe in pairs(force.recipes) do + if recipe.enabled then + dumpRecipe(recipe, true) + end + end + game.print("Exported Unlocked Recipe Data") +end + function dumpTechInfo(force) local data_collection = {} @@ -17,6 +54,7 @@ function dumpTechInfo(force) for _, modifier in pairs(tech.effects) do if modifier.type == "unlock-recipe" then table.insert(unlocks, modifier.recipe) + dumpRecipe(force.recipes[modifier.recipe], false) else tech_data["has_modifier"] = true end @@ -28,48 +66,94 @@ function dumpTechInfo(force) tech.researched = true -- enable all available recipes end game.write_file("techs.json", game.table_to_json(data_collection), false) - game.print("Exported Tech Data") + game.write_file("recipes.json", game.table_to_json(recipe_data_collection), false) + game.print("Exported Tech and Recipe Data") end end -function dumpRecipeInfo(force) - data_collection = {} - for recipe_name, recipe in pairs(force.recipes) do - if recipe.enabled then - local recipe_data = {} - recipe_data["ingredients"] = {} - recipe_data["products"] = {} - recipe_data["category"] = recipe.category - recipe_data["energy"] = recipe.energy - for _, ingredient in pairs(recipe.ingredients) do - recipe_data["ingredients"][ingredient.name] = ingredient.amount - end - for _, product in pairs(recipe.products) do - recipe_data["products"][product.name] = product.amount - end - data_collection[recipe_name] = recipe_data - end - end - game.write_file("recipes.json", game.table_to_json(data_collection), false) - game.print("Exported Recipe Data") -end - function dumpMachineInfo() data_collection = {} for _, proto in pairs(game.entity_prototypes) do - if proto.crafting_categories or proto.resource_categories then - data_collection[proto.name] = {} - if proto.crafting_categories then - data_collection[proto.name]["crafting"] = {} - data_collection[proto.name]["crafting"]["speed"] = proto.crafting_speed - data_collection[proto.name]["crafting"]["categories"] = proto.crafting_categories - end - if proto.resource_categories then - data_collection[proto.name]["mining"] = {} - data_collection[proto.name]["mining"]["categories"] = proto.resource_categories - data_collection[proto.name]["mining"]["speed"] = proto.mining_speed - end - end + if proto.crafting_categories or proto.type == "character" then + data_collection[proto.name] = {} + data_collection[proto.name]["crafting"] = {} + data_collection[proto.name]["crafting"]["type"] = proto.type + data_collection[proto.name]["crafting"]["speed"] = proto.crafting_speed or 1 + data_collection[proto.name]["crafting"]["categories"] = proto.crafting_categories + data_collection[proto.name]["crafting"]["fixed_recipe"] = proto.fixed_recipe + data_collection[proto.name]["crafting"]["input_fluid_box"] = 0 + data_collection[proto.name]["crafting"]["output_fluid_box"] = 0 + for _, fluid_box in pairs(proto.fluidbox_prototypes) do + for _, connection in pairs(fluid_box.pipe_connections) do + if connection.type == "input" then + data_collection[proto.name]["crafting"]["input_fluid_box"] = data_collection[proto.name]["crafting"]["input_fluid_box"] + 1 + end + if connection.type == "output" then + data_collection[proto.name]["crafting"]["output_fluid_box"] = data_collection[proto.name]["crafting"]["output_fluid_box"] + 1 + end + end + end + end + if proto.resource_categories then + data_collection[proto.name] = data_collection[proto.name] or {} + data_collection[proto.name]["mining"] = {} + data_collection[proto.name]["mining"]["type"] = proto.type + data_collection[proto.name]["mining"]["categories"] = proto.resource_categories + data_collection[proto.name]["mining"]["speed"] = proto.mining_speed + data_collection[proto.name]["mining"]["input_fluid_box"] = false + data_collection[proto.name]["mining"]["output_fluid_box"] = false + for _, fluid_box in pairs(proto.fluidbox_prototypes) do + for _, connection in pairs(fluid_box.pipe_connections) do + if connection.type == "input-output" or connection.type == "input" then + data_collection[proto.name]["mining"]["input_fluid_box"] = true + end + if connection.type == "output" then + data_collection[proto.name]["mining"]["output_fluid_box"] = true + end + end + end + end + if proto.lab_inputs then + data_collection[proto.name] = {} + data_collection[proto.name]["lab"] = {} + data_collection[proto.name]["lab"]["inputs"] = proto.lab_inputs + end + if proto.fluid then + data_collection[proto.name] = {} + data_collection[proto.name]["offshore-pump"] = {} + data_collection[proto.name]["offshore-pump"]["type"] = proto.type + data_collection[proto.name]["offshore-pump"]["fluid"] = proto.fluid.name + data_collection[proto.name]["offshore-pump"]["speed"] = proto.pumping_speed + end + if proto.type == "boiler" then + local input_fluid = nil + local output_fluid = nil + for _, fluid_box in pairs(proto.fluidbox_prototypes) do + if fluid_box.production_type == "input-output" or fluid_box.production_type == "input" then + input_fluid = fluid_box + end + if fluid_box.production_type == "output" then + output_fluid = fluid_box + end + end + if input_fluid and output_fluid then + data_collection[proto.name] = {} + data_collection[proto.name]["boiler"] = {} + data_collection[proto.name]["boiler"]["type"] = proto.type + data_collection[proto.name]["boiler"]["input_fluid"] = input_fluid.filter.name + data_collection[proto.name]["boiler"]["output_fluid"] = output_fluid.filter.name + data_collection[proto.name]["boiler"]["target_temperature"] = proto.target_temperature + data_collection[proto.name]["boiler"]["energy_usage"] = proto.max_energy_usage + end + end + if proto.burner_prototype and proto.burner_prototype.burnt_inventory_size > 0 then + -- Only really care about burners that have at least one slot to store the burnt result. + data_collection[proto.name] = data_collection[proto.name] or {} + data_collection[proto.name]["fuel_burner"] = {} + data_collection[proto.name]["fuel_burner"]["type"] = proto.type + data_collection[proto.name]["fuel_burner"]["categories"] = proto.burner_prototype.fuel_categories + data_collection[proto.name]["fuel_burner"]["energy_usage"] = proto.max_energy_usage + end end game.write_file("machines.json", game.table_to_json(data_collection), false) game.print("Exported Machine Data") @@ -114,22 +198,33 @@ function dumpResourceInfo() game.print("Exported Minable Resource Data") end -function dumpMachineInfo() - data_collection = {} - for _, proto in pairs(game.entity_prototypes) do - if proto.crafting_categories then - data_collection[proto.name] = proto.crafting_categories - end - end - - game.write_file("machines.json", game.table_to_json(data_collection), false) - game.print("Exported Machine Data") -end - function dumpItemInfo() data_collection = {} for _, item in pairs(game.item_prototypes) do - data_collection[item.name] = item.stack_size + data_collection[item.name] = {} + data_collection[item.name]["stack_size"] = item.stack_size + if item.burnt_result then + data_collection[item.name]["fuel_value"] = item.fuel_value + data_collection[item.name]["fuel_category"] = item.fuel_category + data_collection[item.name]["burnt_result"] = item.burnt_result.name + end + for _, launch_product in pairs(item.rocket_launch_products) do + data_collection[item.name]["rocket_launch_products"] = data_collection[item.name]["rocket_launch_products"] or {} + if launch_product.amount then + data_collection[item.name]["rocket_launch_products"][launch_product.name] = launch_product.amount + else + amount = math.max(((launch_product.amount_min + launch_product.amount_max) / 2) * launch_product.probablility, 1) + data_collection[item.name]["rocket_launch_products"][launch_product.name] = amount + end + end + --stackable property incorrectly reports true for "spidertron-remote" + data_collection[item.name]["stackable"] = item.stackable + if item.type == "spidertron-remote" then + data_collection[item.name]["stackable"] = false + end + if item.place_result then + data_collection[item.name]["place_result"] = item.place_result.name + end end game.write_file("items.json", game.table_to_json(data_collection), false) @@ -139,18 +234,40 @@ end function dumpFluidInfo() data_collection = {} for _, item in pairs(game.fluid_prototypes) do - table.insert(data_collection, item.name) + fluid = {} + fluid["default_temperature"] = item.default_temperature + fluid["max_temperature"] = item.max_temperature + fluid["heat_capacity"] = item.heat_capacity + data_collection[item.name] = fluid end game.write_file("fluids.json", game.table_to_json(data_collection), false) game.print("Exported Fluid Data") end +function mod_is_AP(str) + -- lua string.match is way more restrictive than regex. Regex would be "^AP-W?\d{20}-P[1-9]\d*-.+$" + local result = string.match(str, "^AP%-W?%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%-P[1-9]%d-%-.+$") + if result ~= nil then + log("Archipelago Mod: " .. result .. " is loaded.") + end + return result ~= nil +end + function dumpGameInfo() + -- Verify that Archipelago is NOT running before dumping the info. + for name, _ in pairs(game.active_mods) do + if mod_is_AP(name) then + game.print("ERROR: Archipelago is running. Can't dump info") + return + end + end + -- dump Game Information that the Archipelago Randomizer needs. local force = game.forces["player"] + dumpModInfo() + dumpRecipeInfo(force) dumpTechInfo(force) - dumpRecipeInfo(force) dumpResourceInfo() dumpMachineInfo() dumpItemInfo() From 680d091a4156f957b0002df7634fc1d8e3d0f76c Mon Sep 17 00:00:00 2001 From: CaitSith2 Date: Sun, 25 Sep 2022 04:07:34 -0700 Subject: [PATCH 2/2] changes * Extract items required to place machine. * Move machine type to common variables. * workaround game.table_to_json bug if max_temperature is math.huge (infinite). (it does not put inf in quotes.) --- archipelago-extractor/control.lua | 42 +++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/archipelago-extractor/control.lua b/archipelago-extractor/control.lua index ba304b7..b1f4a86 100644 --- a/archipelago-extractor/control.lua +++ b/archipelago-extractor/control.lua @@ -71,13 +71,30 @@ function dumpTechInfo(force) end end +function get_items_to_place_prototype(proto) + if proto.type == "character" then + return nil + end + if not proto.items_to_place_this then + return nil + end + items = {} + for _, item in pairs(proto.items_to_place_this) do + if type(item) == "string" then + items[item] = true + else + items[item.name] = true + end + end + return items +end + function dumpMachineInfo() data_collection = {} for _, proto in pairs(game.entity_prototypes) do if proto.crafting_categories or proto.type == "character" then data_collection[proto.name] = {} data_collection[proto.name]["crafting"] = {} - data_collection[proto.name]["crafting"]["type"] = proto.type data_collection[proto.name]["crafting"]["speed"] = proto.crafting_speed or 1 data_collection[proto.name]["crafting"]["categories"] = proto.crafting_categories data_collection[proto.name]["crafting"]["fixed_recipe"] = proto.fixed_recipe @@ -97,14 +114,13 @@ function dumpMachineInfo() if proto.resource_categories then data_collection[proto.name] = data_collection[proto.name] or {} data_collection[proto.name]["mining"] = {} - data_collection[proto.name]["mining"]["type"] = proto.type data_collection[proto.name]["mining"]["categories"] = proto.resource_categories data_collection[proto.name]["mining"]["speed"] = proto.mining_speed data_collection[proto.name]["mining"]["input_fluid_box"] = false data_collection[proto.name]["mining"]["output_fluid_box"] = false for _, fluid_box in pairs(proto.fluidbox_prototypes) do for _, connection in pairs(fluid_box.pipe_connections) do - if connection.type == "input-output" or connection.type == "input" then + if connection.type == "input-output" then data_collection[proto.name]["mining"]["input_fluid_box"] = true end if connection.type == "output" then @@ -121,25 +137,23 @@ function dumpMachineInfo() if proto.fluid then data_collection[proto.name] = {} data_collection[proto.name]["offshore-pump"] = {} - data_collection[proto.name]["offshore-pump"]["type"] = proto.type data_collection[proto.name]["offshore-pump"]["fluid"] = proto.fluid.name data_collection[proto.name]["offshore-pump"]["speed"] = proto.pumping_speed end if proto.type == "boiler" then local input_fluid = nil local output_fluid = nil - for _, fluid_box in pairs(proto.fluidbox_prototypes) do - if fluid_box.production_type == "input-output" or fluid_box.production_type == "input" then + for index, fluid_box in pairs(proto.fluidbox_prototypes) do + if fluid_box.production_type == "input-output" then input_fluid = fluid_box end if fluid_box.production_type == "output" then output_fluid = fluid_box end end - if input_fluid and output_fluid then + if input_fluid and output_fluid then data_collection[proto.name] = {} data_collection[proto.name]["boiler"] = {} - data_collection[proto.name]["boiler"]["type"] = proto.type data_collection[proto.name]["boiler"]["input_fluid"] = input_fluid.filter.name data_collection[proto.name]["boiler"]["output_fluid"] = output_fluid.filter.name data_collection[proto.name]["boiler"]["target_temperature"] = proto.target_temperature @@ -150,10 +164,14 @@ function dumpMachineInfo() -- Only really care about burners that have at least one slot to store the burnt result. data_collection[proto.name] = data_collection[proto.name] or {} data_collection[proto.name]["fuel_burner"] = {} - data_collection[proto.name]["fuel_burner"]["type"] = proto.type data_collection[proto.name]["fuel_burner"]["categories"] = proto.burner_prototype.fuel_categories data_collection[proto.name]["fuel_burner"]["energy_usage"] = proto.max_energy_usage end + if data_collection[proto.name] then + data_collection[proto.name]["common"] = {} + data_collection[proto.name]["common"]["type"] = proto.type + data_collection[proto.name]["common"]["placeable_by"] = get_items_to_place_prototype(proto) + end end game.write_file("machines.json", game.table_to_json(data_collection), false) game.print("Exported Machine Data") @@ -236,7 +254,11 @@ function dumpFluidInfo() for _, item in pairs(game.fluid_prototypes) do fluid = {} fluid["default_temperature"] = item.default_temperature - fluid["max_temperature"] = item.max_temperature + if item.max_temperature == math.huge then + fluid["max_temperature"] = "inf" + else + fluid["max_temperature"] = item.max_temperature + end fluid["heat_capacity"] = item.heat_capacity data_collection[item.name] = fluid end