-- Freeware modding by Adamo require("util") -- Basic utils adamo = { opts = { logging = false, debugging = false }, logfile = function(str) if adamo.opts.log and string_or_bust(str) and type(game) == 'table' and type(game.write_file) == 'function' then game.write_file("adamo.log",str,true) end end, debugfile = function(str) if adamo.opts.debugging and string_or_bust(str) and type(game) == 'table' and type(game.write_file) == 'function' then game.write_file("adamo.debug",str,true) end end, log = function(str) if adamo.opts.log and string_or_bust(str) then log(str) end end, debug = function(str) if adamo.opts.debugging and string_or_bust(str) then log("[DEBUG] "..tostring(str)) end end } -- Power production efficiencies. -- Temps determined to maintain blueprint compatibility. -- Applied by my physics mod. boiler_eff = 0.85 gen_eff = 0.5 furnace_eff = 0.65 reactor_eff = 0.80 chem_temp_max = 315 nuke_temp_max = 990 reactor_consump_mult = 2.01 chem_fuel_quotient = boiler_eff*gen_eff nuke_fuel_quotient = reactor_eff*gen_eff -- Based on theoretical max throughput -- of a 3 meter^2 solar panel (5.1kW). solar_panel_eff = 1/10 -- should be 6 kW accumulator_power_quotient = 1/solar_panel_eff adamo.fuel = { factors = { ["topower"] = 1/60, ["coal"] = 3, ["light-oil"] = 10, ["heavy-oil"] = 20, ["petroleum-gas"] = 20, ["crude-oil"] = 30, ["methane"] = 50, }, values = { ["sulfur"] = "600kJ" } } adamo.fuel.factors["syngas"] = adamo.fuel.factors["methane"]*2 -- For backward compatibility. fuelfactors = { topower = adamo.fuel.factors.topower } fuelfactors["coal"] = adamo.fuel.factors["coal"] fuelfactors["light-oil"] = adamo.fuel.factors["light-oil"] fuelfactors["heavy-oil"] = adamo.fuel.factors["heavy-oil"] fuelfactors["petroleum-gas"] = adamo.fuel.factors["petroleum-gas"] fuelfactors["crude-oil"] = adamo.fuel.factors["crude-oil"] fuelfactors["methane"] = adamo.fuel.factors["methane"] fuelfactors["syngas"] = adamo.fuel.factors["syngas"] fuelvalues = { sulfur = adamo.fuel.values.sulfur } -- Chemical constants adamo.chem = { mult = { fluid = 10 }, fluxables = { ["iron-ore"] = "iron-plate", ["copper-ore"] = "copper-plate" }, ratio = { lime_per_clinker = 1, gypsum_per_clinker = 1/100, fluorite_per_clinker = 1/100, clinker_per_cement = 10, gypsum_per_cement = 1, stone_per_concrete = 1, cement_per_concrete = 1/10, quartz_per_concrete = 1/5, quartz_per_redchip = 3/5, fluorite_per_HF = 1/20, lime_per_battery = 1, HF_per_battery = 10, sulfur_per_flask = 1, redchip_per_flask = 3, } } adamo.chem.ratio.lime_per_cement = adamo.chem.ratio.lime_per_clinker * adamo.chem.ratio.clinker_per_cement adamo.chem.ratio.lime_per_concrete = adamo.chem.ratio.lime_per_cement * adamo.chem.ratio.cement_per_concrete adamo.chem.ratio.gypsum_per_lime = adamo.chem.ratio.gypsum_per_cement / adamo.chem.ratio.lime_per_cement adamo.chem.ratio.quart_per_lime = adamo.chem.ratio.quartz_per_concrete / adamo.chem.ratio.lime_per_concrete adamo.chem.ratio.fluorite_per_lime = adamo.chem.ratio.fluorite_per_HF * adamo.chem.ratio.HF_per_battery / adamo.chem.ratio.lime_per_battery adamo.chem.ratio.quartz_per_sulfur = adamo.chem.ratio.quartz_per_redchip * adamo.chem.ratio.redchip_per_flask / adamo.chem.ratio.sulfur_per_flask adamo.geo = { ore = {}, abundance = { calcite = 0.6, quartz, gypsum, sulfur, fluorite, coal, } } -- values must be between 0 and 1 local battery_share = 0.05 local blue_flask_share = 0.1 local coal_recovery = 0.075 adamo.geo.abundance.fluorite = battery_share *adamo.geo.abundance.calcite *adamo.chem.ratio.fluorite_per_lime adamo.geo.abundance.gypsum = adamo.geo.abundance.calcite *adamo.chem.ratio.gypsum_per_lime adamo.geo.abundance.quartz = adamo.geo.abundance.calcite *adamo.chem.ratio.quart_per_lime adamo.geo.abundance.sulfur = adamo.geo.abundance.fluorite + ( blue_flask_share *adamo.geo.abundance.quartz /adamo.chem.ratio.quartz_per_sulfur ) if adamo.geo.abundance.sulfur < 0 then adamo.geo.abundance.sulfur = 0 end local sum = adamo.geo.abundance.calcite +adamo.geo.abundance.fluorite +adamo.geo.abundance.gypsum +adamo.geo.abundance.quartz +adamo.geo.abundance.sulfur adamo.geo.abundance.coal = coal_recovery * (1 - sum) local quartz_recovery = 1/5 local recover_ratio = 1/2 local ore_mineral_norm = recover_ratio * 1 / ( adamo.geo.abundance.fluorite + adamo.geo.abundance.quartz + adamo.geo.abundance.sulfur ) adamo.geo.ore.mineral_results = { ["adamo-chemical-fluorite"] = adamo.geo.abundance.fluorite * ore_mineral_norm, ["adamo-chemical-quartz"] = adamo.geo.abundance.quartz * ore_mineral_norm, ["SiSi-quartz"] = quartz_recovery*adamo.geo.abundance.quartz * ore_mineral_norm, ["sulfur"] = adamo.geo.abundance.sulfur * ore_mineral_norm, ["coal"] = adamo.geo.abundance.coal * ore_mineral_norm, } adamo.geo.ore.impurity_ratio = 1/20 -- Physical quantities physical_units = { "J", "W", "C" } metrics = { "K","M","G","T","P","E","Z","Y" } get_phynum_val = function(phynum) local phynum,val = phynum_or_bust(phynum) return val end phynum_or_bust = function(phynum) if type(phynum) == 'string' then local val = phynum:match "[0-9%.]+" local unit = phynum:match "%a+" if val and unit then return val..unit,val,unit end end return nil,nil,nil end energy_or_bust = function(energy) if type(energy) == 'number' then return energy..'J',energy,'J' end return phynum_or_bust(energy) end power_or_bust = function(power) if type(power) == 'number' then return power..'W',power,'W' end return phynum_or_bust(power) end energy_to_power = function(energy) return energy_mult(energy,fuelfactors.topower) end power_to_energy = function(energy) return energy_div(energy,fuelfactors.topower) end energy_mult = function(energy,mult) local energy = energy_or_bust(energy) return phynum_mult(energy,mult) end energy_div = function(energy,div) local energy = energy_or_bust(energy) return phynum_div(energy,div) end phynum_mult = function(phynum,mult) local e,val,unit = phynum_or_bust(phynum) if val and unit then e = (val*mult)..unit end return e end phynum_div = function(phynum,div) local e,val,unit = phynum_or_bust(phynum) if val and unit then e = (val/div)..unit end return e end add_prereq_to_tech = function(tech,prereq_name) local tech = tech_or_bust(tech) local prereq_name = string_or_bust(prereq_name) if not tech or not prereq_name then return nil end return add_string_to_list(tech.prerequisites,prereq_name) end add_prereqs_to_tech = function (tech,prereq_table) local prereq_table = table_or_bust(prereq_table) if not prereq_table then return false end local return_val = true for _,prereq_name in pairs(prereq_table) do if not add_prereq_to_tech(tech,prereq_name) then return_val = false end end return return_val end remove_tech = function(old_tech) local old_tech = tech_or_bust(old_tech) if not old_tech then return false end for _,tech in pairs(data.raw.technology) do remove_val_from_table( tech.prerequisites, old_tech.name ) end data.raw.technology[old_tech.name] = nil return true end add_recipe_to_tech = function(tech_name,recipe_name) local recipe_name = string_name_or_bust(recipe_name) local tech = false if type(tech_name) == "table" and tech_name.type == "technology" then tech = tech_name elseif type(tech_name) == "string" then tech = data.raw["technology"][tech_name] end if tech then if not tech.effects then tech.effects = {{ type="unlock-recipe", recipe=recipe_name }} else table.insert(tech.effects, { type="unlock-recipe", recipe=recipe_name }) end return true end return false end add_tech_recipe = function(tech_name,recipe_name) return add_recipe_to_tech(tech_name,recipe_name) end add_recipe_tech = function(tech_name,recipe_name) return add_recipe_to_tech(tech_name,recipe_name) end add_recipes_to_tech = function(tech_name,name_table) if type(name_table) ~= "table" then return nil end for _,name in pairs(name_table) do add_recipe_to_tech(tech_name,name) end end move_recipe_to_tech = function(tech_name,recipe_name) if add_tech_recipe(tech_name,recipe_name) then remove_recipe_all_techs(recipe_name) add_tech_recipe(tech_name,recipe_name) return true end return false end move_recipes_to_tech = function(tech_name,name_table) if type(name_table) ~= "table" then return false end for _,name in pairs(name_table) do move_recipe_to_tech(tech_name,name) end end remove_recipe_all_techs = function(recipe_name) for _,tech in pairs(data.raw.technology) do remove_tech_recipe(tech.name,recipe_name) end end remove_tech_recipe = function(tech_name,recipe_name) if type(recipe_name) == "table" then remove_tech_recipes(recipe_name) end local recipe_name = string_name_or_bust(recipe_name) if not recipe_name then return nil end local tech = false if type(tech_name) == "table" and tech_name.type == "technology" then tech = tech_name elseif type(tech_name) == "string" then tech = data.raw["technology"][tech_name] end if tech then if not tech.effects then return false elseif type(tech.effects) == "table" then return remove_val_from_table( tech.effects, { type="unlock-recipe", recipe=recipe_name } ) end return true end return false end remove_tech_recipes = function(tech_name,name_table) if type(name_table) ~= "table" then return nil end for _,name in pairs(name_table) do remove_tech_recipe(tech_name,name) end end find_recipe_techs = function(recipe_name) local tech_names = {} local recipe_name = string_name_or_bust(recipe_name) for k,tech in pairs(data.raw.technology) do local found_tech = false if tech.effects then for l,effect in pairs(tech.effects) do if effect.type == "unlock-recipe" and effect.recipe == recipe_name then table.insert( tech_names, tech.name ) end end end end return tech_names end all_recipes_of_category = function(category_str) if category_str == "crafting" then return smoosh_tables( all_prototypes_with_value( "recipe", "category", category_str ), all_prototypes_with_value( "recipe", "category", nil ) ) end return all_prototypes_with_value( "recipe", "category", category_str ) end mirror_recipe_in_techs = function(orig_recipe,new_recipe) local tech_names = find_recipe_techs(orig_recipe) for _,tech_name in pairs(tech_names) do add_recipe_to_tech(tech_name,new_recipe) end end mult_recipe_energy = function(recipe,mult) local recipe = recipe_or_bust(recipe) if recipe.normal then recipe.normal.energy_required = (recipe.normal.energy_required or 0.5) * mult if recipe.expensive then recipe.expensive.energy_required = (recipe.expensive.energy_required or 0.5) * mult end else recipe.energy_required = (recipe.energy_required or 0.5) * mult end end get_recipe_category = function(recipe) local recipe = recipe_or_bust(recipe) if not recipe then return false end if recipe.category then return recipe.category end if recipe.expensive and recipe.expensive.category then return recipe.expensive.category end if recipe.normal and recipe.normal.category then return recipe.normal.category end return "crafting" end get_recipe_energy = function(recipe) local recipe = recipe_or_bust(recipe) if not recipe then return nil,0,0 end local normal_energy_required = 1 local expensive_energy_required = 1 if recipe.normal then normal_energy_required = recipe.normal.energy_required if recipe.expensive then expensive_energy_required = recipe.expensive.energy_required end else return (recipe.energy_required or 0.5) end return normal_energy_required,expensive_energy_required end set_recipe_hidden = function(recipe) recipe = table_or_bust(recipe) if not recipe then return nil end if recipe.normal and type(recipe.normal) == "table" then recipe.normal.hidden = true end if recipe.expensive and type(recipe.expensive) == "table" then recipe.expensive.hidden = true end recipe.hidden = true return true end mult_recipe_io = function(recipe,mult,name) mult_in_recipe(recipe,mult,name) end mult_in_recipe = function(recipe,mult,name) if name then replace_recipe_io(recipe,name,name,mult) else local ingredients,results = get_io_names(recipe) for _,name in pairs(ingredients) do replace_recipe_io(recipe,name,name,mult) end for _,name in pairs(results) do replace_recipe_io(recipe,name,name,mult) end end end replace_in_recipe_io = function(recipe,old_name,new_name,mult) io_manip(recipe,old_name,new_name,mult) end replace_recipe_io = function(recipe,old_name,new_name,mult) replace_in_recipe(recipe,old_name,new_name,mult) end replace_in_recipe = function(recipe,old_name,new_name,mult) io_manip(recipe,old_name,new_name,mult) end io_manip = function(io_handler,old_name,new_name,mult) local io_handler = io_handler_or_bust(io_handler) local old_name = string_name_or_bust(old_name) local new_io_type = get_io_type(new_name) local new_name = string_name_or_bust(new_name) local new_normal = 0 local new_expensive = 0 local new_amount = 0 if type(new_name) == "number" then mult = new_name new_name = old_name end if not io_handler or not old_name or not new_name then return false end if not new_io_type then new_io_type = "item" end local mult = number_or_one(mult) if io_handler.normal then new_normal = io_manip( io_handler.normal,old_name,new_name,mult ) end if io_handler.expensive then new_expensive = io_manip( io_handler.expensive,old_name,new_name,mult ) end if io_handler.result then if io_handler.result == old_name then if new_io_type == "item" then io_handler.result = new_name adamo.debug(io_handler.result..": "..(io_handler.result_count or 1)) io_handler.result_count = (io_handler.result_count or 1) * mult new_amount = io_handler.result_count adamo.debug("Replaced as simple result. Count: "..new_amount) else if not io_handler.results then io_handler.results = {} end table.insert( io_handler.results, { name = new_name, type = new_io_type, amount = (io_handler.result_count or 1)*mult } ) io_handler.main_product = new_name new_amount = (io_handler.result_count or 1)*mult io_handler.result = nil io_handler.result_count = nil adamo.debug("Moved result to results. Count: "..new_amount) end end end for _,ingredient in pairs(io_handler.ingredients or {}) do if ingredient.name == old_name then ingredient.name = new_name ingredient.type = new_io_type adamo.debug(ingredient.name..": "..ingredient.amount) ingredient.amount = (ingredient.amount or ingredient[2] or 1)*mult ingredient[2] = nil new_amount = ingredient.amount adamo.debug("Replaced in table by name. Count: "..new_amount) end if ingredient[1] == old_name then ingredient.name = new_name ingredient.amount = (ingredient[2] or ingredient.amount or 1)*mult ingredient.type = new_io_type ingredient[1] = nil ingredient[2] = nil new_amount = ingredient.amount adamo.debug("Replaced in table by index. Count: "..new_amount) end if ingredient.name == nil and ingredient[1] == nil then ingredient = nil end end for _,result in pairs(io_handler.results or {}) do if result.name == old_name then result.name = new_name result.type = new_io_type adamo.debug(result.name..": "..result.amount) result.amount = math.floor( (result.amount or result[2] or 1)*mult ) if new_io_type == "fluid" then result.amount = result.amount *(result.probability or 1) result.probability = nil end result[2] = nil new_amount = result.amount adamo.debug("Replaced in table by name. Count: "..new_amount) end if result[1] == old_name then result.name = new_name result.amount = math.floor( (result[2] or result.amount or 1)*mult ) result.type = new_io_type if new_io_type == "fluid" then result.amount = result.amount *(result.probability or 1) result.probability = nil end result[1] = nil result[2] = nil new_amount = result.amount adamo.debug("Replaced in table by index. Count: "..new_amount) end if result.name == nil and result[1] == nil then result = nil end end if io_handler.main_product == old_name then adamo.debug("Replaced main product.") io_handler.main_product = new_name end if io_handler.name then adamo.debug( 'Replaced '..(io_handler.name)..' IO: ' ..old_name..' --> '..new_name..' ['..new_io_type..']' .." x"..mult.." => {"..new_amount..", "..new_normal..", "..new_expensive.."}" ) end return new_amount end remove_ingredient = function(recipe,ingred_name) local recipe = recipe_or_bust(recipe) local ingred_name = string_name_or_bust(ingred_name,count,prob) local found_and_removed = false if not recipe or not ingred_name then return found_and_removed end if recipe.expensive then for _,ingredient in pairs(recipe.expensive.ingredients or {}) do if ingredient.name == ingred_name or ingredient[1] == ingred_name then recipe.expensive.ingredients[_] = nil found_and_removed = true end end end if recipe.normal then for _,ingredient in pairs(recipe.normal.ingredients or {}) do if ingredient.name == ingred_name or ingredient[1] == ingred_name then recipe.normal.ingredients[_] = nil found_and_removed = true end end end for _,ingredient in pairs(recipe.ingredients or {}) do if ingredient.name == ingred_name or ingredient[1] == ingred_name then recipe.ingredients[_] = nil found_and_removed = true end end return found_and_removed end blend_io_if_match = function(io_base,io_counter) local io_base = io_prototype_or_bust(io_base) local io_counter = io_prototype_or_bust(io_counter) if not io_counter or not io_base then return end local found_match = false if io_base.name == io_counter.name and ( io_base.type == io_counter.type or ( io_base.type == "item" or io_counter.type == "item" ) ) then found_match = true if io_base.probability or io_counter.probability then io_base.probability = ( (io_base.probability or 1) *io_base.amount + (io_counter.probability or 1) *io_counter.amount ) /(io_base.amount + io_counter.amount) if io_base.probability == 1 then io_base.probability = nil end end io_base.amount = io_base.amount + io_counter.amount end adamo.debug( "Blended: " ..tostring(io_base.name).."[" ..tostring(io_base.amount)..", " ..tostring(io_base.probability).."] <-> [" ..tostring(io_counter.name).."[" ..tostring(io_counter.amount)..", " ..tostring(io_counter.probability).."]" ) return found_match end add_ingredient = function(recipe,ingred,count,prob,catalyst_amount) if type(count) == "number" and count == 0 then return nil end if type(prob) == "number" and count == 0 then return nil end local recipe = recipe_or_bust(recipe) local ingred = io_prototype_or_bust(ingred,count,prob,catalyst_amount) if type(prob) ~= "number" then prob = nil end if not recipe or not ingred then return nil end if uses_ingredient(recipe,ingred.name) then if recipe.expensive then for _,ingredient in pairs(recipe.expensive.ingredients or {}) do blend_io_if_match(ingredient,ingred) end end if recipe.normal then for _,ingredient in pairs(recipe.normal.ingredients or {}) do blend_io_if_match(ingredient,ingred) end end for _,ingredient in pairs(recipe.ingredients or {}) do blend_io_if_match(ingredient,ingred) end else if recipe.expensive then local exp_ingred = util.table.deepcopy(ingred) if not recipe.expensive.ingredients then recipe.expensive.ingredients = {} end table.insert(recipe.expensive.ingredients,exp_ingred) end if recipe.normal then if not recipe.normal.ingredients then recipe.normal.ingredients = {} end table.insert(recipe.normal.ingredients,ingred) end if recipe.ingredients then table.insert(recipe.ingredients,ingred) elseif not recipe.normal and not recipe.expensive then recipe.ingredients = {} table.insert(recipe.ingredients,ingred) end end end add_result = function(recipe,product,count,prob) if type(count) == "number" and count == 0 then return nil end if type(prob) == "number" and count == 0 then return nil end local recipe = recipe_or_bust(recipe) local product = io_prototype_or_bust(product,count,prob) if not recipe or not product then return nil end if type(count) ~= "number" then count = nil end if type(prob) ~= "number" then prob = nil end format_results(recipe) if uses_result(recipe,product) then if recipe.expensive then for _,result in pairs(recipe.expensive.results or {recipe.expensive.result} ) do blend_io_if_match(result,product) end end if recipe.normal then for _,result in pairs(recipe.normal.results or {recipe.normal.result} ) do blend_io_if_match(result,product) end end for _,result in pairs(recipe.results or {recipe.result} ) do blend_io_if_match(result,product) end else if recipe.expensive then local exp_product = util.table.deepcopy(product) exp_product.amount = (exp_product.amount or 1)*2 table.insert(recipe.expensive.results,exp_product) end if recipe.normal then table.insert(recipe.normal.results,product) end if recipe.results then table.insert(recipe.results,product) elseif not recipe.normal and not recipe.expensive then recipe.results = {} table.insert(recipe.results,product) end end end move_result_to_main_product = function(recipe) local recipe = recipe_or_bust(recipe) if not recipe then return false end if recipe.expensive then if not recipe.expensive.results or type(recipe.expensive.results) ~= "table" then if recipe.expensive.result then recipe.expensive.results = {{ type = "item", name = recipe.expensive.result, amount = recipe.expensive.result_count or 1 }} if not recipe.expensive.main_product then recipe.expensive.main_product = recipe.expensive.result end recipe.expensive.result = nil recipe.expensive.result_count = nil end elseif recipe.expensive.result then if not recipe.expensive.main_product then recipe.expensive.main_product = recipe.expensive.result end table.insert( results, { type = "item", name = recipe.expensive.result, amount = (recipe.expensive.result_count or 1) } ) recipe.expensive.result = nil recipe.expensive.result_count = nil end end if recipe.normal then if not recipe.normal.results or type(recipe.normal.results) ~= "table" then if recipe.normal.result then recipe.normal.results = {{ type = "item", name = recipe.normal.result, amount = recipe.normal.result_count or 1 }} if not recipe.normal.main_product then recipe.normal.main_product = recipe.normal.result end recipe.normal.result = nil recipe.normal.result_count = nil end elseif recipe.normal.result then if not recipe.normal.main_product then recipe.normal.main_product = recipe.normal.result end table.insert( results, { type = "item", name = recipe.normal.result, amount = (recipe.normal.result_count or 1) } ) recipe.normal.result = nil recipe.normal.result_count = nil end end if not recipe.results or type(recipe.results) ~= "table" then if recipe.result then recipe.results = {{ type = "item", name = recipe.result, amount = recipe.result_count or 1 }} if not recipe.main_product then recipe.main_product = recipe.result end recipe.result = nil recipe.result_count = nil end elseif recipe.result then if not recipe.main_product then recipe.main_product = recipe.result end table.insert( results, { type = "item", name = recipe.result, amount = (recipe.result_count or 1) } ) recipe.result = nil recipe.result_count = nil end end format_results = function(recipe) local recipe = recipe_or_bust(recipe) if not recipe then return false end move_result_to_main_product(recipe) -- needs to reformat the results table, yet. end format_ingredients = function(recipe) local recipe = recipe_or_bust(recipe) if not recipe then return false end if recipe.normal and type(recipe.normal) == "table" then for _,ingredient in pairs(recipe.normal.ingredients or {}) do if ingredient[1] then ingredient.name = ingredient[1] ingredient[1] = nil end if ingredient[2] then ingredient.amount = ingredient[2] ingredient[2] = nil end if ingredient.name and not ingredient.type then ingredient.type = "item" end end end if recipe.expensive and type(recipe.expensive) == "table" then for _,ingredient in pairs(recipe.expensive.ingredients or {}) do if ingredient[1] then ingredient.name = ingredient[1] ingredient[1] = nil end if ingredient[2] then ingredient.amount = ingredient[2] ingredient[2] = nil end if ingredient.name and not ingredient.type then ingredient.type = "item" end end end for _,ingredient in pairs(recipe.ingredients or {}) do if ingredient[1] then ingredient.name = ingredient[1] ingredient[1] = nil end if ingredient[2] then ingredient.amount = ingredient[2] ingredient[2] = nil end if ingredient.name and not ingredient.type then ingredient.type = "item" end end return true end set_main_product = function(recipe,name) local recipe = recipe_or_bust(recipe) local name = string_name_or_bust(name) if not recipe or not name then return false end if not uses_result(recipe,name) then return false end if recipe.normal then recipe.normal.main_product = name end if recipe.expensive then recipe.expensive.main_product = name end if recipe.results or recipe.result then recipe.main_product = name end end get_main_result = function(recipe) local recipe = recipe_or_bust(recipe) if not recipe then return nil,0,0,0 end local result_name = nil local result_count = nil local expensive_result_count = 0 local normal_result_count = 0 if recipe.expensive then if recipe.expensive.results then for _,result in pairs(recipe.expensive.results) do if result_name == nil then result_name = result.name or result[1] end if (result.name == result_name) or (result[1] == result_name) then expensive_result_count = result.amount or result[2] end end elseif recipe.expensive.result then if result_name == nil then result_name = recipe.expensive.result end expensive_result_count = recipe.expensive.result_count or 1 end end if recipe.normal then if recipe.normal.results then for _,result in pairs(recipe.normal.results) do if result_name == nil then result_name = result.name or result[1] end if (result.name == result_name) or (result[1] == result_name) then normal_result_count = result.amount or result[2] end end elseif recipe.normal.result then if result_name == nil then result_name = recipe.normal.result end normal_result_count = recipe.normal.result_count or 1 end end if recipe.results then for _,result in pairs(recipe.results) do if result_name == nil then result_name = result.name or result[1] end if (result.name == result_name) or (result[1] == result_name) then result_count = result.amount or result[2] end end elseif recipe.result then result_count = recipe.result_count or 1 result_name = recipe.result end if result_count == nil then result_count = normal_result_count or expensive_result_count or 0 end return result_name,result_count,expensive_result_count end has_ingredients = function(recipe) local recipe = recipe_or_bust(recipe) if recipe then if recipe.normal then if recipe.normal.ingredients then for _,ingredient in pairs(recipe.normal.ingredients) do if ingredient.amount and ingredient.amount > 0 then return true end if ingredient[2] and ingredient[2] > 0 then return true end end end end if recipe.ingredients then for _,ingredient in pairs(recipe.ingredients) do if ingredient.amount and ingredient.amount > 0 then return true end if ingredient[2] and ingredient[2] > 0 then return true end end end end return false end has_results = function(recipe) local recipe = recipe_or_bust(recipe) if not recipe then return false end if recipe.expensive then if recipe.expensive.results then for _,result in pairs(recipe.expensive.results) do if result.amount and result.amount > 0 then return true end if result[2] and result[2] > 0 then return true end end end if recipe.normal.result then return true end end if recipe.normal then if recipe.normal.results then for _,result in pairs(recipe.normal.results) do if result.amount and result.amount > 0 then return true end if result[2] and result[2] > 0 then return true end end end if recipe.normal.result then return true end end if recipe.results then for _,result in pairs(recipe.results) do if result.amount and result.amount > 0 then return true end if result[2] and result[2] > 0 then return true end end end if recipe.result then return true end return false end get_io_names = function(recipe) local recipe = recipe_or_bust(recipe) if not recipe or recipe.type ~= "recipe" then return {} end local ingredients = {} local results = {} if recipe.expensive then add_strings_to_list( ingredients, get_table_names(recipe.expensive.ingredients) ) add_strings_to_list( results, get_table_names(recipe.expensive.results) ) add_string_to_list( results, recipe.expensive.result ) end if recipe.normal then add_strings_to_list( ingredients, get_table_names(recipe.normal.ingredients) ) add_strings_to_list( results, get_table_names(recipe.normal.results) ) add_string_to_list( results, recipe.normal.result ) end add_strings_to_list( ingredients, get_table_names(recipe.ingredients) ) add_strings_to_list( results, get_table_names(recipe.results) ) add_string_to_list( results, recipe.result ) return ingredients,results end recipe_uses = function(recipe,name) local name = string_name_or_bust(name) local recipe = recipe_or_bust(recipe) if not recipe or not name then return false end local ingredients,results = get_io_names(recipe) for _,ingredient_name in pairs(ingredients) do if ingredient_name == name then return true end end for _,result_name in pairs(results) do if (result_name) == name then return true end end return false end uses_ingredient = function(recipe,name) local name = string_name_or_bust(name) local recipe = recipe_or_bust(recipe) if not recipe or not name then return false end local ingredients,results = get_io_names(recipe) for _,ingredient_name in pairs(ingredients) do if ingredient_name == name then return true end end return false end uses_result = function(recipe,name) local name = string_name_or_bust(name) local recipe = recipe_or_bust(recipe) if not recipe or not name then return false end local ingredients,results = get_io_names(recipe) for _,result_name in pairs(results) do if (result_name) == name then return true end end return false end get_io_type = function(prototype) if type(prototype) == "table" then local io_type = get_table_type(prototype) if io_type == "item" then return "item" elseif io_type == "fluid" then return "fluid" end end if type(prototype) == "string" then if data.raw.item[prototype] then return "item" elseif data.raw.fluid[prototype] then return "fluid" end end return nil end get_table_type = function(prototype) if type(prototype) == "table" then return prototype.type end return nil end get_table_names = function(prototable) local names = {} local prototable = table_or_bust(prototable) if not prototable then return end for _,prototype in pairs(prototable) do prototype = table_or_bust(prototype) if prototype then if prototype.name and type(prototype.name) == "string" then table.insert( names, prototype.name ) elseif prototype[1] and type(prototype[1]) == "string" then table.insert( names, prototype[1] ) end end end return names end get_ingredient = function(recipe,index) local recipe = recipe_or_bust(recipe) if type(recipe) ~= "table" then return end local ingredient_name = index local ingredient_amount = 0 local expensive_amount = 0 if not has_ingredients(recipe) then return nil,0,0 end if recipe.normal and type(recipe.normal.ingredients) == "table" then if type(ingredient_name) == "string" then for _,ingredient in pairs(recipe.normal.ingredients) do if (ingredient.name == ingredient_name) or (ingredient[1] == ingredient_name) then ingredient_amount = ingredient.amount or ingredient[2] end end if ingredient_amount == 0 then return nil,0,0 end if recipe.expensive then for _,ingredient in pairs(recipe.expensive.ingredients or {}) do if (ingredient.name == ingredient_name) or (ingredient[1] == ingredient_name) then expensive_amount = ingredient.amount or ingredient[2] end end end else if type( recipe.normal.ingredients[ingredient_name] ) == "table" then ingredient_amount = recipe.normal .ingredients[ingredient_name].amount or recipe.normal .ingredients[ingredient_name][2] ingredient_name = recipe.normal .ingredients[ingredient_name].name or recipe.normal .ingredients[ingredient_name][1] if recipe.expensive then for _,ingredient in pairs(recipe.expensive.ingredients) do if ( ingredient.name == ingredient_name ) or ( ingredient[1] == ingredient_name ) then expensive_amount = ingredient.amount or ingredient[2] end end end else return nil,0,0 end end else if type(ingredient_name) == "string" then for _,ingredient in pairs(recipe.ingredients or {}) do if (ingredient.name == ingredient_name) or (ingredient[1] == ingredient_name) then ingredient_amount = ingredient.amount or ingredient[2] end end if ingredient_amount == 0 then return nil,0,0 end else if type(recipe.ingredients[ingredient_name]) == "table" then ingredient_amount = recipe.ingredients[ingredient_name] .amount or recipe.ingredients[ingredient_name][2] ingredient_name = recipe.ingredients[ingredient_name].name or recipe.ingredients[ingredient_name][1] else return nil,0,0 end end end return ingredient_name,ingredient_amount,expensive_amount end -- Might be broken get_result = function(recipe,index) local recipe = recipe_or_bust(recipe) if not recipe then return end format_results(recipe) if not has_results(recipe) then return nil,0,0 end local result_name = index local result_amount = 0 local expensive_amount = 0 if recipe.normal and type(recipe.normal.results) == "table" then if type(result_name) == "string" then for _,result in pairs(recipe.normal.results) do if (result.name == result_name) or (result[1] == result_name) then result_amount = result.amount or result[2] end end if result_amount == 0 then return nil,0,0 end if recipe.expensive then for _,result in pairs(recipe.expensive.results or {}) do if (result.name == result_name) or (result[1] == result_name) then expensive_amount = result.amount or result[2] end end end else if type( recipe.normal.results[result_name] ) == "table" then result_amount = recipe.normal .results[result_name].amount or recipe.normal .results[result_name][2] result_name = recipe.normal .results[result_name].name or recipe.normal .results[result_name][1] if recipe.expensive then for _,result in pairs(recipe.expensive.results) do if ( result.name == result_name ) or ( result[1] == result_name ) then expensive_amount = result.amount or result[2] end end end else return nil,0,0 end end else if type(result_name) == "string" then for _,result in pairs(recipe.results or {}) do if (result.name == result_name) or (result[1] == result_name) then result_amount = result.amount or result[2] end end if result_amount == 0 then return nil,0,0 end else if type(recipe.results[result_name]) == "table" then result_amount = recipe.results[result_name] .amount or recipe.results[result_name][2] result_name = recipe.results[result_name].name or recipe.results[result_name][1] else return nil,0,0 end end end return result_name,result_amount,expensive_amount end set_productivity_recipe = function(recipe_name) local recipe_name = string_name_or_bust(recipe_name) if not recipe_name then return end for k,v in pairs({ data.raw.module["productivity-module"], data.raw.module["productivity-module-2"], data.raw.module["productivity-module-3"] }) do if v.limitation then table.insert(v.limitation, recipe_name) end end end set_productivity_recipes = function(recipe_names) for _,recipe_name in pairs(recipe_names or {}) do set_productivity_recipe(recipe_name) end end add_productivity_recipe = function(recipe_name) set_productivity_recipe(recipe_name) end add_productivity_recipes = function(recipe_names) set_productivity_recipes(recipe_names) end is_productivity_recipe = function(recipe_name) local recipe_name = string_name_or_bust(recipe_name) if not data.raw.recipe[recipe_name] then return false end for k,listed_recipe_name in pairs( data.raw.module["productivity-module"].limitation ) do if listed_recipe_name == recipe_name then return true end end return false end find_unused_layer = function() local unused_layers = { "layer-11", "layer-12", "layer-13", "layer-14", "layer-15", } for i,data_type in pairs(data.raw) do for j,data_obj in pairs(data_type) do for k,layer in pairs(data_obj.collision_mask or {}) do for l,unused_layer in pairs(unused_layers) do if layer == unused_layer then unused_layers[l] = nil end end end end end for _,layer in pairs(unused_layers) do return layer end return nil end -- Not really checking if it's an entit, -- but entity definitely has to have "order". entity_or_bust = function(entity) local entity = table_or_bust(entity) return entity end get_collision_lengths = function(entity) local lengths = {0,0} if entity_or_bust(entity) then local colbox = entity.collision_box if type(colbox) == "table" then if type(colbox[1]) == "table" then if type(colbox[1][1]) == "number" then lengths[1] = lengths[1] - colbox[1][1] end if type(colbox[1][2]) == "number" then lengths[2] = lengths[2] - colbox[1][2] end end if type(colbox[2]) == "table" then if type(colbox[2][1]) == "number" then lengths[1] = lengths[1] + colbox[2][1] end if type(colbox[2][2]) == "number" then lengths[2] = lengths[2] + colbox[2][2] end end end end return lengths end get_collision_hypotenuse = function(entity) local len = get_collision_lengths(entity) return math.sqrt(len[1]*len[1] + len[2]*len[2]) end get_collision_area = function(entity) local len = get_collision_lengths(entity) return len[1]*len[2] end set_pipe_distance = function(pipe, dist) if data.raw["pipe-to-ground"][pipe] then for _,connection in pairs( data.raw["pipe-to-ground"][pipe] .fluid_box.pipe_connections ) do if connection.max_underground_distance then data.raw["pipe-to-ground"][pipe] .fluid_box.pipe_connections[_] .max_underground_distance = dist end end end end set_shift = function(shift, tab) tab.shift = shift if tab.hr_version then tab.hr_version.shift = shift end return tab end -- probably doesn't work as written: could work -- with sufficiently large empty image. empty_animation = function(frames,speed) if type(frames) ~= "number" then frames = 1 end if type(speed) ~= "number" then speed = 1 end return { frame_count = frames, speed = speed, type = "forward-then-backward", filename = "__core__/graphics/empty.png", priority = "extra-high", line_length = 1/math.sqrt(frames), width = 1, height = 1 } end empty_sprite = function() return { filename = "__core__/graphics/empty.png", priority = "extra-high", width = 1, height = 1, hr_version = { filename = "__core__/graphics/empty.png", priority = "extra-high", width = 1, height = 1, } } end emptysprite = function() return empty_sprite() end centrifuge_idle_layers = function(size,speed) local size = number_or_one(size) local speed = number_or_one(speed) return {{ filename = "__base__/graphics/entity/" .."centrifuge/centrifuge-C.png", priority = "extra-high", line_length = 8, width = 119, height = 107, scale = size, frame_count = 64, animation_speed = speed, shift = util.by_pixel( -0.5, -26.5 ), hr_version = { filename = "__base__/graphics/entity/" .."centrifuge/hr-centrifuge-C.png", priority = "extra-high", scale = size*0.5, line_length = 8, width = 237, height = 214, frame_count = 64, animation_speed = speed, shift = util.by_pixel( -0.25, -26.5 ) } },{ filename = "__base__/graphics/entity/" .."centrifuge/centrifuge-C-shadow.png", draw_as_shadow = true, priority = "extra-high", line_length = 8, width = 132, height = 74, frame_count = 64, scale = size, animation_speed = speed, shift = util.by_pixel( 20, -10 ), hr_version = { filename = "__base__/graphics/entity/" .."centrifuge/hr-centrifuge-C-shadow.png", draw_as_shadow = true, priority = "extra-high", scale = size*0.5, line_length = 8, width = 279, height = 152, frame_count = 64, animation_speed = speed, shift = util.by_pixel( 16.75, -10 ) } },{ filename = "__base__/graphics/entity/" .."centrifuge/centrifuge-B.png", priority = "extra-high", line_length = 8, width = 78, height = 117, scale = size, frame_count = 64, animation_speed = speed, shift = util.by_pixel( 23, 6.5 ), hr_version = { filename = "__base__/graphics/entity/" .."centrifuge/hr-centrifuge-B.png", priority = "extra-high", scale = size*0.5, line_length = 8, width = 156, height = 234, frame_count = 64, animation_speed = speed, shift = util.by_pixel( 23, 6.5 ) } },{ filename = "__base__/graphics/entity/" .."centrifuge/centrifuge-B-shadow.png", draw_as_shadow = true, priority = "extra-high", line_length = 8, width = 124, height = 74, frame_count = 64, scale = size, animation_speed = speed, shift = util.by_pixel( 63, 16 ), hr_version = { filename = "__base__/graphics/entity/" .."centrifuge/hr-centrifuge-B-shadow.png", draw_as_shadow = true, priority = "extra-high", scale = size*0.5, line_length = 8, width = 251, height = 149, frame_count = 64, animation_speed = speed, shift = util.by_pixel( 63.25, 15.25 ) } },{ filename = "__base__/graphics/entity/" .."centrifuge/centrifuge-A.png", priority = "extra-high", line_length = 8, width = 70, height = 123, scale = size, frame_count = 64, animation_speed = speed, shift = util.by_pixel( -26, 3.5 ), hr_version = { filename = "__base__/graphics/entity/" .."centrifuge/hr-centrifuge-A.png", priority = "extra-high", scale = size*0.5, line_length = 8, width = 139, height = 246, frame_count = 64, animation_speed = speed, shift = util.by_pixel( -26.25, 3.5 ) } },{ filename = "__base__/graphics/entity/" .."centrifuge/centrifuge-A-shadow.png", priority = "extra-high", draw_as_shadow = true, line_length = 8, width = 108, height = 54, frame_count = 64, scale = size, animation_speed = speed, shift = util.by_pixel( 6, 27 ), hr_version = { filename = "__base__/graphics/entity/" .."centrifuge/hr-centrifuge-A-shadow.png", priority = "extra-high", draw_as_shadow = true, scale = size*0.5, line_length = 8, width = 230, height = 124, frame_count = 64, animation_speed = speed, shift = util.by_pixel( 8.5, 23.5 ) } }} end bulkypipepictures = function() local pipe_sprites = pipepictures() return { north = set_shift( {0, 1}, util.table .deepcopy(pipe_sprites.straight_vertical) ), south = empty_sprite(), east = set_shift( {-1, 0}, util.table .deepcopy(pipe_sprites.straight_horizontal) ), west = set_shift( {1, 0}, util.table .deepcopy(pipe_sprites.straight_horizontal) ) } end chem_assembling_machine_fluid_boxes = function() return {{ production_type = "input", pipe_picture = assembler3pipepictures(), pipe_covers = pipecoverspictures(), base_area = 1, base_level = -1, height = 2, pipe_connections = {{ type="input-output", position = {0, -2} }}, secondary_draw_orders = { north = -1 } },{ production_type = "input", pipe_picture = assembler3pipepictures(), pipe_covers = pipecoverspictures(), base_area = 1, base_level = -1, height = 2, pipe_connections = {{ type="input-output", position = {2, 0} }}, secondary_draw_orders = { north = -1 } },{ production_type = "output", pipe_picture = assembler3pipepictures(), pipe_covers = pipecoverspictures(), base_area = 1, base_level = 1, pipe_connections = {{ type="output", position = {-2, 0} }}, secondary_draw_orders = { north = -1 } },{ production_type = "input", pipe_picture = assembler3pipepictures(), pipe_covers = pipecoverspictures(), base_area = 1, base_level = -1, height = 2, pipe_connections = {{ type="input-output", position = {0, 2} }}, secondary_draw_orders = { north = -1 } }, off_when_no_fluid_recipe = true } end centrifuge_fluid_boxes = function() return {{ production_type = "input", base_area = 1, base_level = -1, pipe_picture = assembler3pipepictures(), pipe_covers = pipecoverspictures(), pipe_connections = {{ position = { 0, 2 }, }}, secondary_draw_orders = { north = -32, west = -32, east = -32, south = 4 } },{ production_type = "output", base_area = 1, base_level = 1, pipe_picture = assembler3pipepictures(), pipe_covers = pipecoverspictures(), pipe_connections = {{ position = { -2, 0 }, }}, secondary_draw_orders = { north = -32, west = -32, east = -32, south = 4 } },{ production_type = "output", base_area = 1, base_level = 1, pipe_picture = assembler3pipepictures(), pipe_covers = pipecoverspictures(), pipe_connections = {{ position = { 0, -2 }, }}, secondary_draw_orders = { north = -32, west = -32, east = -32, south = 4 } },{ production_type = "output", base_area = 1, base_level = 1, pipe_picture = assembler3pipepictures(), pipe_covers = pipecoverspictures(), pipe_connections = {{ position = { 2, 0 }, }}, secondary_draw_orders = { north = -32, west = -32, east = -32, south = 4 }, }, off_when_no_fluid_recipe = true } end trivial_smoke = function(opts) return { type = "trivial-smoke", name = opts.name, duration = opts.duration or 600, fade_in_duration = opts.fade_in_duration or 0, fade_away_duration = opts.fade_away_duration or ( (opts.duration or 600) - (opts.fade_in_duration or 0) ), spread_duration = opts.spread_duration or 600, start_scale = opts.start_scale or 0.20, end_scale = opts.end_scale or 1.0, color = opts.color, cyclic = true, affected_by_wind = opts.affected_by_wind or true, animation = { width = 152, height = 120, line_length = 5, frame_count = 60, shift = {-0.53125, -0.4375}, priority = "high", animation_speed = 0.25, filename = "__base__/graphics/entity/smoke/smoke.png", flags = { "smoke" } } } end apply_vanilla_fluid_fuel_stats = function() local solid_fuel = item_or_bust(data.raw.item["solid-fuel"]) if not solid_fuel then return false end local solid_fuel_value = get_fuel_value(solid_fuel) apply_fluid_fuel_stat( "light-oil", energy_div( solid_fuel_value, fuelfactors["light-oil"] ) ) apply_fluid_fuel_stat( "heavy-oil", energy_div( solid_fuel_value, fuelfactors["heavy-oil"] ) ) apply_fluid_fuel_stat( "petroleum-gas", energy_div( solid_fuel_value, fuelfactors["petroleum-gas"] ) ) apply_fluid_fuel_stat( "crude-oil", energy_div( solid_fuel_value, fuelfactors["crude-oil"] ) ) end apply_fluid_fuel_stat = function(fluid,fuel_value,fuel_type) local fluid = fluid_or_bust(fluid) local fuel_value = string_or_bust(fuel_value) if not fluid or not fuel_value then return nil end local fuel_type = string_or_bust(fuel_type) fluid.fuel_category = fuel_type fluid.fuel_value = fuel_value return fluid end apply_sulfur_fuel_stats = function(sulfur) if type(sulfur) ~= "table" or sulfur.type ~= "item" then log("invalid sulfur item received") return nil end local sulfur_fueltype = "sulfur" sulfur.fuel_category = sulfur_fueltype sulfur.fuel_emissions_multiplier = 12 sulfur.fuel_value = fuelvalues.sulfur sulfur.fuel_acceleration_multiplier = 0.4 sulfur.fuel_glow_color = {r = 1, g = 0.2, b = 1} add_fueltype_to_basic_burners(sulfur_fueltype) return sulfur end add_fueltype_to_basic_burners = function(fueltype) if not data.raw["fuel-category"][fueltype] then data:extend({ {type="fuel-category",name=fueltype} }) end add_fuel_type(data.raw.boiler.boiler,fueltype) for _,ent in pairs(data.raw.car) do if table_incl("chemical",get_fuel_types(ent)) then add_fuel_type(ent,fueltype) end end add_fuel_type(data.raw.furnace["stone-furnace"],fueltype) add_fuel_type(data.raw.furnace["steel-furnace"],fueltype) for _,ent in pairs(data.raw.reactor) do if table_incl("chemical",get_fuel_types(ent)) then add_fuel_type(ent,fueltype) end end for _,ent in pairs(data.raw.lamp) do if table_incl("chemical",get_fuel_types(ent)) then add_fuel_type(ent,fueltype) end end end add_fuel_type = function(entity,fuel_type) entity = table_or_bust(entity) fuel_type = string_or_bust(fuel_type) if not entity or not fuel_type then return false end local energy_source = get_energy_source(entity) if not energy_source then return false end if is_burner(energy_source) then if not energy_source.fuel_categories then if energy_source.fuel_category then energy_source .fuel_categories = {energy_source.fuel_category} else energy_source.fuel_categories = {"chemical"} end energy_source.fuel_category = nil end add_string_to_list( energy_source.fuel_categories, fuel_type ) end end get_fuel_value = function(prototype) local prototype = table_or_bust(prototype) local fuel_value = "0kW" if not prototype then return fuel_value end if prototype.type == "fluid" or prototype.type == "item" then fuel_value = prototype.fuel_value end if type(fuel_value) ~= "string" then return "0kW" end return fuel_value end get_fuel_types = function(entity) entity = table_or_bust(entity) if not entity then return {} end local energy_source = get_energy_source(entity) if not energy_source then return {} end if is_burner(energy_source) then if energy_source.fuel_categories then return energy_source.fuel_categories end if energy_source.fuel_category then return {energy_source.fuel_category} end return ({"chemical"}) end return {} end uses_fuel_type = function(name,entity) local name = string_name_or_bust(name) local entity = table_or_bust(entity) if entity and name and table_incl(name,get_fuel_types(entity)) then return true end return false end is_burner = function(energy_source) energy_source = table_or_bust(energy_source) if not energy_source then return false end if energy_source.type == "burner" or not energy_source.type then return true end return false end get_energy_source = function(entity) entity = entity_or_bust(entity) if not entity then return nil end if entity.burner then return entity.burner else return entity.energy_source end end number_or_bust = function(number) if type(number) ~= "number" then return nil end return number end number_or_one = function(number) if type(number) ~= "number" then number = 1 end return number end number_or_zero = function(number) if type(number) ~= "number" then number = 0 end return number end table_or_bust = function(prototype) if type(prototype) == "table" then return prototype end return nil end string_or_bust = function(str) if type(str) == "string" then return str end return nil end fluid_or_bust = function(prototype) if type(prototype) == "table" and prototype.type == "fluid" then return prototype end if type(prototype) == "string" then return fluid_or_bust(data.raw.fluid[prototype]) end return nil end item_or_bust = function(prototype) if type(prototype) == "table" then for _,type in pairs(adamo.item.types) do if prototype.type == type then return prototype end end end if type(prototype) == "string" then local item = adamo.get.from_types(adamo.item.types,prototype) if item then return item end end return nil end all_prototypes_with_value = function(type_tbl,key,val) local type_tbl = prototype_table_or_bust(type_tbl) if not type_tbl then return {} end local prototypes = {} for _,prototype in pairs(type_tbl) do if prototype and type(prototype) == "table" and prototype[key] == val then table.insert(prototypes,prototype) end end return prototypes end prototype_table_or_bust = function(prototype_tbl) if type(prototype_tbl) == "string" then return data.raw[prototype_tbl] else return table_or_bust(prototype_tbl) end end io_prototype_or_bust = function(prototype,count,prob,catalyst_amount) if type(prototype) == "table" then if type(prototype.name) == "string" and type(prototype.type) == "string" and type(prototype.amount) == "number" then return prototype end end local new_prototype = construct_io_prototype( prototype,count,prob,catalyst_amount ) if type(new_prototype.name) == "string" and type(new_prototype.type) == "string" and type(new_prototype.amount) == "number" then prototype = new_prototype return prototype end return nil end construct_io_prototype = function( prototype,count,prob,catalyst_amount ) local new_prototype = {} if type(prob) == "number" then if prob < 0 or prob > 1 then prob = nil end end if type(count) == "number" then if count < 0 then count = nil elseif count < 1 then prob = (prob or 1)*count count = 1 end end if type(count) ~= "number" then count = nil end if type(prob) ~= "number" then prob = nil end if type(prototype) == "table" then new_prototype.name = prototype.name or prototype[1] new_prototype.type = prototype.type or "item" new_prototype.amount = count or prototype.amount or prototype[2] or 1 new_prototype.catalyst_amount = catalyst_amount or prototype.catalyst_amount new_prototype.probability = prob or prototype.probability end if type(prototype) == "string" then if data.raw.fluid[prototype] then new_prototype.name = prototype new_prototype.type = "fluid" new_prototype.amount = count or 1 if data.raw.item[prototype] then new_prototype.type = "item" new_prototype.catalyst_amount = catalyst_amount or prototype.catalyst_amount new_prototype.probability = prob end else new_prototype.name = prototype new_prototype.type = "item" new_prototype.amount = count or 1 new_prototype.catalyst_amount = catalyst_amount or prototype.catalyst_amount new_prototype.probability = prob end end if new_prototype.type == "item" then if new_prototype.amount < 0 then elseif new_prototype.amount < 1 then new_prototype.probability = (new_prototype.probability or 1) *new_prototype.amount new_prototype.amount = 1 end elseif new_prototype.type == "fluid" and prob then new_prototype.amount = new_prototype.amount*prob end adamo.debug( "Constructed IO Prototype: " ..new_prototype.name..": "..new_prototype.amount ) return new_prototype end tech_or_bust = function(prototype) if type(prototype) == "table" and prototype.type == "technology" then return prototype end if type(prototype) == "string" then return data.raw.technology[prototype] end return nil end recipe_or_bust = function(prototype) if type(prototype) == "table" and prototype.type == "recipe" then return prototype end if type(prototype) == "string" then return data.raw.recipe[prototype] end return nil end io_handler_or_bust = function(io_handler) local recipe = recipe_or_bust(io_handler) if recipe then return recipe end if type(io_handler) == "table" then if type(io_handler.result) == "string" or type(io_handler.results) == "table" or type(io_handler.ingredients) == "table" then return io_handler end end return nil end string_name_or_bust = function(prototype_name) if type(prototype_name) == "string" then return prototype_name end if type(prototype_name) == "table" then return prototype_name.name end return nil end add_strings_to_table = function(str_list,new_strings) return add_strings_to_list(str_list,new_strings) end add_strings_to_list = function(str_list,new_strings) if type(new_strings) ~= "table" then if type(str_list) == "table" then return str_list else return {} end end for _,str in pairs(new_strings) do add_string_to_list(str_list,str) end if type(str_list) == "table" then return str_list else return {} end end add_string_to_table = function(str_list,new_str) return add_string_to_list(str_list,new_str) end add_string_to_list = function(str_list,new_str) if type(new_str) == "table" then add_strings_to_list( str_list, new_str ) end if type(str_list) ~= "table" then if type(new_str) == "string" then if type(str_list) == "string" then str_list = { str_list, new_str } end if str_list == nil then str_list = {new_str} end return str_list else return str_list end end for _,str in pairs(str_list) do if str == new_str then return str_list end end if type(new_str) == "string" then table.insert(str_list,new_str) end return str_list end table_incl = function(val,comp) comp = table_or_bust(comp) if not val or not comp then return false end for _,val_comp in pairs(comp) do if val_comp == val then return true end end return false end remove_val_from_table = function(tbl,val) local tbl = table_or_bust(tbl) local removed = false for _,match in pairs(tbl or {}) do if table_match(match,val) then tbl[_] = nil removed = true end end return removed end table_match = function(left_table,right_table) if left_table == right_table then return true end if type(left_table) == "table" and type(right_table) == "table" then if #(left_table) ~= #(right_table) then return false end for _,val in pairs(left_table) do if not right_table[_] then return false end if not table_match(right_table[_],val) then return false end end return true end return false end smoosh_tables = function(left_table,right_table) local left_table = table_or_bust(left_table) local right_table = table_or_bust(right_table) local new_table = {} for _,entry in pairs(left_table or {}) do table.insert(new_table,entry) end for _,entry in pairs(right_table or {}) do table.insert(new_table,entry) end return new_table end get_player_index = function(player) local player = get_player(player) if player then return player.index end return nil end get_player = function(player_index) if type(player_index) == "table" then if player_index.index then return get_player(player_index.index) end end if game and game.players and type(game.players) == "table" and game.players[player_index] and type(game.players[player_index]) == "table" and game.players[player_index]['valid'] then return game.players[player_index] end return nil end get_player_armor_inventory = function(player) local player = get_player(player) if player then return player.get_inventory(defines.inventory.character_armor) end end dict_to_str = function(dict) local str = "{" if table_or_bust(dict) then str = str.."\n" for key,val in pairs(dict) do str = str.."\t"..tostring(key)..": "..tostring(val).."\n" end end str = str.."}\n" return str end adamo.color = { clear = {r=0,g=0,b=0,a=0}, black = {}, white = {r=1,g=1,b=1}, red = {r=1,g=0,b=0}, green = {r=0,g=1,b=0}, blue = {r=0,g=0,b=1}, yellow = {r=1,g=1,b=0}, magenta = {r=1,b=0,g=1}, cyan = {r=0,g=1,b=1}, softpink = {r=255,g=150,b=150}, softgreen = {r=126,g=255,b=126}, darkgrey = {r=40,g=40,b=40}, lowgrey = {r=80,g=80,b=80}, midgrey = {r=127,g=127,b=127}, highgrey = {r=204,g=204,b=204}, darkbrown = {r=48,g=26,b=2}, lowbrown = {r=84,g=50,b=13}, midbrown = {r=145,g=95,b=41}, highbrown = {r=222,g=184,b=135}, hf_base = {r=46,g=51,b=5}, hf_flow = {r=0.7,g=1,b=0.1}, sodiumlamp = {r=255,g=223,b=0}, syngasred = {r=255,g=120, b=110}, heating_element_core = {r=255,g=50,b=0}, heating_element_glow = {r=255,g=40,b=20}, gas_fire_glow = {r=1,g=0.5,b=0.5}, chemical_fire_glow = {r=255,g=63,b=0}, is_clear = function(color) if not color then return true end local color = table_or_bust(color) if color then --color = util.table.deepcopy(color) color = { r = color.r or color[1] or 0, g = color.g or color[2] or 0, b = color.b or color[3] or 0, a = color.a or color[4] or 1 } end if color.r == 0 and color.g == 0 and color.b == 0 then return true end --if adamo.obj.match(color,adamo.color.clear) then -- return true --end return false end } adamo.item = { types = { "ammo", "armor", "blueprint", "blueprint-book", "capsule", "copy-paste-tool", "deconstruction-item", "gun", "item", "item-with-entity-data", "item-with-inventory", "item-with-label", "item-with-tags", "mining-tool", "module", "rail-planner", "repair-tool", "selection-tool", "tool", "upgrade-item", }, color = { ['chemical-science-pack'] = { base_color = {r=90,g=200,b=220}, flow_color = adamo.color.cyan }, ['coal'] = { base_color = adamo.color.black, flow_color = adamo.color.darkgrey }, ['adamo-chemical-fluorite'] = { base_color = adamo.color.midgrey, flow_color = adamo.color.hf_flow }, ['adamo-chemical-gypsum'] = { base_color = adamo.color.highgrey, flow_color = adamo.color.darkgrey }, ['adamo-chemical-calcite'] = { base_color = adamo.color.highgrey, flow_color = adamo.color.midgrey }, by_item = function(item) local item = item_or_bust(item) if item then return adamo.shape.item_color(adamo.item.color[item.name]) end return adamo.shape.item_color() end }, manifest = { pu238 = { "adamo-nuclear-Pu238-oxide", "plutonium-238", "pu-238" }, HF = { "adamo-chemical-hydrofluoric-acid" } }, -- Returns first item found. find = function(name) if string_or_bust(name) and adamo.item.manifest[name] then for _,item_name in pairs(adamo.item.manifest[name]) do local item = item_or_bust(item_name) if item then return item end local fluid = fluid_or_bust(item_name) if fluid then return fluid end end end return nil end } adamo.finditem = function(name) return adamo.item.find(name) end adamo.get = { from_types = function(types,name) if not table_or_bust(types) or not string_or_bust(name) then return nil end for _,category in pairs(types) do if data.raw[category] and data.raw[category][name] then return data.raw[category][name] end end return nil end, recipe_tint = function(recipe) local recipe = recipe_or_bust(recipe) local tint = recipe.crafting_machine_tint if recipe then return adamo.shape.crafting_machine_tint(tint) end return { primary = adamo.color.clear, secondary = adamo.color.clear, tertiary = adamo.color.clear, quaternary = adamo.color.clear } end } adamo.shapes = { color = {["r"] = 0,["g"] = 0,["b"] = 0,["a"] = 255} } adamo.shape = { color = function(color) local shape = util.table.deepcopy(adamo.shapes.color) local color = table_or_bust(color) if not color then return adamo.color.clear end for field,val in pairs(adamo.shapes.color) do if color[field] > 0 and color[field] < 1 then shape[field] = shape[field]*255 elseif color[field] then shape[field] = color[field] end end return shape end, item_color = function(item_color) local item_color = table_or_bust(item_color) if not item_color then return { base_color = adamo.color.clear, flow_color = adamo.color.clear } end return { base_color = item_color.base_color or adamo.color.clear, flow_color = item_color.flow_color or adamo.color.clear } end, crafting_machine_tint = function(primary,secondary,tertiary,quaternary) if not adamo.test.is_color(primary) then end local crafting_machine_tint = primary local tint = tint_or_bust(crafting_machine_tint) if tint then if type(tint.crafting_machine_tint) ~= "table" then tint.crafting_machine_tint = adamo.shape.crafting_machine_tint() end tint = tint.crafting_machine_tint else tint = table_or_bust(crafting_machine_tint) end if type(tint) ~= "table" then return { primary = adamo.color.clear, secondary = adamo.color.clear, tertiary = adamo.color.clear, quaternary = adamo.color.clear } end return { primary = adamo.shape.color(tint.primary), secondary = adamo.shape.color(tint.secondary), tertiary = adamo.shape.color(tint.tertiary), quaternary = adamo.shape.color(tint.quaternary), } end } adamo.obj = { match = function(obj1,obj2) if type(obj1) ~= type(obj2) then return false end if type(obj1) == "table" then local internals_match = true local tab1 = next(obj1) local tab2 = next(obj2) if not tab1 and not tab2 then return true end if (tab1 and not tab2) or (not tab1 and tab2) then return false end for k,v in pairs(obj1) do if obj2[k] then if not adamo.obj.match(v,obj2[k]) then internals_match = false end else return false end end for k,v in pairs(obj2) do if obj1[k] then if not adamo.obj.match(v,obj1[k]) then internals_match = false end else return false end end return internals_match else return obj1 == obj2 end end, stringify = function(obj,ml_mode,lead_str) if type(obj) == "table" then result = "{" if (ml_mode) then result = result.."\n" end for k,v in pairs(obj) do if (ml_mode) then result = result..(lead_str or "\t") end result = result ..tostring(k)..": " ..adamo.obj.stringify( v,ml_mode,(lead_str or "\t").."\t" ) .."," if (ml_mode) then result = result.."\n" end end result = result.."}" else result = tostring(obj) end return result end } colors = adamo.color adamo.recipe = { -- Ingredient/result-based colors tint = { -- adamo.recipe.tint.apply apply = function(recipe) local recipe = recipe_or_bust(recipe) if not recipe then return end local tint = recipe.crafting_machine_tint if not tint then recipe.crafting_machine_tint = {} tint = recipe.crafting_machine_tint end local old_tint = util.table.deepcopy(tint) local ingredients,results = get_io_names(recipe) adamo.debug("Applying tint to recipe "..recipe.name) local fields = {"quaternary","tertiary"} for _,name in pairs(ingredients) do adamo.debug("Checking flow_color "..name) adamo.recipe.tint.fill(tint,name,"flow_color",fields) end fields = {"primary","secondary"} for _,name in pairs(results) do adamo.debug("Checking base_color"..name) adamo.recipe.tint.fill(tint,name,"base_color",fields) end fields = {"secondary","primary"} for _,name in pairs(ingredients) do adamo.debug("Checking base_color "..name) adamo.recipe.tint.fill(tint,name,"base_color",fields) end fields = {"quaternary","tertiary","primary","secondary"} for _,name in pairs(results) do adamo.debug("Checking flow_color"..name) adamo.recipe.tint.fill(tint,name,"flow_color",fields) end for _,name in pairs(ingredients) do adamo.debug("Checking base_color "..name) adamo.recipe.tint.fill(tint,name,"base_color",fields) end fields = {"secondary","primary"} for _,name in pairs(results) do adamo.debug("Checking flow_color"..name) adamo.recipe.tint.fill(tint,name,"flow_color",fields) end for _,name in pairs(ingredients) do adamo.debug("Checking flow_color "..name) adamo.recipe.tint.fill(tint,name,"flow_color",fields) end if not adamo.obj.match(old_tint,tint) then adamo.debug("Tint changed on recipe "..recipe.name..".") adamo.debug("Old tint: "..adamo.obj.stringify(old_tint)) --adamo.debug("New tint: "..adamo.obj.stringify(tint)) --recipe.crafting_machine_tint = tint adamo.debug( "Recipe tint: " ..adamo.obj.stringify(recipe.crafting_machine_tint) ) end end, fill = function(tint,name,color_type,fields) local fluid = fluid_or_bust(name) local item = item_or_bust(name) if not table_or_bust(tint) or not string_or_bust(color_type) or not table_or_bust(fields) then return end if fluid and fluid[color_type] then for _,field in pairs(fields) do if adamo.color.is_clear(tint[field]) then adamo.debug( "Applying {" ..tostring(fluid[color_type].r) ..", " ..tostring(fluid[color_type].b) ..", " ..tostring(fluid[color_type].g) ..", " ..tostring(fluid[color_type].a) .. "} to " .. tostring(field) ) tint[field] = util.table.deepcopy(fluid[color_type]) break end end elseif item then local item_colors = adamo.item.color.by_item(item) for _,field in pairs(fields) do if adamo.color.is_clear(tint[field]) then adamo.debug( "Applying {" ..tostring(item_colors[color_type].r) ..", " ..tostring(item_colors[color_type].b) ..", " ..tostring(item_colors[color_type].g) ..", " ..tostring(item_colors[color_type].a) .. "} to " .. tostring(field) ) tint[field] = util.table.deepcopy(item_colors[color_type]) break end end end end } } adamo.test = { unit ={ recipe = { tint = { apply = function() local tint -- = adamo.example.recipe.tint end } } } } adamo.example = { recipe = { -- ["crafting_machine_tint"] = { -- ["primary"] = adamo.shapes.color, -- ["secondary"] =, -- ["tertiary"] =, -- ["quaternary"] = -- -- }, -- ["tinted"] = { -- name = "test-recipe", -- type = "recipe", -- flow_color = -- } } }