-- WARNING WARNING WARNING -- This file will be overwritten in mod zipfiles, edit bzlib/data-util.lua -- WARNING WARNING WARNING local me = require("me") local util = {} util.me = me util.get_setting = util.me.get_setting util.titanium_plate = "" util.titanium_processing = "" util.A = {{"automation-science-pack", 1}} util.AL = {{"automation-science-pack", 1}, {"logistic-science-pack", 1}} util.ALC = {{"automation-science-pack", 1}, {"logistic-science-pack", 1}, {"chemical-science-pack", 1}} if mods["FactorioExtended-Plus-Core"] then util.titanium_plate = "titanium-alloy" else util.titanium_plate = "titanium-plate" end if mods["pyrawores"] then util.titanium_processing = "titanium-mk01" else util.titanium_processing = "titanium-processing" end util.vacuum_icon = { icon="__base__/graphics/icons/fluid/steam.png", tint={r=.1, g=.1, b=.5, a=.5} } util.vacuum_icon_small = { icon="__base__/graphics/icons/fluid/steam.png", tint={r=.1, g=.1, b=.5, a=.5}, scale=0.25, shift={-8,-8}, } util.vacuum_vis = {r=.1, g=.1, b=.5} function util.item(item, quantity, probability) if not quantity then quantity = 1 end if probability then return {type="item", name=item, amount=quantity, probability=probability} else return {type="item", name=item, amount=quantity} end end function util.fluid(fluid, quantity) if not quantity then quantity = 10 end return {type="fluid", name=fluid, amount=quantity} end function util.se6() return mods["space-exploration"] and mods["space-exploration"] >= "0.6" end util.cablesg = util.se6() and "electronic" or "cable" function get_setting(name) if settings.startup[name] == nil then return nil end return settings.startup[name].value end allbypass = {} if get_setting("bz-recipe-bypass") then for recipe in string.gmatch(me.get_setting("bz-recipe-bypass"), '[^",%s]+') do allbypass[recipe] = true end end function util.is_foundry() return mods.bzfoundry and not me.get_setting("bzfoundry-minimal") end function should_force(options) return options and options.force end function bypass(recipe_name) if me.bypass[recipe_name] then return true end if allbypass[recipe_name] then return true end if get_setting("bz-tabula-rasa") then return true end end function util.fe_plus(sub) if mods["FactorioExtended-Plus-"..sub] then return true end end function util.get_stack_size(default) if mods.Krastorio2 then local size = get_setting("kr-stack-size") if size and tonumber(size) then return tonumber(size) end end return default end function util.k2assets() if mods["Krastorio2Assets"] then return "__Krastorio2Assets__" end return "__Krastorio2__/graphics" end -- check if a table contains a sought value function util.contains(table, sought) for i, value in pairs(table) do if value == sought then return true end end return false end -- Add the gleba rock. If it exists, still add resource to mine from it function util.add_gleba_rock(resource, amount_min, amount_max) if not data.raw.planet.gleba then return end if not data.raw["simple-entity"]["gleba-rock"] then local autoplace_utils = require("autoplace_utils") local hit_effects = require ("__base__.prototypes.entity.hit-effects") local sounds = require ("__base__.prototypes.entity.sounds") local decorative_trigger_effects = require("__base__.prototypes.decorative.decorative-trigger-effects") data.raw.planet.gleba.map_gen_settings.autoplace_settings.entity.settings["gleba-rock"] = {} data:extend({ { type = "simple-entity", name = "gleba-rock", localised_name = {"entity-name.big-rock"}, flags = {"placeable-neutral", "placeable-off-grid"}, icon = "__base__/graphics/icons/big-sand-rock.png", subgroup = "grass", order = "b[decorative]-l[rock]-a[big]", deconstruction_alternative = "big-rock", collision_box = {{-0.75, -0.75}, {0.75, 0.75}}, selection_box = {{-1.0, -1.0}, {1.0, 0.75}}, damaged_trigger_effect = hit_effects.rock(), render_layer = "object", max_health = 500, autoplace = { control = "gleba_plants", order = "z[gleba]-a[rock]-b[big]", probability_expression = "max(main_probability, invasion_tall_probability)", richness_expression = "random_penalty_at(3, 1)", tile_restriction = { "highland-yellow-rock", "highland-dark-rock", "highland-dark-rock-2", }, local_expressions = { main_box = "gleba_select(gleba_moisture, 0, 0.25, 0.01, -10, 1) - 1", main_probability = "min(0.08, 0.15 * (main_box + gleba_plants_noise_b - 0.45) * control:gleba_plants:size)", -- bigger patches, denser invasion_tall_box = "gleba_select(gleba_moisture, 0, 0.35, 0.01, -10, 1) - 1", invasion_tall_probability = "min(0.05, 0.15 * (invasion_tall_box + gleba_plants_noise_b - 0.4) * control:gleba_plants:size)", -- smaller patches, sparser } }, dying_trigger_effect = decorative_trigger_effects.big_rock(), minable = { mining_particle = "stone-particle", mining_time = 2, results = { {type = "item", name = "stone", amount_min = 5, amount_max = 10}, } }, resistances = { { type = "fire", percent = 100 } }, map_color = {129, 105, 78}, count_as_rock_for_filtered_deconstruction = true, mined_sound = sounds.deconstruct_bricks(1.0), impact_category = "stone", pictures = { { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-01.png", width = 209, height = 138, shift = {0.304688, -0.4}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-02.png", width = 165, height = 129, shift = {0.0, 0.0390625}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-03.png", width = 151, height = 139, shift = {0.151562, 0.0}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-04.png", width = 216, height = 110, shift = {0.390625, 0.0}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-05.png", width = 154, height = 147, shift = {0.328125, 0.0703125}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-06.png", width = 154, height = 132, shift = {0.16875, -0.1}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-07.png", width = 193, height = 130, shift = {0.3, -0.2}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-08.png", width = 136, height = 117, shift = {0.0, 0.0}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-09.png", width = 157, height = 115, shift = {0.1, 0.0}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-10.png", width = 198, height = 153, shift = {0.325, -0.1}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-11.png", width = 190, height = 115, shift = {0.453125, 0.0}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-12.png", width = 229, height = 126, shift = {0.539062, -0.015625}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-13.png", width = 151, height = 125, shift = {0.0703125, 0.179688}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-14.png", width = 137, height = 117, shift = {0.160938, 0.0}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-15.png", width = 201, height = 141, shift = {0.242188, -0.195312}, scale = 0.5 }, { filename = "__base__/graphics/decorative/sand-rock/big-sand-rock-16.png", width = 209, height = 154, shift = {0.351562, -0.1}, scale = 0.5 } } }, }) local probability = data.raw["simple-entity"]["gleba-rock"].autoplace.probability_expression -- A lot more common near starting point when aps gleba local factor = (mods["any-planet-start"] and me.get_setting("aps-planet") == "gleba" and 20) or 1 data.raw["simple-entity"]["gleba-rock"].autoplace.probability_expression = probability..[[* if(distance_from_nearest_point{x = x, y = y, points = starting_positions} < 200, ]]..factor..[[, if(distance_from_nearest_point{x = x, y = y, points = starting_positions} < 700, 100/(distance_from_nearest_point{x = x, y = y, points = starting_positions} - 100), 0.17)) ]] end if data.raw.item[resource] then amount_min = (amount_min or 10) * ((mods["any-planet-start"] and me.get_setting("aps-planet") == "gleba" and 4) or 1) amount_max = (amount_max or 20) * ((mods["any-planet-start"] and me.get_setting("aps-planet") == "gleba" and 4) or 1) util.add_minable_result( "simple-entity", "gleba-rock", {type="item", name=resource, amount_min=amount_min, amount_max=amount_max}) end end -- Replace 'uranium-mining' tech with 'fluid-mining', defaulting to same costs function util.add_fluid_mining() if data.raw.technology["fluid-mining"] then return end data:extend({ { type = "technology", name = "fluid-mining", icon = "__"..util.me.name.."__/graphics/technology/fluid-mining.png", icon_size = 256, effects = { { type = "mining-with-fluid", modifier = true } }, prerequisites = {"chemical-science-pack", "concrete"}, unit = { count = 100, ingredients = { {"automation-science-pack", 1}, {"logistic-science-pack", 1}, {"chemical-science-pack", 1} }, time = 30, } } }) end -- Final fix to make sure nothing uses "uranium-mining" function util.use_fluid_mining_final() for i, tech in pairs(data.raw.technology) do if tech.prerequisites then for j, pre in pairs(tech.prerequisites) do if pre == "uranium-mining" then util.add_prerequisite(tech.name, "fluid-mining") util.remove_prerequisite(tech.name, "uranium-mining") break end end end end util.remove_raw("technology", "uranium-mining") end -- If Hot metals mod is enabled, mark these metals as hot function util.add_hot_metals(metals) if HotMetals and HotMetals.items then for _, metal in pairs(metals) do if data.raw.item[metal] or (metal.name and data.raw.item[metal.name]) then table.insert(HotMetals.items, metal) end end end end -- se landfill -- params: ore, icon_size function util.se_landfill(params) if mods["space-exploration"] then if not params.icon_size then params.icon_size = 64 end local lname="landfill-"..params.ore data:extend({ { type = "recipe", icons = { { icon = "__base__/graphics/icons/landfill.png", icon_size = 64, icon_mipmaps = 3 }, { icon = "__"..me.name.."__/graphics/icons/"..params.ore..".png", icon_size = params.icon_size, scale = 0.33*64/params.icon_size}, }, energy_required = 1, enabled=false, name = lname, category = "hard-recycling", order = "z-b-"..params.ore, subgroup = "terrain", result = "landfill", ingredients = {{params.ore, 50}}, } }) util.add_unlock("se-recycling-facility", lname) end end -- k2 matter -- params: {k2matter}, k2baseicon , {icon} function util.k2matter(params) local matter = require("__Krastorio2__/lib/public/data-stages/matter-util") if mods["space-exploration"] then params.k2matter.need_stabilizer = true end if not params.k2matter.minimum_conversion_quantity then params.k2matter.minimum_conversion_quantity = 10 end if not data.raw.technology[params.k2matter.unlocked_by_technology] then local icon = "" if params.k2baseicon then icon = util.k2assets().."/technologies/matter-"..params.k2baseicon..".png" else icon = util.k2assets().."/technologies/backgrounds/matter.png" end data:extend( { { type = "technology", name = params.k2matter.unlocked_by_technology, icons = { { icon = icon, icon_size = 256, }, params.icon, }, prerequisites = {"kr-matter-processing"}, unit = { count = 350, ingredients = mods["space-exploration"] and { {"automation-science-pack", 1}, {"logistic-science-pack", 1}, {"chemical-science-pack", 1}, {"se-astronomic-science-pack-4", 1}, {"se-energy-science-pack-4", 1}, {"se-material-science-pack-4", 1}, {"se-deep-space-science-pack-2", 1}, {"se-kr-matter-science-pack-2", 1}, } or { {"production-science-pack", 1}, {"utility-science-pack", 1}, {"matter-tech-card", 1} }, time = 45, }, -- (ignore for now) localised_name = {"technology-name.k2-conversion", {"item-name."..params.k2matter.item_name}}, }, }) end matter.createMatterRecipe(params.k2matter) end -- se matter -- params: ore, energy_required, quant_out, quant_in, icon_size, stream_out function util.se_matter(params) if mods["space-exploration"] > "0.6" then if not params.quant_in then params.quant_in = params.quant_out end if not params.icon_size then params.icon_size = 64 end local fname = "matter-fusion-"..params.ore local sedata = mods.Krastorio2 and "se-kr-matter-synthesis-data" or "se-fusion-test-data" local sejunk = mods.Krastorio2 and "se-broken-data" or "se-junk-data" data:extend({ { type = "recipe", name = fname, localised_name = {"recipe-name.se-matter-fusion-to", {"item-name."..params.ore}}, category = "space-materialisation", subgroup = "materialisation", order = "a-b-z", icons = { {icon = "__space-exploration-graphics__/graphics/blank.png", icon_size = 64, scale = 0.5}, {icon = "__space-exploration-graphics__/graphics/icons/fluid/particle-stream.png", icon_size = 64, scale = 0.33, shift = {8,-8}}, {icon = "__"..util.me.name.."__/graphics/icons/"..params.ore..".png", icon_size = params.icon_size, scale = 0.33 * 64/params.icon_size, shift={-8, 8}}, {icon = "__space-exploration-graphics__/graphics/icons/transition-arrow.png", icon_size = 64, scale = 0.5}, }, energy_required = params.energy_required, enabled = false, ingredients = { {sedata, 1}, {type="fluid", name="se-particle-stream", amount=50}, {type="fluid", name="se-space-coolant-supercooled", amount=25}, }, results = { {params.ore, params.quant_out}, {"se-contaminated-scrap", 1}, {type=item, name=sedata, amount=1, probability=.99}, {type=item, name=sejunk, amount=1, probability=.01}, {type="fluid", name="se-space-coolant-hot", amount=25, catalyst_amount=25}, } } }) util.add_unlock("se-space-matter-fusion", fname) if mods.Krastorio2 then local lname = params.ore.."-to-particle-stream" data:extend({ enabled = false, { type = "recipe", name = lname, localised_name = {"recipe-name.se-kr-matter-liberation", {"item-name."..params.ore}}, category = "space-materialisation", subgroup = "advanced-particle-stream", order = "a-b-z", icons = { {icon = "__space-exploration-graphics__/graphics/blank.png", icon_size = 64, scale = 0.5}, {icon = "__space-exploration-graphics__/graphics/icons/fluid/particle-stream.png", icon_size = 64, scale = 0.33, shift = {-8,8}}, {icon = "__"..util.me.name.."__/graphics/icons/"..params.ore..".png", icon_size = params.icon_size, scale = 0.33 * 64/params.icon_size, shift={8, -8}}, {icon = "__space-exploration-graphics__/graphics/icons/transition-arrow.png", icon_size = 64, scale = 0.5}, }, energy_required = 30, enabled = false, ingredients = { {"se-kr-matter-liberation-data", 1}, {params.ore, params.quant_in}, {type="fluid", name="se-particle-stream", amount=50}, }, results = { {type=item, name="se-kr-matter-liberation-data", amount=1, probability=.99}, {type=item, name=sejunk, amount=1, probability=.01}, {type="fluid", name="se-particle-stream", amount=params.stream_out, catalyst_amount=50}, } } }) if not data.raw.technology["bz-advanced-stream-production"] then data:extend({ { type = "technology", name ="bz-advanced-stream-production", localised_name = {"", {"technology-name.se-kr-advanced-stream-production"}, " 2"}, icon = "__space-exploration-graphics__/graphics/technology/material-fabricator.png", icon_size = 128, effects = {}, unit = { count = 100, time = 15, ingredients = { {"automation-science-pack", 1}, {"logistic-science-pack", 1}, {"chemical-science-pack", 1}, {"se-rocket-science-pack", 1}, {"space-science-pack", 1}, {"production-science-pack", 1}, {"utility-science-pack", 1}, {"se-astronomic-science-pack-4", 1}, {"se-energy-science-pack-4", 1}, {"se-material-science-pack-4", 1}, {"matter-tech-card", 1}, {"se-deep-space-science-pack-1", 1}, } }, prerequisites = {"se-kr-advanced-stream-production"}, }, }) end util.add_unlock("bz-advanced-stream-production", lname) end end end -- deprecated -- Get the normal prototype for a recipe -- either .normal or the recipe itself function util.get_normal(recipe_name) if data.raw.recipe[recipe_name] then recipe = data.raw.recipe[recipe_name] return recipe end end -- Set/override a technology's prerequisites function util.set_prerequisite(technology_name, prerequisites) local technology = data.raw.technology[technology_name] if technology then technology.prerequisites = {} for i, prerequisite in pairs(prerequisites) do if data.raw.technology[prerequisite] then table.insert(technology.prerequisites, prerequisite) end end end end -- Add a prerequisite to a given technology function util.add_prerequisite(technology_name, prerequisite) local technology = data.raw.technology[technology_name] if technology and data.raw.technology[prerequisite] then if technology.prerequisites then for i, pre in pairs(technology.prerequisites) do if pre == prerequisite then return end end table.insert(technology.prerequisites, prerequisite) else technology.prerequisites = {prerequisite} end end end -- Remove a prerequisite from a given technology function util.remove_prerequisite(technology_name, prerequisite) local technology = data.raw.technology[technology_name] local index = -1 if technology then for i, prereq in pairs(technology.prerequisites) do if prereq == prerequisite then index = i break end end if index > -1 then table.remove(technology.prerequisites, index) end end end -- Add an effect to a given technology function util.add_effect(technology_name, effect) local technology = data.raw.technology[technology_name] if technology then if not technology.effects then technology.effects = {} end if effect and effect.type == "unlock-recipe" then if not data.raw.recipe[effect.recipe] then return end table.insert(technology.effects, effect) end end end -- Add an effect to a given technology to unlock recipe function util.add_unlock(technology_name, recipe) util.add_effect(technology_name, {type="unlock-recipe", recipe=recipe}) end -- Check if a tech unlocks a recipe function util.check_unlock(technology_name, recipe_name) local technology = data.raw.technology[technology_name] if technology and technology.effects then for i, effect in pairs(technology.effects) do if effect.type == "unlock-recipe" and effect.recipe == recipe_name then return true end end end return false end -- remove recipe unlock effect from a given technology, multiple times if necessary function util.remove_recipe_effect(technology_name, recipe_name) local technology = data.raw.technology[technology_name] local index = -1 local cnt = 0 if technology and technology.effects then for i, effect in pairs(technology.effects) do if effect.type == "unlock-recipe" and effect.recipe == recipe_name then index = i cnt = cnt + 1 end end if index > -1 then table.remove(technology.effects, index) if cnt > 1 then -- not over yet, do it again util.remove_recipe_effect(technology_name, recipe_name) end end end end -- Set technology ingredients function util.set_tech_recipe(technology_name, ingredients) local technology = data.raw.technology[technology_name] if technology then if not technology.unit then -- set a sane unit just in case technology.unit = {time = 30, count = 50} end technology.unit.ingredients = ingredients technology.research_trigger = nil end end -- Set technology trigger function util.set_tech_trigger(technology_name, trigger) local technology = data.raw.technology[technology_name] if technology then technology.unit = nil technology.research_trigger = trigger end end function util.set_enabled(recipe_name, enabled) if data.raw.recipe[recipe_name] then data.raw.recipe[recipe_name].enabled = enabled end end function util.set_hidden(recipe_name) if data.raw.recipe[recipe_name] then data.raw.recipe[recipe_name].hidden = true end end -- adds a crafting category if it doesn't exist, also optionally allowing handcrafting function util.add_new_crafting_category(category, by_hand) if not data.raw["recipe-category"][category] then data:extend({{ type="recipe-category", name=category, }}) end if by_hand then for i, character in pairs(data.raw.character) do if character.crafting_categories then table.insert(character.crafting_categories, category) end end end end -- Add a given quantity of ingredient to a given recipe function util.add_or_add_to_ingredient(recipe_name, ingredient, quantity, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] and data.raw.item[ingredient] then me.add_modified(recipe_name) add_or_add_to_ingredient(data.raw.recipe[recipe_name], ingredient, quantity) end end function add_or_add_to_ingredient(recipe, ingredient, quantity) if recipe ~= nil and recipe.ingredients ~= nil then for i, existing in pairs(recipe.ingredients) do if existing.name == ingredient then add_to_ingredient(recipe, ingredient, quantity) return end end table.insert(recipe.ingredients, {ingredient, quantity}) end end -- Add a given quantity of ingredient to a given recipe function util.add_ingredient(recipe_name, ingredient, quantity, options) if not should_force(options) and bypass(recipe_name) then return end local is_fluid = not not data.raw.fluid[ingredient] if data.raw.recipe[recipe_name] and (data.raw.item[ingredient] or is_fluid) then me.add_modified(recipe_name) add_ingredient(data.raw.recipe[recipe_name], ingredient, quantity, is_fluid) end end function add_ingredient(recipe, ingredient, quantity, is_fluid) if recipe ~= nil and recipe.ingredients ~= nil then for i, existing in pairs(recipe.ingredients) do if existing.name == ingredient then return end end if is_fluid then table.insert(recipe.ingredients, {type="fluid", name=ingredient, amount=quantity}) else table.insert(recipe.ingredients, {type="item", name=ingredient, amount=quantity}) end end end -- Add a given ingredient prototype to a given recipe function util.add_ingredient_raw(recipe_name, ingredient, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] and data.raw.item[ingredient.name] then me.add_modified(recipe_name) add_ingredient_raw(data.raw.recipe[recipe_name], ingredient) end end function add_ingredient_raw(recipe, ingredient) if recipe ~= nil and recipe.ingredients ~= nil then for i, existing in pairs(recipe.ingredients) do if existing.name == ingredient.name then return end end table.insert(recipe.ingredients, ingredient) end end -- Set an ingredient to a given quantity function util.set_ingredient(recipe_name, ingredient, quantity, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] and data.raw.item[ingredient] then me.add_modified(recipe_name) set_ingredient(data.raw.recipe[recipe_name], ingredient, quantity) end end function set_ingredient(recipe, ingredient, quantity) if recipe ~= nil and recipe.ingredients ~= nil then for i, existing in pairs(recipe.ingredients) do if existing.name == ingredient then existing.amount = quantity existing.amount_min = nil existing.amount_max = nil return end end table.insert(recipe.ingredients, {ingredient, quantity}) end end -- Add a given quantity of product to a given recipe. -- Only works for recipes with multiple products function util.add_product(recipe_name, product, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] and (data.raw.item[product.name] or data.raw.fluid[product.name] ) then add_product(data.raw.recipe[recipe_name], product) end end function add_product(recipe, product) if recipe ~= nil then if product.name and data.raw[product.type][product.name] then if recipe.results == nil then recipe.results = {{recipe.result, recipe.result_count and recipe.result_count or 1}} end recipe.result = nil recipe.result_count = nil table.insert(recipe.results, product) end end end -- Get the amount of the ingredient function util.get_ingredient_amount(recipe_name, ingredient_name) local recipe = data.raw.recipe[recipe_name] if recipe then if recipe.ingredients then for i, ingredient in pairs(recipe.ingredients) do if ingredient.name == ingredient_name then return ingredient.amount end end end return 0 end return 0 end -- Get the amount of the result function util.get_amount(recipe_name, product) if not product then product = recipe_name end local recipe = data.raw.recipe[recipe_name] if recipe then if recipe.results then for i, result in pairs(recipe.results) do if result.name == product then return result.amount end end end return 0 end return 0 end -- Get the count of results function util.get_result_count(recipe_name, product) if not product then product = recipe_name end local recipe = data.raw.recipe[recipe_name] if recipe then if recipe.results then return #(recipe.results) end return 1 end return 0 end -- Replace one ingredient with another in a recipe -- Use amount to set an amount. If that amount is a multiplier instead of an exact amount, set multiply true. function util.replace_ingredient(recipe_name, old, new, amount, multiply, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] and (data.raw.item[new] or data.raw.fluid[new]) then me.add_modified(recipe_name) replace_ingredient(data.raw.recipe[recipe_name], old, new, amount, multiply) end end function replace_ingredient(recipe, old, new, amount, multiply) if recipe ~= nil and recipe.ingredients ~= nil then for i, existing in pairs(recipe.ingredients) do if existing.name == new then return end end for i, ingredient in pairs(recipe.ingredients) do if ingredient.name == old then ingredient.name = new if amount then if multiply then ingredient.amount = amount * ingredient.amount else ingredient.amount = amount end end end end end end -- Remove an ingredient from a recipe function util.remove_ingredient(recipe_name, old, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) remove_ingredient(data.raw.recipe[recipe_name], old) end end function remove_ingredient(recipe, old) index = -1 if recipe ~= nil and recipe.ingredients ~= nil then for i, ingredient in pairs(recipe.ingredients) do if ingredient.name == old then index = i break end end if index > -1 then table.remove(recipe.ingredients, index) end end end -- Replace an amount of a product, leaving at least 1 of old function util.replace_some_product(recipe_name, old, old_amount, new, new_amount, options) if not should_force(options) and bypass(recipe_name) then return end local is_fluid = not not data.raw.fluid[new] -- NOTE CURRENTLY UNUSUED if data.raw.recipe[recipe_name] and (data.raw.item[new] or is_fluid) then me.add_modified(recipe_name) replace_some_product(data.raw.recipe[recipe_name], old, old_amount, new, new_amount, is_fluid) end end function replace_some_product(recipe, old, old_amount, new, new_amount) if recipe ~= nil then if recipe.result == new then return end if recipe.results then for i, existing in pairs(recipe.results) do if existing.name == new then return end end end add_product(recipe, {new, new_amount}) for i, product in pairs(recipe.results) do if product.name == old then product.amount = math.max(1, product.amount - old_amount) end end end end -- Replace an amount of an ingredient in a recipe. Keep at least 1 of old. function util.replace_some_ingredient(recipe_name, old, old_amount, new, new_amount, options) if not should_force(options) and bypass(recipe_name) then return end local is_fluid = not not data.raw.fluid[new] if data.raw.recipe[recipe_name] and (data.raw.item[new] or is_fluid) then me.add_modified(recipe_name) replace_some_ingredient(data.raw.recipe[recipe_name], old, old_amount, new, new_amount, is_fluid) end end function replace_some_ingredient(recipe, old, old_amount, new, new_amount, is_fluid) if recipe ~= nil and recipe.ingredients ~= nil then for i, existing in pairs(recipe.ingredients) do if existing.name == new then return end end for i, ingredient in pairs(recipe.ingredients) do if ingredient.name == old then ingredient.amount = math.max(1, ingredient.amount - old_amount) end end add_ingredient(recipe, new, new_amount, is_fluid) end end -- set the probability of a product. function util.set_product_probability(recipe_name, product, probability, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) set_product_probability(data.raw.recipe[recipe_name], product, probability) end end function set_product_probability(recipe, product, probability) if recipe then if recipe.results then for i, result in pairs(recipe.results) do if result.name == product then result.probability = probability end end end end end -- set the amount of a product. function util.set_product_amount(recipe_name, product, amount, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) set_product_amount(data.raw.recipe[recipe_name], product, amount) end end function set_product_amount(recipe, product, amount) if recipe then if recipe.results then for i, result in pairs(recipe.results) do if result.name == product then if result.amount then result.amount = amount end if result.amount_min ~= nil then result.amount_min = nil result.amount_max = nil result.amount = amount end end end end if not recipe.results and not recipe.result_count then -- implicit one item result recipe.result_count = amount end end end -- multiply the cost, energy, and results of a recipe by a multiple function util.multiply_recipe(recipe_name, multiple, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) multiply_recipe(data.raw.recipe[recipe_name], multiple) end end function multiply_recipe(recipe, multiple) if recipe then if recipe.energy_required then recipe.energy_required = recipe.energy_required * multiple else recipe.energy_required = 0.5 * multiple -- 0.5 is factorio default end if recipe.result_count then recipe.result_count = recipe.result_count * multiple end if recipe.results then for i, result in pairs(recipe.results) do if result.name then if result.amount then result.amount = result.amount * multiple end if result.amount_min ~= nil then result.amount_min = result.amount_min * multiple result.amount_max = result.amount_max * multiple end if result.catalyst_amount then result.catalyst_amount = result.catalyst_amount * multiple end end end end if not recipe.results and not recipe.result_count then -- implicit one item result recipe.result_count = multiple end if recipe.ingredients then for i, ingredient in pairs(recipe.ingredients) do if ingredient.name then ingredient.amount = ingredient.amount * multiple end end end end end -- Returns true if a recipe has an ingredient function util.has_ingredient(recipe_name, ingredient) return data.raw.recipe[recipe_name] and has_ingredient(data.raw.recipe[recipe_name], ingredient) end function has_ingredient(recipe, ingredient) if recipe ~= nil and recipe.ingredients ~= nil then for i, existing in pairs(recipe.ingredients) do if existing.name == ingredient then return true end end end return false end -- Remove a product from a recipe, WILL NOT remove the only product function util.remove_product(recipe_name, old, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) remove_product(data.raw.recipe[recipe_name], old) end end function remove_product(recipe, old) index = -1 if recipe ~= nil and recipe.results ~= nil then for i, result in pairs(recipe.results) do if result.name == old then index = i break end end if index > -1 then table.remove(recipe.results, index) end end end function util.set_main_product(recipe_name, product, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then set_main_product(data.raw.recipe[recipe_name], product) end end function set_main_product(recipe, product) if recipe then recipe.main_product = product end end -- Replace one product with another in a recipe function util.replace_product(recipe_name, old, new, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then replace_product(data.raw.recipe[recipe_name], old, new, options) end end function replace_product(recipe, old, new, options) if recipe then if recipe.main_product == old then recipe.main_product = new end if recipe.result == old then recipe.result = new return end if recipe.results then for i, result in pairs(recipe.results) do if result.name == old then result.name = new end end end end end -- Remove an element of type t and name from data.raw function util.remove_raw(t, name) if not data.raw[t] then log(t.." not found in data.raw") return end if data.raw[t][name] then for i, elem in pairs(data.raw[t]) do if elem.name == name then data.raw[t][i] = nil break end end end end -- Set energy required function util.set_recipe_time(recipe_name, time, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) set_recipe_time(data.raw.recipe[recipe_name], time) end end function set_recipe_time(recipe, time) if recipe then if recipe.energy_required then recipe.energy_required = time end end end -- Multiply energy required function util.multiply_time(recipe_name, factor, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) multiply_time(data.raw.recipe[recipe_name], factor) end end function multiply_time(recipe, factor) if recipe then if recipe.energy_required then recipe.energy_required = recipe.energy_required * factor end end end -- Add to energy required function util.add_time(recipe_name, amount, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) add_time(data.raw.recipe[recipe_name], amount) end end function add_time(recipe, amount) if recipe then if recipe.energy_required then recipe.energy_required = recipe.energy_required + amount end end end -- Set recipe category function util.set_category(recipe_name, category, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] and data.raw["recipe-category"][category] then me.add_modified(recipe_name) data.raw.recipe[recipe_name].category = category end end -- Set recipe subgroup function util.set_subgroup(recipe_name, subgroup, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] and data.raw["item-subgroup"][subgroup] then me.add_modified(recipe_name) data.raw.recipe[recipe_name].subgroup = subgroup end end -- Set item subgroup function util.set_item_subgroup(item, subgroup, options) if not should_force(options) and bypass(item) then return end -- imperfect, close enough for now? if data.raw.item[item] and data.raw["item-subgroup"][subgroup] then data.raw.item[item].subgroup = subgroup end end function util.add_icon(recipe_name, icon, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) if not (data.raw.recipe[recipe_name].icons and #(data.raw.recipe[recipe_name].icons) > 0) then if data.raw.recipe[recipe_name].icon then data.raw.recipe[recipe_name].icons = {{ icon=data.raw.recipe[recipe_name].icon, icon_size=data.raw.recipe[recipe_name].icon_size, icon_mipmaps=data.raw.recipe[recipe_name].icon_mipmaps, }} elseif data.raw.item[data.raw.recipe[recipe_name].main_product] then data.raw.recipe[recipe_name].icons = {{ icon=data.raw.item[data.raw.recipe[recipe_name].main_product].icon, icon_size=data.raw.item[data.raw.recipe[recipe_name].main_product].icon_size, icon_mipmaps=data.raw.item[data.raw.recipe[recipe_name].main_product].icon_mipmaps, }} elseif data.raw.item[data.raw.recipe[recipe_name].result] then data.raw.recipe[recipe_name].icons = {{ icon=data.raw.item[data.raw.recipe[recipe_name].result].icon, icon_size=data.raw.item[data.raw.recipe[recipe_name].result].icon_size, icon_mipmaps=data.raw.item[data.raw.recipe[recipe_name].result].icon_mipmaps, }} end data.raw.recipe[recipe_name].icon = nil data.raw.recipe[recipe_name].icon_size = nil end table.insert(data.raw.recipe[recipe_name].icons, icon) end end -- Set recipe icons function util.set_icons(recipe_name, icons, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then me.add_modified(recipe_name) data.raw.recipe[recipe_name].icons = icons data.raw.recipe[recipe_name].icon = nil data.raw.recipe[recipe_name].icon_size = nil end end -- Set tech icons function util.set_tech_icons(technology, icons, options) if not should_force(options) and bypass(technology) then return end if data.raw.technology[technology] then me.add_modified(technology) data.raw.technology[technology].icons = icons data.raw.technology[technology].icon = nil data.raw.technology[technology].icon_size = nil end end -- Set recipe icons function util.set_item_icons(item_name, icons) if data.raw.item[item_name] then data.raw.item[item_name].icons = icons data.raw.item[item_name].icon = nil data.raw.item[item_name].icon_size = nil end end -- Gets an item or fluid icon function util.get_item_or_fluid_icon(name) icon = "" if data.raw.item[name] then icon = data.raw.item[name].icon if not icon then icon = data.raw.item[name].icons[1].icon end elseif data.raw.fluid[name] then icon = data.raw.fluid[name].icon if not icon then icon = data.raw.fluid[name].icons[1].icon end end return icon end function util.set_to_founding(recipe, options) util.set_category(recipe, "founding", options) util.set_subgroup(recipe, "foundry-intermediate", options) end -- Add crafting category to an entity function util.add_crafting_category(entity_type, entity, category) if data.raw[entity_type][entity] and data.raw["recipe-category"][category] then for i, existing in pairs(data.raw[entity_type][entity].crafting_categories) do if existing == category then return end end table.insert(data.raw[entity_type][entity].crafting_categories, category) end end -- Add crafting category to all entities that have another category function util.add_crafting_category_if(entity_type, category, other_category) if data.raw[entity_type] and data.raw["recipe-category"][category] and data.raw["recipe-category"][other_category] then for _, entity in pairs(data.raw[entity_type]) do local found_good = false local found_bad = false for _, existing in pairs(entity.crafting_categories) do if existing == other_category then found_good = true end if existing == category then found_bad = true end end if found_good and not found_bad then table.insert(entity.crafting_categories, category) end end end end function util.add_to_ingredient(recipe, ingredient, amount, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe] then add_to_ingredient(data.raw.recipe[recipe], ingredient, amount) end end function add_to_ingredient(recipe, it, amount) if recipe ~= nil and recipe.ingredients ~= nil then for i, ingredient in pairs(recipe.ingredients) do if ingredient.name == it then ingredient.amount = ingredient.amount + amount return end if ingredient[1] == it then ingredient[2] = ingredient[2] + amount return end end end end function util.add_to_product(recipe_name, product, amount, options) if not should_force(options) and bypass(recipe_name) then return end if data.raw.recipe[recipe_name] then add_to_product(data.raw.recipe[recipe_name], product, amount) end end function add_to_product(recipe, product, amount) if recipe ~= nil and recipe.results ~= nil then if recipe.result == product then recipe.result_count = recipe.result_count + amount return end for i, result in pairs(recipe.results) do if result.name == product then result.amount = result.amount + amount return end end end end -- Adds a result to a mineable type function util.add_minable_result(t, name, result) if data.raw[t] and data.raw[t][name] and data.raw[t][name].minable then if data.raw[t][name].minable.result and not data.raw[t][name].minable.results then data.raw[t][name].minable.results = { {data.raw[t][name].minable.result ,data.raw[t][name].minable.count}} data.raw[t][name].minable.result = nil data.raw[t][name].minable.result_count = nil end if data.raw[t][name].minable.results then table.insert(data.raw[t][name].minable.results, result) end end end local function insert(nodes, node, value) table.insert(node, value) -- store as parameter if 21 == #node then node = {""} table.insert(nodes, node) end return node end local function encode(data) local node = {""} local root = {node} local n = string.len(data) for i = 1,n,200 do local value = string.sub(data, i, i+199) node = insert(root, node, value) end while #root > 20 do local nodes,node = {},{""} for _, value in ipairs(root) do node = insert(nodes, node, value) end root = nodes end if #root == 1 then root = root[1] else table.insert(root, 1, "") -- no locale template end return #root < 3 and (root[2] or "") or root end function decode(data) if type(data) == "string" then return data end local str = {} for i = 2, #data do str[i-1] = decode(data[i]) end return table.concat(str, "") end function util.create_list() if #me.list>0 then if not data.raw.item[me.name.."-list"] then data:extend({{ type="item", name=me.name.."-list", localised_description = "", enabled=false, icon = "__core__/graphics/empty.png", icon_size = 1, stack_size = 1, hidden = true, hidden_in_factoriopedia = true, flags = {"hide-from-bonus-gui"} }}) end local have = {} local list = {} for i, recipe in pairs(me.list) do if not have[recipe] then have[recipe] = true table.insert(list, recipe) end end if #list>0 then data.raw.item[me.name.."-list"].localised_description = encode(decode(data.raw.item[me.name.."-list"].localised_description).."\n"..table.concat(list, "\n")) end end end function util.remove_prior_unlocks(tech, recipe) if data.raw.technology[tech].prerequisites then for i, prerequisite in pairs(data.raw.technology[tech].prerequisites) do remove_prior_unlocks(prerequisite, recipe) end end end function remove_prior_unlocks(tech, recipe) local technology = data.raw.technology[tech] if technology then util.remove_recipe_effect(tech, recipe) if technology.prerequisites then for i, prerequisite in pairs(technology.prerequisites) do if string.sub(prerequisite, 1, 3) ~= 'ei_' then -- log("BZZZ removing prior unlocks for " .. recipe .. " from " .. tech ..", checking " .. prerequisite) -- Handy Debug :| remove_prior_unlocks(prerequisite, recipe) end end end end end function util.replace_ingredients_prior_to(tech, old, new, multiplier) if not data.raw.technology[tech] then log("Not replacing ingredient "..old.." with "..new.." because tech "..tech.." was not found") return end util.remove_prior_unlocks(tech, old) for i, recipe in pairs(data.raw.recipe) do if (recipe.enabled and recipe.enabled ~= 'false') and (not recipe.hidden or recipe.hidden == 'true') -- probably don't want to change hidden recipes and string.sub(recipe.name, 1, 3) ~= 'se-' -- have to exlude SE in general :( then -- log("BZZZ due to 'enabled' replacing " .. old .. " with " .. new .." in " .. recipe.name) -- Handy Debug :| util.replace_ingredient(recipe.name, old, new, multiplier, true) end end if data.raw.technology[tech].prerequisites then for i, prerequisite in pairs(data.raw.technology[tech].prerequisites) do replace_ingredients_prior_to(prerequisite, old, new, multiplier) end end end function replace_ingredients_prior_to(tech, old, new, multiplier) local technology = data.raw.technology[tech] if technology then if technology.effects then for i, effect in pairs(technology.effects) do if effect.type == "unlock-recipe" then -- log("BZZZ replacing " .. old .. " with " .. new .." in " .. effect.recipe) -- Handy Debug :| util.replace_ingredient(effect.recipe, old, new, multiplier, true) end end end if technology.prerequisites then for i, prerequisite in pairs(technology.prerequisites) do -- log("BZZZ checking " .. prerequisite) -- Handy Debug :| if string.sub(prerequisite, 1, 3) ~= 'ei_' then replace_ingredients_prior_to(prerequisite, old, new, multiplier) end end end end end function util.remove_all_recipe_effects(recipe_name) for name, _ in pairs(data.raw.technology) do util.remove_recipe_effect(name, recipe_name) end end function util.add_unlock_force(technology_name, recipe) util.set_enabled(recipe, false) util.remove_all_recipe_effects(recipe) util.add_unlock(technology_name, recipe) end -- sum the products of a recipe function util.sum_products(recipe_name) -- this is going to end up approximate in some cases, integer division is probs fine if data.raw.recipe[recipe_name] then local recipe = data.raw.recipe[recipe_name] if not recipe.results then return recipe.result_count end local sum = 0 for i, result in pairs(recipe.results) do local amt = 0 if result[2] then amt = result[2] elseif result.amount then amt = result.amount elseif result.amount_min then amt = (result.amount_min + result.amount_max)/2 end if result.probability then amt = amt * result.probability end sum = sum + amt end return sum end return 0 end function util.set_vtk_dcm_ingredients() if mods["vtk-deep-core-mining"] then local sum = util.sum_products("vtk-deepcore-mining-ore-chunk-refining") log("setting vtk deepcore based on " .. serpent.dump(sum) .. " to " ..serpent.dump(sum*0.8)) util.set_ingredient("vtk-deepcore-mining-ore-chunk-refining", "vtk-deepcore-mining-ore-chunk", sum * 0.8) local sum = 1+util.sum_products("vtk-deepcore-mining-ore-chunk-refining-no-uranium") log("setting vtk deepcore no uranium to " .. serpent.dump(sum)) util.set_ingredient("vtk-deepcore-mining-ore-chunk-refining-no-uranium", "vtk-deepcore-mining-ore-chunk", sum) end end -- Recalculate recycling recipes, since our mod might make updates after quality generates them function util.redo_recycling() if mods.quality then local recycling = require("__quality__.prototypes.recycling") for _, recipe in pairs(data.raw.recipe) do recycling.generate_recycling_recipe(recipe) end end end return util