From 1690c366568ed721a9970a679917d96d669a41e0 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Sat, 25 May 2024 20:04:31 -0500 Subject: [PATCH 01/13] Remove stdlib --- cached-configs/run.lua | 6 +- data-final-fixes.lua | 19 +- data-updates.lua | 9 +- info.json | 1 - lib.lua | 3 + .../StronglyConnectedComponents.lua | 2 +- luagraphs/data/graph.lua | 6 +- luagraphs/flow/FordFulkerson.lua | 2 +- luagraphs/mst/EagerPrimMST.lua | 4 +- luagraphs/mst/KruskalMST.lua | 6 +- luagraphs/mst/PrimMST.lua | 4 +- luagraphs/search/BreadthFirstSearch.lua | 4 +- luagraphs/search/DepthFirstSearch.lua | 2 +- luagraphs/shortest_paths/BellmanFord.lua | 2 +- luagraphs/shortest_paths/Dijkstra.lua | 4 +- .../shortest_paths/TopoSortShortestPath.lua | 4 +- luagraphs/sort/TopologicalSort.lua | 2 +- prototypes/functions/auto_tech.lua | 143 +++--- prototypes/functions/compatibility.lua | 51 +-- prototypes/functions/data_parser.lua | 409 +++++++++--------- prototypes/functions/fuzzy_graph.lua | 36 +- prototypes/functions/fz_topo_sort.lua | 5 +- prototypes/functions/galdoc.lua | 18 +- prototypes/functions/search/fz_lazy_bfs.lua | 3 +- prototypes/functions/transitive_reduction.lua | 2 - prototypes/functions/utils.lua | 103 ----- prototypes/yafc.lua | 20 +- 27 files changed, 372 insertions(+), 498 deletions(-) create mode 100644 lib.lua delete mode 100644 prototypes/functions/utils.lua diff --git a/cached-configs/run.lua b/cached-configs/run.lua index e3791ed..0ea6d6f 100644 --- a/cached-configs/run.lua +++ b/cached-configs/run.lua @@ -1,5 +1,3 @@ -local Table = require('__stdlib__/stdlib/utils/table') - local function merge(table, value) for k, v in pairs(value) do if type(v) == 'table' and k ~= 'prerequisites' and k ~= 'ingredients' then @@ -68,8 +66,8 @@ for _, cache_file_info in pairs(pypp_registered_cache_files) do union_of_all_subsets[mod] = 1 end end -union_of_all_subsets = Table.keys(union_of_all_subsets) -local recognized_enabled_mods = Table.filter(union_of_all_subsets, function(potential_mod) return mods[potential_mod] end) +union_of_all_subsets = table.keys(union_of_all_subsets) +local recognized_enabled_mods = table.filter(union_of_all_subsets, function(potential_mod) return mods[potential_mod] end) table.sort(recognized_enabled_mods) if #recognized_enabled_mods == 0 then diff --git a/data-final-fixes.lua b/data-final-fixes.lua index 4b627f5..e57e2c3 100644 --- a/data-final-fixes.lua +++ b/data-final-fixes.lua @@ -1,9 +1,5 @@ local dev_mode = settings.startup['pypp-dev-mode'].value local create_cache_mode = settings.startup['pypp-create-cache'].value - -require('__stdlib__/stdlib/data/data').Util.create_data_globals() - -local table = require('__stdlib__/stdlib/utils/table') local config = require 'prototypes.config' for _, module in pairs(data.raw.module) do @@ -16,7 +12,7 @@ for _, module in pairs(data.raw.module) do end if not table.is_empty(remove_recipe) then - local limit = table.array_to_dictionary(module.limitation, true) + local limit = table.invert(module.limitation) for r, _ in pairs(remove_recipe) do limit[r] = nil @@ -34,7 +30,7 @@ for _, module in pairs(data.raw.module) do end if not table.is_empty(remove_recipe) then - local limit = table.array_to_dictionary(module.limitation_blacklist, true) + local limit = table.invert(module.limitation_blacklist) for r, _ in pairs(remove_recipe) do limit[r] = nil @@ -217,11 +213,10 @@ if mods['PyBlock'] then end if mods.pycoalprocessing and not mods['extended-descriptions'] then - local FUN = require('__pycoalprocessing__/prototypes/functions/functions') - for _, recipe in pairs(data.raw.module['productivity-module'].limitation or {}) do + for _, recipe in pairs(data.raw.module['productivity-module'].limitation or {}) do recipe = data.raw.recipe[recipe] if recipe then - FUN.add_to_description('recipe', recipe, {'recipe-description.affected-by-productivity'}) + py.add_to_description('recipe', recipe, {'recipe-description.affected-by-productivity'}) end end end @@ -261,7 +256,7 @@ if dev_mode then end log('AUTOTECH START') - local at = require('prototypes.functions.auto_tech').create() + local at = require 'prototypes.functions.auto_tech'.create() at:run() if create_cache_mode then at:create_cachefile_code() @@ -274,7 +269,7 @@ end ---------------------------------------------------- -- THIRD PARTY COMPATIBILITY ---------------------------------------------------- -require('prototypes/functions/compatibility') +require 'prototypes/functions/compatibility' ---------------------------------------------------- -- TECHNOLOGY CHANGES @@ -342,7 +337,7 @@ end -- YAFC if type(data.data_crawler) == 'string' and string.sub(data.data_crawler, 1, 5) == 'yafc ' then - require('prototypes/yafc') + require 'prototypes/yafc' end -- force mining drill speed to not increase with speed modules diff --git a/data-updates.lua b/data-updates.lua index 7167ae1..7f5717e 100644 --- a/data-updates.lua +++ b/data-updates.lua @@ -1,17 +1,14 @@ -local py_utils = require('prototypes.functions.utils') -require('__stdlib__/stdlib/data/data').Util.create_data_globals() - local function set_underground_recipe(underground, belt, prev_underground, prev_belt) local dist = data.raw['underground-belt'][underground].max_distance + 1 local prev_dist = 0 if prev_underground then prev_dist = data.raw['underground-belt'][prev_underground].max_distance + 1 - local recipe_data = data.raw.recipe[belt].normal or data.raw.recipe[belt] - local belt_count = py_utils.standardize_products(recipe_data.results, nil, recipe_data.result, recipe_data.result_count)[1].amount + local recipe = data.raw.recipe[belt]:standardize() + local belt_count = recipe[1].amount local fluid = false - for _, ing in pairs(py_utils.standardize_products(recipe_data.ingredients)) do + for _, ing in pairs(recipe.ingredients) do if ing.name ~= prev_belt then RECIPE(underground):remove_ingredient(ing.name) :add_ingredient{ type = ing.type, name = ing.name, amount = ing.amount * prev_dist / belt_count} diff --git a/info.json b/info.json index 5b89058..5bf9617 100644 --- a/info.json +++ b/info.json @@ -9,7 +9,6 @@ "description": "Post-processing steps for Pyanodons modpack. Overhauls the technology tree to make sure prerequisites are set based on unlocked recipes.", "dependencies": [ "base >= 1.1.0", - "stdlib >= 1.4.0", "? pycoalprocessing", "? pyindustry", "? pyfusionenergy", diff --git a/lib.lua b/lib.lua new file mode 100644 index 0000000..9e008a1 --- /dev/null +++ b/lib.lua @@ -0,0 +1,3 @@ +if py then return py end +require 'lib.lib' +return py \ No newline at end of file diff --git a/luagraphs/connectivity/StronglyConnectedComponents.lua b/luagraphs/connectivity/StronglyConnectedComponents.lua index 1256f04..41aa2b4 100644 --- a/luagraphs/connectivity/StronglyConnectedComponents.lua +++ b/luagraphs/connectivity/StronglyConnectedComponents.lua @@ -34,7 +34,7 @@ function StronglyConnectedComponents:run(G) end local g_prime = G:reverse() - local topo_sort = require('luagraphs.sort.TopologicalSort').create() + local topo_sort = require 'luagraphs.sort.TopologicalSort'.create() topo_sort:run(g_prime) local order = topo_sort:path() diff --git a/luagraphs/data/graph.lua b/luagraphs/data/graph.lua index 5a64ca8..10dac34 100644 --- a/luagraphs/data/graph.lua +++ b/luagraphs/data/graph.lua @@ -1,12 +1,8 @@ --- -- Created by IntelliJ IDEA. -- User: chen0 -- Date: 26/6/2017 -- Time: 12:48 AM -- To change this template use File | Settings | File Templates. --- - -local table = require "__stdlib__.stdlib.utils.table" local graph = {} graph.__index = graph @@ -106,7 +102,7 @@ function graph.createFromVertexList(vertices, directed) local g = graph.create(0, directed) setmetatable(g, graph) - g.vertexList = table.deep_copy(vertices) + g.vertexList = table.deepcopy(vertices) g.adjList = {} for v, _ in pairs(g.vertexList) do diff --git a/luagraphs/flow/FordFulkerson.lua b/luagraphs/flow/FordFulkerson.lua index 4c15552..9ec0afe 100644 --- a/luagraphs/flow/FordFulkerson.lua +++ b/luagraphs/flow/FordFulkerson.lua @@ -93,7 +93,7 @@ end function FordFulkerson:minCuts() - local result = require('luagraphs.data.list').create() + local result = require 'luagraphs.data.list'.create() for _, v in pairs(self.network.vertexList:enumerate()) do for _, e in pairs(self.network:adj(v):enumerate()) do diff --git a/luagraphs/mst/EagerPrimMST.lua b/luagraphs/mst/EagerPrimMST.lua index 214dc6e..2af3d23 100644 --- a/luagraphs/mst/EagerPrimMST.lua +++ b/luagraphs/mst/EagerPrimMST.lua @@ -6,8 +6,8 @@ -- To change this template use File | Settings | File Templates. -- -local list = require('luagraphs.data.list') -local intexedMinPQ = require('luagraphs.data.IndexedMinPQ') +local list = require 'luagraphs.data.list' +local intexedMinPQ = require 'luagraphs.data.IndexedMinPQ' local EagerPrimMST = {} EagerPrimMST.__index = EagerPrimMST diff --git a/luagraphs/mst/KruskalMST.lua b/luagraphs/mst/KruskalMST.lua index b6b26e8..a29c742 100644 --- a/luagraphs/mst/KruskalMST.lua +++ b/luagraphs/mst/KruskalMST.lua @@ -6,9 +6,9 @@ -- To change this template use File | Settings | File Templates. -- -local list = require('luagraphs.data.list') -local minPQ = require('luagraphs.data.MinPQ') -local unionFind = require('luagraphs.data.UnionFind') +local list = require 'luagraphs.data.list' +local minPQ = require 'luagraphs.data.MinPQ' +local unionFind = require 'luagraphs.data.UnionFind' local KruskalMST = {} KruskalMST.__index = KruskalMST diff --git a/luagraphs/mst/PrimMST.lua b/luagraphs/mst/PrimMST.lua index e4eb128..cda0180 100644 --- a/luagraphs/mst/PrimMST.lua +++ b/luagraphs/mst/PrimMST.lua @@ -6,8 +6,8 @@ -- To change this template use File | Settings | File Templates. -- -local list = require('luagraphs.data.list') -local minPQ = require('luagraphs.data.MinPQ') +local list = require 'luagraphs.data.list' +local minPQ = require 'luagraphs.data.MinPQ' local PrimMST = {} PrimMST.__index = PrimMST diff --git a/luagraphs/search/BreadthFirstSearch.lua b/luagraphs/search/BreadthFirstSearch.lua index f774eea..e663249 100644 --- a/luagraphs/search/BreadthFirstSearch.lua +++ b/luagraphs/search/BreadthFirstSearch.lua @@ -6,8 +6,8 @@ -- To change this template use File | Settings | File Templates. -- -local queue = require('luagraphs.data.queue') -local stack = require('luagraphs.data.stack') +local queue = require 'luagraphs.data.queue' +local stack = require 'luagraphs.data.stack' local BreadthFirstSearch = {} BreadthFirstSearch.__index = BreadthFirstSearch diff --git a/luagraphs/search/DepthFirstSearch.lua b/luagraphs/search/DepthFirstSearch.lua index bb1d138..090b4d4 100644 --- a/luagraphs/search/DepthFirstSearch.lua +++ b/luagraphs/search/DepthFirstSearch.lua @@ -6,7 +6,7 @@ -- To change this template use File | Settings | File Templates. -- -local stack = require('luagraphs.data.stack') +local stack = require 'luagraphs.data.stack' local DepthFirstSearch = {} DepthFirstSearch.__index = DepthFirstSearch diff --git a/luagraphs/shortest_paths/BellmanFord.lua b/luagraphs/shortest_paths/BellmanFord.lua index e3b7b33..b2caea8 100644 --- a/luagraphs/shortest_paths/BellmanFord.lua +++ b/luagraphs/shortest_paths/BellmanFord.lua @@ -6,7 +6,7 @@ -- To change this template use File | Settings | File Templates. -- -local stack = require('luagraphs.data.stack').create() +local stack = require 'luagraphs.data.stack'.create() local BellmanFord = {} diff --git a/luagraphs/shortest_paths/Dijkstra.lua b/luagraphs/shortest_paths/Dijkstra.lua index aecad56..7435ce7 100644 --- a/luagraphs/shortest_paths/Dijkstra.lua +++ b/luagraphs/shortest_paths/Dijkstra.lua @@ -34,7 +34,7 @@ function Dijkstra:run(G, s) self.cost[v] = Dijkstra.MAX_VALUE end - local pq = require('luagraphs.data.IndexedMinPQ').create() + local pq = require 'luagraphs.data.IndexedMinPQ'.create() self.cost[s] = 0 pq:add(s, self.cost[s]) @@ -80,7 +80,7 @@ function Dijkstra:getPathLength(v) end function Dijkstra:getPathTo(v) - local stack = require('luagraphs.data.stack').create() + local stack = require 'luagraphs.data.stack'.create() local x = v while x ~= self.source do local e = self.edgeTo[x] diff --git a/luagraphs/shortest_paths/TopoSortShortestPath.lua b/luagraphs/shortest_paths/TopoSortShortestPath.lua index c44dfe7..a6fc32e 100644 --- a/luagraphs/shortest_paths/TopoSortShortestPath.lua +++ b/luagraphs/shortest_paths/TopoSortShortestPath.lua @@ -33,7 +33,7 @@ function TopoSortShortestPath:run(G, s) self.cost[s] = 0 - local topoSort = require('luagraphs.sort.TopologicalSort').create() + local topoSort = require 'luagraphs.sort.TopologicalSort'.create() topoSort:run(G) local order = topoSort:path() @@ -67,7 +67,7 @@ function TopoSortShortestPath:getPathLength(v) end function TopoSortShortestPath:getPathTo(v) - local stack = require('luagraphs.data.stack').create() + local stack = require 'luagraphs.data.stack'.create() local x = v while x ~= self.source do local e = self.edgeTo[x] diff --git a/luagraphs/sort/TopologicalSort.lua b/luagraphs/sort/TopologicalSort.lua index 095db6c..f47eea3 100644 --- a/luagraphs/sort/TopologicalSort.lua +++ b/luagraphs/sort/TopologicalSort.lua @@ -13,7 +13,7 @@ function TopologicalSort.create() local s = {} setmetatable(s, TopologicalSort) - s.reversedPostOrder = require('luagraphs.data.stack').create() + s.reversedPostOrder = require 'luagraphs.data.stack'.create() s.marked = {} return s diff --git a/prototypes/functions/auto_tech.lua b/prototypes/functions/auto_tech.lua index c20e3cf..aeef8e5 100644 --- a/prototypes/functions/auto_tech.lua +++ b/prototypes/functions/auto_tech.lua @@ -1,13 +1,11 @@ -local table = require "__stdlib__.stdlib.utils.table" -local queue = require "__stdlib__.stdlib.misc.queue" -local config = require "prototypes.config" -local data_parser = require("prototypes.functions.data_parser") -local fz_graph = require("prototypes.functions.fuzzy_graph") -local py_utils = require("prototypes.functions.utils") -local fz_lazy_bfs = require("prototypes.functions.search.fz_lazy_bfs") -local fz_topo = require "prototypes.functions.fz_topo_sort" -local trans_reduct = require "prototypes.functions.transitive_reduction" -local BreadthFirstSearch = require("luagraphs.search.BreadthFirstSearch") +local queue = require 'luagraphs.queue.queue' +local config = require 'prototypes.config' +local data_parser = require 'prototypes.functions.data_parser' +local fz_graph = require 'prototypes.functions.fuzzy_graph' +local fz_lazy_bfs = require 'prototypes.functions.search.fz_lazy_bfs' +local fz_topo = require 'prototypes.functions.fz_topo_sort' +local trans_reduct = require 'prototypes.functions.transitive_reduction' +local BreadthFirstSearch = require 'luagraphs.search.BreadthFirstSearch' local auto_tech = {} auto_tech.__index = auto_tech @@ -52,7 +50,7 @@ function auto_tech:create_cachefile_code() error('\n\n\n\n----------------------------------------------\nSuccess! pypostprocessing config file was created @ factorio-current.log\n'..get_modlist_string()..'\n----------------------------------------------\n\n\n\n', 10) end -local LABEL_UNLOCK_RECIPE = "__unlock_recipe__" +local LABEL_UNLOCK_RECIPE = '__unlock_recipe__' local function deadend_node(n, _, g) return not g:has_links_to(n) or not g:has_links_from(n) or false end local function ifd_deadend_node(n, _, g) return n.ignore_for_dependencies and deadend_node(n, _, g) end @@ -63,7 +61,7 @@ function auto_tech.create() setmetatable(a, auto_tech) a.spf_cache = {} - a.verbose_logging = settings.startup["pypp-verbose-logging"].value + a.verbose_logging = settings.startup['pypp-verbose-logging'].value return a end @@ -100,10 +98,10 @@ function auto_tech:run() end end - log("Mandatory recipe count: " .. recipe_count) - log("Optional recipe count: " .. opt_recipe_count) + log('Mandatory recipe count: ' .. recipe_count) + log('Optional recipe count: ' .. opt_recipe_count) - log("PROCESS TECHS START") + log('PROCESS TECHS START') for _, node in pairs(fg.nodes) do if node.type == fz_graph.NT_TECH_HEAD then @@ -111,11 +109,11 @@ function auto_tech:run() end end - log("PROCESS TECHS END") + log('PROCESS TECHS END') - -- log(serpent.block(fg.graph.revList[fg:get_node("fracking / frack-natural-gas", fz_graph.NT_RECIPE).key])) + -- log(serpent.block(fg.graph.revList[fg:get_node('fracking / frack-natural-gas', fz_graph.NT_RECIPE).key])) - -- self:process_tech(fg:get_node("tholin-mk01", fz_graph.NT_TECH_HEAD), fg) + -- self:process_tech(fg:get_node('tholin-mk01', fz_graph.NT_TECH_HEAD), fg) fg:recursive_remove(ifd_deadend_node, false) local spg = self:extract_science_pack_graph(fg, parser) @@ -127,8 +125,8 @@ function auto_tech:run() error_found, ts = self:topo_sort_with_sp(fg, spg, parser.science_packs) - if error_found then - local msg = "\n\nERROR: Dependency loop detected in step 1\n" + if error_message then + local msg = '\n\nERROR: Dependency loop detected\n' .. error_message error(msg) end @@ -136,8 +134,8 @@ function auto_tech:run() error_found, ts = self:topo_sort_with_sp(fg2, spg, parser.science_packs) - if error_found then - local msg = "\n\nERROR: Dependency loop detected in step 2\n" + if error_message then + local msg = '\n\nERROR: Dependency loop detected\n' .. error_message error(msg) end @@ -145,8 +143,8 @@ function auto_tech:run() local tech_ts = fz_topo.create(tg) error_found = tech_ts:run(false, false) - if error_found then - local msg = "\n\nERROR: Dependency loop detected in step 3\n" + if error_message then + local msg = '\n\nERROR: Dependency loop detected\n' .. error_message error(msg) end @@ -156,12 +154,12 @@ function auto_tech:run() -- Set science pack order for _, node in pairs(spg.nodes) do - science_pack_order(node.name, string.format("%03d-%06d", sp_ts.level[node.key], ts.level[node.key])) + science_pack_order(node.name, string.format('%03d-%06d', sp_ts.level[node.key] or 0, ts.level[node.key])) local sp = data.raw.tool[node.name] - sp.subgroup = "science-pack" - sp.order = string.format("%03d-%06d", sp_ts.level[node.key], ts.level[node.key]) - sp_level[node.name] = sp_ts.level[node.key] + sp.subgroup = 'science-pack' + sp.order = string.format('%03d-%06d', sp_ts.level[node.key] or 0, ts.level[node.key]) + sp_level[node.name] = sp_ts.level[node.key] or 0 if sp_level[sp.name] > max_level then max_level = sp_level[sp.name] @@ -187,6 +185,8 @@ function auto_tech:run() local tech = data.raw.technology[node.name] if tech then + tech:standardize() + node.mandatory = (node.name == config.WIN_GAME_TECH or tech_bfs:has_path_to(node)) local pre = {} @@ -199,7 +199,7 @@ function auto_tech:run() local highest_sp local highest_level = 0 - for _, sp in pairs(py_utils.standardize_products(tech.unit.ingredients)) do + for _, sp in pairs(tech.unit.ingredients) do if sp_level[sp.name] > highest_level then highest_level = sp_level[sp.name] highest_sp = sp.name @@ -208,7 +208,7 @@ function auto_tech:run() tech_highest_sp[tech.name] = highest_level - for i, sp in pairs(py_utils.standardize_products(tech.unit.ingredients)) do + for i, sp in pairs(tech.unit.ingredients) do sp.amount = level_amount[highest_level - sp_level[sp.name] + 1] set_tech_property(tech, {unit = {ingredients = {[i] = sp}}}) tech.unit.ingredients[i] = sp @@ -224,16 +224,16 @@ function auto_tech:run() set_tech_property(tech, {unit = {time = config.TC_SCIENCE_PACK_TIME[highest_sp]}}) set_tech_property(tech, {prerequisites = table.keys(pre)}) - set_tech_property(tech, {order = string.format("%06d", tech_ts.level[node.key])}) + set_tech_property(tech, {order = string.format('%06d', tech_ts.level[node.key])}) tech.unit.time = config.TC_SCIENCE_PACK_TIME[highest_sp] tech.prerequisites = table.keys(pre) - tech.order = string.format("%06d", tech_ts.level[node.key]) + tech.order = string.format('%06d', tech_ts.level[node.key]) end end -- Calculate tech costs local target = config.TC_BASE_MULT * (recipe_count * config.TC_MANDATORY_RECIPE_COUNT_MULT + opt_recipe_count * config.TC_OPTIONAL_RECIPE_COUNT_MULT) - log("Target: " .. target) + log('Target: ' .. target) local win_level = tech_ts.level[tg:get_node(config.WIN_GAME_TECH, fz_graph.NT_TECH_HEAD).key] local win_sp_level = tech_highest_sp[config.WIN_GAME_TECH] @@ -265,8 +265,8 @@ function auto_tech:run() end end - log("Mandatory tech pack count: " .. sum_mand_packs) - log("Total tech pack count: " .. sum_total_packs) + log('Mandatory tech pack count: ' .. sum_mand_packs) + log('Total tech pack count: ' .. sum_total_packs) end @@ -278,7 +278,7 @@ function auto_tech:topo_sort_with_sp(fg, sp_graph, science_packs) local bfs = fz_lazy_bfs.create(sp_graph, sp, false, true) for _, e in sp_graph:iter_links_to(sp) do - -- log("Checking links from : " .. e:from() .. " To: " .. sp.name) + -- log('Checking links from : ' .. e:from() .. ' To: ' .. sp.name) local sp2 = sp_graph:get_node(e:from()) for tech, _ in pairs(science_packs[sp2.name]) do @@ -298,7 +298,7 @@ function auto_tech:topo_sort_with_sp(fg, sp_graph, science_packs) if other_parents and tech_node and not science_packs[sp.name][tech] then fg:add_link(tech_node, sp_node, tech) table.insert(sp_links, {from = tech_node, to = sp_node }) - -- log(" - Adding sp link: " .. tech_node.key .. " >> " .. sp_node.key) + -- log(' - Adding sp link: ' .. tech_node.key .. ' >> ' .. sp_node.key) end end end @@ -317,7 +317,15 @@ function auto_tech:topo_sort_with_sp(fg, sp_graph, science_packs) error_found = ts:run(false, self.verbose_logging) end - return error_found, ts + local error_message + if error_found then + error_message = '' + for key, _ in pairs(errors) do + error_message = error_message .. key .. '\n' + end + end + + return error_message, ts end @@ -368,7 +376,7 @@ function auto_tech:calculate_factor(level_sp_cost, target, win_level, win_sp_lev if t > target * (1 + config.TC_EXP_THRESHOLD) then break end end - log("MAXF: " .. max_f .. " SPF: " .. spf .. " T: " .. t) + log('MAXF: ' .. max_f .. ' SPF: ' .. spf .. ' T: ' .. t) if t < target * (1 - config.TC_EXP_THRESHOLD) then max_f = max_f * 2 @@ -393,7 +401,7 @@ function auto_tech:calculate_factor(level_sp_cost, target, win_level, win_sp_lev if t > target * (1 + config.TC_EXP_THRESHOLD) then break end end - log("F: " .. f .. " SPF: " .. spf .. " T: " .. t) + log('F: ' .. f .. ' SPF: ' .. spf .. ' T: ' .. t) if t < target * (1 - config.TC_EXP_THRESHOLD) then min_f = f @@ -454,8 +462,8 @@ function auto_tech:extract_tech_graph(fg) for k, node in pairs(tech_graph.nodes) do self:add_tech_prerequisites(fg, tech_graph, fg:get_node(node.name, fz_graph.NT_TECH_TAIL)) - if not tech_graph:has_links_to(node) and node.name ~= "__START__" then - tech_graph:add_link(tech_graph.start_node, node, "__tech_prerequisite____START__") + if not tech_graph:has_links_to(node) and node.name ~= '__START__' then + tech_graph:add_link(tech_graph.start_node, node, '__tech_prerequisite____START__') end end @@ -484,7 +492,7 @@ function auto_tech:add_tech_prerequisites(fg, tg, node) local tg_node = tg:get_node(p_node.name, fz_graph.NT_TECH_HEAD) if tg_node and p_node.type == fz_graph.NT_TECH_TAIL then - tg:add_link(tg_node, tg:get_node(node.name, fz_graph.NT_TECH_HEAD), "__tech_prerequisite__" .. p_node.name) + tg:add_link(tg_node, tg:get_node(node.name, fz_graph.NT_TECH_HEAD), '__tech_prerequisite__' .. p_node.name) else q(p_node) end @@ -499,7 +507,7 @@ function auto_tech:add_original_prerequisites(fg, tg, levels) table.sort(nodes, function (n1, n2) return levels[fg:get_node(n1.name, fz_graph.NT_TECH_TAIL).key] < levels[fg:get_node(n2.name, fz_graph.NT_TECH_TAIL).key] end) for _, node in pairs(nodes) do - -- log("Processing " .. node.key) + -- log('Processing ' .. node.key) local target_hnode = tg:get_node(node.key) local tech = data.raw.technology[node.name] local bfs = fz_lazy_bfs.create(fg, node, true) @@ -516,11 +524,11 @@ function auto_tech:add_original_prerequisites(fg, tg, levels) if not found then if not bfs:has_path_to(fg:get_node(pre, fz_graph.NT_TECH_TAIL)) then - tg:add_link(tg:get_node(pre, fz_graph.NT_TECH_TAIL), target_hnode, "__tech_prerequisite__" .. pre) - fg:add_link(fg:get_node(pre, fz_graph.NT_TECH_TAIL), target_hnode, "__tech_prerequisite__" .. pre) - -- log(" - Adding link " .. tg:get_node(pre, fz_graph.NT_TECH_TAIL).key .. " >> " .. target_hnode.key) + tg:add_link(tg:get_node(pre, fz_graph.NT_TECH_TAIL), target_hnode, '__tech_prerequisite__' .. pre) + fg:add_link(fg:get_node(pre, fz_graph.NT_TECH_TAIL), target_hnode, '__tech_prerequisite__' .. pre) + -- log(' - Adding link ' .. tg:get_node(pre, fz_graph.NT_TECH_TAIL).key .. ' >> ' .. target_hnode.key) -- else - -- log(" - Skipping link " .. tg:get_node(pre, fz_graph.NT_TECH_TAIL).key .. " >> " .. target_hnode.key) + -- log(' - Skipping link ' .. tg:get_node(pre, fz_graph.NT_TECH_TAIL).key .. ' >> ' .. target_hnode.key) end end end @@ -554,8 +562,8 @@ function auto_tech:process_tech(tech_node, fg) for _, node in pairs(recipes) do if not node.temp then for label, _ in pairs(node.labels) do - if not recipes[fz_graph.node.get_key(node.name .. "/" .. label, node.type)] and fg:has_label_to(node, label) then - local tmp_node = fg:add_node(node.name .. "/" .. label, node.type, node) + if not recipes[fz_graph.node.get_key(node.name .. '/' .. label, node.type)] and fg:has_label_to(node, label) then + local tmp_node = fg:add_node(node.name .. '/' .. label, node.type, node) tmp_node.temp = true recipes[tmp_node.key] = tmp_node table.insert(tmp_nodes, tmp_node) @@ -573,7 +581,8 @@ function auto_tech:process_tech(tech_node, fg) for _, e in pairs(fg:get_links_from(node)) do if e:to() ~= tail_node.key then local p = fg.nodes[e:to()] - py_utils.insert_double_lookup(products, p.key, e.label) + products[p.key] = products[p.key] or {} + products[p.key][e.label] = true node.products[p.key] = true end end @@ -582,7 +591,7 @@ function auto_tech:process_tech(tech_node, fg) -- local internal_nodes = self:find_internal_nodes(fg, tech_node.name, recipes) for k, _ in pairs(products) do - -- if tech_node.name == "__START__" then log(" - Processing product: " .. k) end + -- if tech_node.name == '__START__' then log(' - Processing product: ' .. k) end local bfs = BreadthFirstSearch.create() bfs:run(fg.graph, k, function (v, from) @@ -615,12 +624,12 @@ function auto_tech:process_tech(tech_node, fg) for r, _ in pairs(recipes) do if bfs:hasPathTo(r) then - -- if tech_node.name == "__START__" then log(" - Path to recipe: " .. r) end + -- if tech_node.name == '__START__' then log(' - Path to recipe: ' .. r) end local n = bfs.pathTo[r] while n and fg.nodes[n].tech_name == nil do - -- if tech_node.name == "__START__" then log(" - " .. n) end - local new_name = tech_node.name .. ":" .. fg.nodes[n].name + -- if tech_node.name == '__START__' then log(' - ' .. n) end + local new_name = tech_node.name .. ':' .. fg.nodes[n].name local new_key = fz_graph.node.get_key(new_name, fg.nodes[n].type) if not internal_nodes[new_key] then @@ -631,7 +640,7 @@ function auto_tech:process_tech(tech_node, fg) recipes[new_key] = internal_nodes[new_key] change = true end - -- if tech_node.name == "__START__" then log(" - Add internal node: " .. new_key) end + -- if tech_node.name == '__START__' then log(' - Add internal node: ' .. new_key) end end n = bfs.pathTo[n] @@ -682,15 +691,15 @@ function auto_tech:process_tech(tech_node, fg) local error_found = ts:run(false, false) for _, e in pairs(ts.removed_links) do - -- log("Remove link: " .. e:from() .. " > " .. e:to() .. " / " .. e.label) + -- log('Remove link: ' .. e:from() .. ' > ' .. e:to() .. ' / ' .. e.label) fg:remove_link(fg:get_node(e:from()), fg:get_node(e:to()), e.label) end if error_found then - -- log("Local loop: " .. tech_node.name) + -- log('Local loop: ' .. tech_node.name) -- for k, node in pairs(tgraph.nodes) do -- if not node.ignore_for_dependencies and not ts.sorted[k] then - -- log(" - " .. k) + -- log(' - ' .. k) -- end -- end else @@ -730,7 +739,7 @@ end function auto_tech:find_internal_nodes(fg, tech_name, recipes) - -- log("find_internal_nodes - START") + -- log('find_internal_nodes - START') local internal_nodes = {} local q = queue() local marked = {} @@ -773,7 +782,7 @@ function auto_tech:find_internal_nodes(fg, tech_name, recipes) end end end - -- log("find_internal_nodes - END") + -- log('find_internal_nodes - END') return internal_nodes end @@ -783,9 +792,9 @@ function auto_tech:find_dependency_loop(fg, ts) local g = fg:create_subgraph(node_list) g:recursive_remove(deadend_node, false) -- self:remove_redundant_deps(g) - -- log(serpent.block(g:get_links_to(g:get_node("silicon-mk01 / trichlorosilane", fz_graph.NT_RECIPE), "hydrogen-chloride"))) + -- log(serpent.block(g:get_links_to(g:get_node('silicon-mk01 / trichlorosilane', fz_graph.NT_RECIPE), 'hydrogen-chloride'))) -- g:recursive_remove(deadend_node, false) - -- log(serpent.block(g:get_links_to(g:get_node("silicon-mk01 / trichlorosilane", fz_graph.NT_RECIPE), "hydrogen-chloride"))) + -- log(serpent.block(g:get_links_to(g:get_node('silicon-mk01 / trichlorosilane', fz_graph.NT_RECIPE), 'hydrogen-chloride'))) local path @@ -809,7 +818,7 @@ function auto_tech:remove_redundant_deps(fg) for key, node in pairs(fg.nodes) do for _, e in fg:iter_links_from(node) do - if e.label and e.label ~= "" then + if e.label and e.label ~= '' then local to_node = fg:get_node(e:to()) local count = 0 @@ -838,7 +847,7 @@ function auto_tech:remove_redundant_deps(fg) local links_to_remove = {} for _, e in fg:iter_links_from(node) do - if e.label and e.label ~= "" then + if e.label and e.label ~= '' then local to_node = fg:get_node(e:to()) for _, e2 in fg:iter_links_to(to_node, e.label) do @@ -857,12 +866,12 @@ function auto_tech:remove_redundant_deps(fg) end for _, e in pairs(links_to_remove) do - -- log(" - Removing link: " .. e:from() .. " >> " .. e:to() .. " : " .. e.label) + -- log(' - Removing link: ' .. e:from() .. ' >> ' .. e:to() .. ' : ' .. e.label) fg:remove_link(fg:get_node(e:from()), fg:get_node(e:to()), e.label) end if clear then - -- log("- Cleared node: " .. key) + -- log('- Cleared node: ' .. key) fuzzy_nodes[key] = nil end end diff --git a/prototypes/functions/compatibility.lua b/prototypes/functions/compatibility.lua index ca15294..16376eb 100644 --- a/prototypes/functions/compatibility.lua +++ b/prototypes/functions/compatibility.lua @@ -1,19 +1,16 @@ -- Compatibility changes which need to modify data.raw should go here. -- Compatibility changes affecting auto-tech config should go in the bottom of config.lua -local py_utils = require 'prototypes.functions.utils' -local table = require '__stdlib__.stdlib.utils.table' - if mods['pyrawores'] then for _, recipe in pairs(data.raw.recipe) do - if recipe.enabled == nil or recipe.enabled == true and recipe.name ~= "coal-gas" then + if recipe.enabled == nil or recipe.enabled == true and recipe.name ~= 'coal-gas' then RECIPE(recipe):replace_ingredient('coal', 'raw-coal') end end end -if mods["galdocs-manufacturing"] then - require("prototypes/functions/galdoc") +if mods['galdocs-manufacturing'] then + require 'prototypes/functions/galdoc' end if mods['DeadlockLargerLamp'] then @@ -23,7 +20,7 @@ if mods['DeadlockLargerLamp'] then end if mods['deadlock-beltboxes-loaders'] then - for item_name, item in py_utils.iter_prototypes('item') do + for item_name, item in py.iter_prototypes('item') do local stack = data.raw.item['deadlock-stack-' .. item_name] if stack then stack.ignore_for_dependencies = true @@ -59,7 +56,7 @@ if mods['deadlock-beltboxes-loaders'] then end if mods['DeadlockCrating'] then - for item_name, item in py_utils.iter_prototypes('item') do + for item_name, item in py.iter_prototypes('item') do if data.raw.item['deadlock-crate-' .. item_name] ~= nil then data.raw.item['deadlock-crate-' .. item_name].ignore_for_dependencies = true data.raw.recipe['deadlock-packrecipe-' .. item_name].ignore_for_dependencies = true @@ -97,9 +94,9 @@ if mods['LightedPolesPlus'] then end if mods['reverse-factory'] then - local cat = table.array_to_dictionary({ 'recycle-products', 'recycle-intermediates', 'recycle-with-fluids', 'recycle-productivity' }, true) + local cat = table.invert{'recycle-products', 'recycle-intermediates', 'recycle-with-fluids', 'recycle-productivity'} - for item_name, item in py_utils.iter_prototypes('item') do + for item_name, item in py.iter_prototypes('item') do local recipe_name = 'rf-' .. item_name if data.raw.recipe[recipe_name] and cat[data.raw.recipe[recipe_name].category] then @@ -241,7 +238,7 @@ if mods['miniloader'] then end if mods['Flare Stack'] then - local cat = table.array_to_dictionary({ 'gas-venting', 'flaring', 'incineration', 'fuel-incineration' }, true) + local cat = table.invert{'gas-venting', 'flaring', 'incineration', 'fuel-incineration'} for recipe_name, recipe in pairs(data.raw.recipe) do if cat[recipe.category] then @@ -492,17 +489,17 @@ then end end -if mods["aai-loaders"] then - TECHNOLOGY("aai-express-loader"):remove_prereq("advanced-electronics-2") - if mods["pyalienlife"] then - TECHNOLOGY("aai-fast-loader"):remove_prereq('advanced-electronics'):remove_pack('chemical-science-pack') +if mods['aai-loaders'] then + TECHNOLOGY('aai-express-loader'):remove_prereq('advanced-electronics-2') + if mods['pyalienlife'] then + TECHNOLOGY('aai-fast-loader'):remove_prereq('advanced-electronics'):remove_pack('chemical-science-pack') end end -if mods["cargo-ships"] then - TECHNOLOGY("water_transport"):remove_prereq("logistics-2"):remove_pack("logistic-science-pack") - TECHNOLOGY("cargo_ships"):remove_pack("logistic-science-pack") - TECHNOLOGY("automated_water_transport"):remove_pack("logistic-science-pack") +if mods['cargo-ships'] then + TECHNOLOGY('water_transport'):remove_prereq('logistics-2'):remove_pack('logistic-science-pack') + TECHNOLOGY('cargo_ships'):remove_pack('logistic-science-pack') + TECHNOLOGY('automated_water_transport'):remove_pack('logistic-science-pack') end if mods['RenaiTransportation'] then @@ -581,16 +578,16 @@ end if mods['jetpack'] and mods['pyrawores'] and mods['pypetroleumhandling'] then -- using remove_pack doesn't work, I don't understand why - local rocket_fuel = data.raw.technology["rocket-fuel"] - rocket_fuel.prerequisites = mods['pyalienlife'] and { "py-science-pack-mk01", "scrude", "electrolysis"} or {"scrude", "electrolysis"} + local rocket_fuel = data.raw.technology['rocket-fuel'] + rocket_fuel.prerequisites = mods['pyalienlife'] and { 'py-science-pack-mk01', 'scrude', 'electrolysis'} or {'scrude', 'electrolysis'} rocket_fuel.unit = { - ingredients = {{ mods['pyalienlife'] and "py-science-pack-1" or "automation-science-pack", 1 }}, + ingredients = {{ mods['pyalienlife'] and 'py-science-pack-1' or 'automation-science-pack', 1 }}, count = 100, time = 30 } - TECHNOLOGY("jetpack-1"):set_fields{prerequisites = {"rocket-fuel"}}:remove_pack("chemical-science-pack"):remove_pack("logistic-science-pack"):add_pack("py-science-pack-1") - RECIPE("jetpack-1"):add_ingredient({type = 'item', name = 'mechanical-parts-01', amount = 2}):replace_ingredient("electronic-circuit", "electronics-mk01") - TECHNOLOGY("jetpack-2"):set_fields{prerequisites = {"jetpack-1"}}:remove_pack("chemical-science-pack"):add_pack("py-science-pack-2"):add_prereq(mods['pyalienlife'] and 'py-science-pack-mk02' or 'logistic-science-pack') - TECHNOLOGY("jetpack-3"):set_fields{prerequisites = {"jetpack-2"}}:remove_pack("production-science-pack"):remove_pack("py-science-pack-4"):remove_pack("utility-science-pack"):add_pack("py-science-pack-3"):add_prereq(mods['pyalienlife'] and 'py-science-pack-mk03' or 'chemical-science-pack') - TECHNOLOGY("jetpack-4"):set_fields{prerequisites = {"jetpack-3"}}:remove_pack("space-science-pack"):add_prereq('utility-science-pack') + TECHNOLOGY('jetpack-1'):set_fields{prerequisites = {'rocket-fuel'}}:remove_pack('chemical-science-pack'):remove_pack('logistic-science-pack'):add_pack('py-science-pack-1') + RECIPE('jetpack-1'):add_ingredient({type = 'item', name = 'mechanical-parts-01', amount = 2}):replace_ingredient('electronic-circuit', 'electronics-mk01') + TECHNOLOGY('jetpack-2'):set_fields{prerequisites = {'jetpack-1'}}:remove_pack('chemical-science-pack'):add_pack('py-science-pack-2'):add_prereq(mods['pyalienlife'] and 'py-science-pack-mk02' or 'logistic-science-pack') + TECHNOLOGY('jetpack-3'):set_fields{prerequisites = {'jetpack-2'}}:remove_pack('production-science-pack'):remove_pack('py-science-pack-4'):remove_pack('utility-science-pack'):add_pack('py-science-pack-3'):add_prereq(mods['pyalienlife'] and 'py-science-pack-mk03' or 'chemical-science-pack') + TECHNOLOGY('jetpack-4'):set_fields{prerequisites = {'jetpack-3'}}:remove_pack('space-science-pack'):add_prereq('utility-science-pack') end \ No newline at end of file diff --git a/prototypes/functions/data_parser.lua b/prototypes/functions/data_parser.lua index d89df98..077c316 100644 --- a/prototypes/functions/data_parser.lua +++ b/prototypes/functions/data_parser.lua @@ -1,38 +1,34 @@ -local table = require "__stdlib__.stdlib.utils.table" -local string = require "__stdlib__.stdlib.utils.string" - -local config = require "prototypes.config" -local fz_graph = require "prototypes.functions.fuzzy_graph" -local py_utils = require "prototypes.functions.utils" +local config = require 'prototypes.config' +local fz_graph = require 'prototypes.functions.fuzzy_graph' local data_parser = {} data_parser.__index = data_parser -local FUEL_FLUID = "fluid" -local FUEL_ELECTRICITY = "electricity" -local FUEL_HEAT = "heat" - -local RECIPE_PREFIX_START = "start:" -local RECIPE_PREFIX_BURNT = "burnt:" -local RECIPE_PREFIX_BOILER = "boiler:" -local RECIPE_PREFIX_GENERATOR = "generator:" -local RECIPE_PREFIX_OFFSHORE = "offshore:" -local RECIPE_PREFIX_REACTOR = "reactor:" -local RECIPE_PREFIX_MINING = "mining:" -local RECIPE_PREFIX_ROCKET = "rocket-product:" - -local LABEL_RECIPE_RESULT = "__recipe_result__" -local LABEL_MODULE = "__module__" -local LABEL_FUEL = "__fuel__" -local LABEL_RAIL = "__rail__" -local LABEL_LOCO = "__locomotive__" -local LABEL_PUMP = "__pump__" -local LABEL_TRAINSTOP = "__trainstop__" -local LABEL_CRAFTING_MACHINE = "__crafting__" -local LABEL_BONUS = "__bonus__" -local LABEL_GRID = "__grid__" -local LABEL_UNLOCK_RECIPE = "__unlock_recipe__" -local LABEL_TECH_FINISH = "__tech_finish__" +local FUEL_FLUID = 'fluid' +local FUEL_ELECTRICITY = 'electricity' +local FUEL_HEAT = 'heat' + +local RECIPE_PREFIX_START = 'start:' +local RECIPE_PREFIX_BURNT = 'burnt:' +local RECIPE_PREFIX_BOILER = 'boiler:' +local RECIPE_PREFIX_GENERATOR = 'generator:' +local RECIPE_PREFIX_OFFSHORE = 'offshore:' +local RECIPE_PREFIX_REACTOR = 'reactor:' +local RECIPE_PREFIX_MINING = 'mining:' +local RECIPE_PREFIX_ROCKET = 'rocket-product:' + +local LABEL_RECIPE_RESULT = '__recipe_result__' +local LABEL_MODULE = '__module__' +local LABEL_FUEL = '__fuel__' +local LABEL_RAIL = '__rail__' +local LABEL_LOCO = '__locomotive__' +local LABEL_PUMP = '__pump__' +local LABEL_TRAINSTOP = '__trainstop__' +local LABEL_CRAFTING_MACHINE = '__crafting__' +local LABEL_BONUS = '__bonus__' +local LABEL_GRID = '__grid__' +local LABEL_UNLOCK_RECIPE = '__unlock_recipe__' +local LABEL_TECH_FINISH = '__tech_finish__' function data_parser.create() @@ -63,6 +59,13 @@ function data_parser.create() return d end +local function insert_double_lookup(tab, category, item) + if not tab[category] then + tab[category] = {} + end + + tab[category][item] = true +end function data_parser:run() self:pre_process() @@ -71,23 +74,23 @@ function data_parser:run() -- electricity self.fg:add_node(FUEL_ELECTRICITY, fz_graph.NT_ITEM, { virtual = true }) - py_utils.insert_double_lookup(self.fuel_categories, FUEL_ELECTRICITY, FUEL_ELECTRICITY) + insert_double_lookup(self.fuel_categories, FUEL_ELECTRICITY, FUEL_ELECTRICITY) -- heat for _, temp in pairs(self.heat_temps) do local node = self:parse_fluid(FUEL_HEAT, temp, { virtual = true }) - py_utils.insert_double_lookup(self.fuel_categories, FUEL_HEAT, node.name) + insert_double_lookup(self.fuel_categories, FUEL_HEAT, node.name) end -- starting entities for _, entity_name in pairs(config.STARTING_ENTITIES:enumerate()) do - local entity = py_utils.get_prototype("entity", entity_name, true) + local entity = ENTITY(entity_name) if entity and self.placed_by[entity_name] then for item_name, _ in pairs(self.placed_by[entity_name]) do config.STARTING_ITEMS:add(item_name) - if not py_utils.get_prototype("item", item_name, true) then + if not ITEM(item_name) then self.fg:add_node(item_name, fz_graph.NT_ITEM, { virtual = true }) end end @@ -96,13 +99,13 @@ function data_parser:run() -- starting items for _, item_name in pairs(config.STARTING_ITEMS:enumerate()) do - local item = py_utils.get_prototype("item", item_name, true) + local item = ITEM(item_name) if item or self.fg:node_exists(item_name, fz_graph.NT_ITEM) then local recipe = { name = RECIPE_PREFIX_START .. item_name, ingredients = {}, - results = {{ type = "item", name = item_name, amount = 1 }} + results = {{ type = 'item', name = item_name, amount = 1 }} } local node = self:parse_recipe(fz_graph.START_NODE_NAME, recipe, true) @@ -122,7 +125,7 @@ function data_parser:run() end -- minable entities - for _, entity in py_utils.iter_prototypes("entity") do + for _, entity in py.iter_prototypes('entity') do if (entity.script_autoplace or entity.autoplace) and entity.minable and (entity.minable.result or entity.minable.results) then self:add_mining_recipe(entity) end @@ -140,7 +143,9 @@ end function data_parser:parse_recipe(tech_name, recipe, no_crafting) - local name = (tech_name and (tech_name .. " / ") or "") .. recipe.name + recipe:standardize() + + local name = (tech_name and (tech_name .. ' / ') or '') .. recipe.name local node = self.fg:add_node(name, fz_graph.NT_RECIPE, { tech_name = tech_name, factorio_name = (not recipe.virtual and recipe.name), virtual = recipe.virtual }) @@ -157,11 +162,11 @@ function data_parser:parse_recipe(tech_name, recipe, no_crafting) local fluid_out = 0 local ingredients = {} - local recipe_data = (type(recipe.normal) == "table" and recipe.normal or recipe) + local recipe_data = (type(recipe.normal) == 'table' and recipe.normal or recipe) - for _, ing in pairs(py_utils.standardize_products(recipe_data.ingredients)) do - if ing.type == "item" then - local item = py_utils.get_prototype("item", ing.name) + for _, ing in pairs(recipe_data.ingredients) do + if ing.type == 'item' then + local item = ITEM(ing.name) local node_item = self:parse_item(item) self.fg:add_link(node_item, node, ing.name) ing_count = ing_count + 1 @@ -190,10 +195,10 @@ function data_parser:parse_recipe(tech_name, recipe, no_crafting) end if (recipe.unlock_results ~= false) and not recipe.ignore_in_pypp then - for _, res in pairs(py_utils.standardize_products(recipe_data.results, nil, recipe_data.result, recipe_data.result_count)) do - if res.type == "item" then + for _, res in pairs(recipe_data.results) do + if res.type == 'item' then self:add_recipe_result_item(res.name, recipe.name, node, ingredients) - elseif res.type == "fluid" then + elseif res.type == 'fluid' then local fluid = data.raw.fluid[res.name] local temp = res.temperature or (fluid and fluid.default_temperature) @@ -223,7 +228,7 @@ function data_parser:parse_recipe(tech_name, recipe, no_crafting) end if not no_crafting then - local category = recipe.category or "crafting" + local category = recipe.category or 'crafting' local found = false if self.crafting_categories[category] then @@ -236,7 +241,7 @@ function data_parser:parse_recipe(tech_name, recipe, no_crafting) end if not found and not recipe.ignore_for_dependencies then - error("\n\nERROR: Missing crafting category: " .. category .. " (ingredients: " .. ing_count .. ", fluids in: " .. fluid_in .. ", fluids out:" .. fluid_out .. "), for " .. name .. "\n", 0) + error('\n\nERROR: Missing crafting category: ' .. category .. ' (ingredients: ' .. ing_count .. ', fluids in: ' .. fluid_in .. ', fluids out:' .. fluid_out .. '), for ' .. name .. '\n', 0) end end @@ -254,15 +259,14 @@ function data_parser:add_recipe_result_item(item_name, recipe_name, recipe_node, local item if not node_item.virtual then - item = py_utils.get_prototype("item", item_name) + item = ITEM(item_name) node_item = self:parse_item(item) end self.fg:add_link(recipe_node, node_item, LABEL_RECIPE_RESULT) for entity_name, _ in pairs(self.place_result[item_name] or {}) do - local entity = py_utils.get_prototype("entity", entity_name) - self:add_entity_dependencies(entity, recipe_node, recipe_name, item, ingredients) + self:add_entity_dependencies(ENTITY(entity_name), recipe_node, recipe_name, item, ingredients) end if item and (item.rocket_launch_products or item.rocket_launch_product) then @@ -275,6 +279,8 @@ end function data_parser:parse_tech(tech) + tech:standardize() + local node = self.fg:add_node(tech.name, fz_graph.NT_TECH_HEAD, { tech_name = tech.name, factorio_name = tech.name }) if self.processed_techs[tech.name] then @@ -296,25 +302,25 @@ function data_parser:parse_tech(tech) -- local n_parent = self.fg:add_node(dep, fz_graph.NT_TECH_TAIL) -- self.fg:add_link(n_parent, node) -- else - -- error("\n\nInvalid tech dependency: " .. dep .. "\nSource: " .. tech.name .. "\n") + -- error('\n\nInvalid tech dependency: ' .. dep .. '\nSource: ' .. tech.name .. '\n') -- end -- end local packs = {} -- Science packs - for _, ing in pairs(py_utils.standardize_products(tech.unit.ingredients)) do - local item = py_utils.get_prototype(fz_graph.NT_ITEM, ing.name) + for _, ing in pairs(tech.unit.ingredients) do + local item = ITEM(ing.name) local n_item = self:parse_item(item) self.fg:add_link(n_item, node, ing.name) table.insert(packs, ing.name) - py_utils.insert_double_lookup(self.science_packs, ing.name, tech.name) + insert_double_lookup(self.science_packs, ing.name, tech.name) end node:add_label(LABEL_CRAFTING_MACHINE) local found_lab = false for _, lab in pairs(data.raw.lab) do - local inputs = table.array_to_dictionary(lab.inputs) + local inputs = table.invert(lab.inputs) if table.all(packs, function(p) return inputs[p] end) then for item, _ in pairs(self.placed_by[lab.name] or {}) do @@ -326,12 +332,12 @@ function data_parser:parse_tech(tech) end if not found_lab then - error("\n\nNo suitable lab found to research tech: " .. node.name .. "\n", 0) + error('\n\nNo suitable lab found to research tech: ' .. node.name .. '\n', 0) end -- Recipes for _, effect in pairs(tech.effects or {}) do - if effect.type == "unlock-recipe" then + if effect.type == 'unlock-recipe' then local recipe = data.raw.recipe[effect.recipe] if recipe then @@ -342,45 +348,45 @@ function data_parser:parse_tech(tech) end end -- Bonuses require at least on entity where they can be applied - elseif effect.type == "inserter-stack-size-bonus" then - self:add_bonus_dependencies(node, effect, "inserter", function(e) return not e.stack end) - elseif effect.type == "stack-inserter-capacity-bonus" then - self:add_bonus_dependencies(node, effect, "inserter", function(e) return e.stack end) - elseif effect.type == "laboratory-speed" or effect.type == "laboratory-productivity" then - self:add_bonus_dependencies(node, effect, "lab") - elseif effect.type == "mining-drill-productivity-bonus" then - self:add_bonus_dependencies(node, effect, "mining-drill") - elseif effect.type == "train-braking-force-bonus" then - self:add_bonus_dependencies(node, effect, "locomotive") - elseif effect.type == "maximum-following-robots-count" or effect.type == "follower-robot-lifetime" then - self:add_bonus_dependencies(node, effect, "combat-robot", function(e) return e.follows_player end) - elseif effect.type == "worker-robot-speed" or effect.type == "worker-robot-storage" or effect.type == "worker-robot-battery" then - self:add_bonus_dependencies(node, effect, "construction-robot") - self:add_bonus_dependencies(node, effect, "logistic-robot") - elseif effect.type == "character-logistic-requests" or effect.type == "character-logistic-trash-slots" then - self:add_bonus_dependencies(node, effect, "logistic-robot") - elseif effect.type == "artillery-range" then - self:add_bonus_dependencies(node, effect, "artillery-turret") - self:add_bonus_dependencies(node, effect, "artillery-wagon") - elseif effect.type == "turret-attack" then - self:add_bonus_dependencies(node, effect, "ammo-turret", function(e) return e.name == effect.turret_id end, false, effect.turret_id) - self:add_bonus_dependencies(node, effect, "electric-turret", function(e) return e.name == effect.turret_id end, false, effect.turret_id) - self:add_bonus_dependencies(node, effect, "fluid-turret", function(e) return e.name == effect.turret_id end, false, effect.turret_id) - elseif effect.type == "ammo-damage" or effect.type == "gun-speed" then - self:add_bonus_dependencies(node, effect, "ammo", function (i) return + elseif effect.type == 'inserter-stack-size-bonus' then + self:add_bonus_dependencies(node, effect, 'inserter', function(e) return not e.stack end) + elseif effect.type == 'stack-inserter-capacity-bonus' then + self:add_bonus_dependencies(node, effect, 'inserter', function(e) return e.stack end) + elseif effect.type == 'laboratory-speed' or effect.type == 'laboratory-productivity' then + self:add_bonus_dependencies(node, effect, 'lab') + elseif effect.type == 'mining-drill-productivity-bonus' then + self:add_bonus_dependencies(node, effect, 'mining-drill') + elseif effect.type == 'train-braking-force-bonus' then + self:add_bonus_dependencies(node, effect, 'locomotive') + elseif effect.type == 'maximum-following-robots-count' or effect.type == 'follower-robot-lifetime' then + self:add_bonus_dependencies(node, effect, 'combat-robot', function(e) return e.follows_player end) + elseif effect.type == 'worker-robot-speed' or effect.type == 'worker-robot-storage' or effect.type == 'worker-robot-battery' then + self:add_bonus_dependencies(node, effect, 'construction-robot') + self:add_bonus_dependencies(node, effect, 'logistic-robot') + elseif effect.type == 'character-logistic-requests' or effect.type == 'character-logistic-trash-slots' then + self:add_bonus_dependencies(node, effect, 'logistic-robot') + elseif effect.type == 'artillery-range' then + self:add_bonus_dependencies(node, effect, 'artillery-turret') + self:add_bonus_dependencies(node, effect, 'artillery-wagon') + elseif effect.type == 'turret-attack' then + self:add_bonus_dependencies(node, effect, 'ammo-turret', function(e) return e.name == effect.turret_id end, false, effect.turret_id) + self:add_bonus_dependencies(node, effect, 'electric-turret', function(e) return e.name == effect.turret_id end, false, effect.turret_id) + self:add_bonus_dependencies(node, effect, 'fluid-turret', function(e) return e.name == effect.turret_id end, false, effect.turret_id) + elseif effect.type == 'ammo-damage' or effect.type == 'gun-speed' then + self:add_bonus_dependencies(node, effect, 'ammo', function (i) return (i.ammo_type.category and i.ammo_type.category == effect.ammo_category) or not i.ammo_type.category and table.any(i.ammo_type, function (at) return at.category == effect.ammo_category end) end, true, effect.ammo_category) - self:add_bonus_dependencies(node, effect, "capsule", function (i) return + self:add_bonus_dependencies(node, effect, 'capsule', function (i) return i.capsule_action.attack_parameters and ((i.capsule_action.attack_parameters.ammo_type.category and i.capsule_action.attack_parameters.ammo_type.category == effect.ammo_category) or not i.capsule_action.attack_parameters.ammo_type.category and table.any(i.capsule_action.attack_parameters.ammo_type, function (at) return at.category == effect.ammo_category end)) end, true, effect.ammo_category) - self:add_bonus_dependencies(node, effect, "electric-turret", function (e) return + self:add_bonus_dependencies(node, effect, 'electric-turret', function (e) return (e.attack_parameters.ammo_type.category and e.attack_parameters.ammo_type.category == effect.ammo_category) or not e.attack_parameters.ammo_type.category and table.any(e.attack_parameters.ammo_type, function (at) return at.category == effect.ammo_category end) end, false, effect.ammo_category) - self:add_bonus_dependencies(node, effect, "land-mine", function (e) return ((e.ammo_category or "") == effect.ammo_category) end, false, effect.ammo_category) + self:add_bonus_dependencies(node, effect, 'land-mine', function (e) return ((e.ammo_category or '') == effect.ammo_category) end, false, effect.ammo_category) end end end @@ -391,7 +397,7 @@ function data_parser.get_fluid_name(fluid_name, temperature) temperature = data.raw.fluid[fluid_name].default_temperature end - return fluid_name .. "(" .. temperature .. ")" + return fluid_name .. '(' .. temperature .. ')' end @@ -445,19 +451,19 @@ function data_parser:parse_item(item) end if item.place_result then - local entity = py_utils.get_prototype("entity", item.place_result) + local entity = ENTITY(item.place_result) - if entity.type == "boiler" then + if entity.type == 'boiler' then self:add_boiler_recipe(entity) - elseif entity.type == "generator" then + elseif entity.type == 'generator' then self:add_generator_recipe(entity) - elseif entity.type == "burner-generator" then + elseif entity.type == 'burner-generator' then self:add_simple_generator_recipe(entity) - elseif entity.type == "electric-energy-interface" and entity.energy_production and util.parse_energy(entity.energy_production) > 0 then + elseif entity.type == 'electric-energy-interface' and entity.energy_production and util.parse_energy(entity.energy_production) > 0 then self:add_simple_generator_recipe(entity) - elseif entity.type == "offshore-pump" then + elseif entity.type == 'offshore-pump' then self:add_offhsore_pump_recipe(entity) - elseif entity.type == "reactor" then + elseif entity.type == 'reactor' then self:add_reactor_recipe(entity) end end @@ -467,7 +473,7 @@ end function data_parser:add_module_dependencies(node, recipe) - local category = data.raw["recipe-category"][recipe.category or "crafting"] + local category = data.raw['recipe-category'][recipe.category or 'crafting'] if category.modules_required and category.allowed_module_categories then node:add_label(LABEL_MODULE) @@ -487,16 +493,16 @@ function data_parser:add_entity_dependencies(entity, recipe_node, recipe_name, i local energy_source = entity.burner or entity.energy_source if energy_source then - if energy_source.type == "burner" then + if energy_source.type == 'burner' then recipe_node:add_label(LABEL_FUEL) - for _, category in pairs(energy_source.fuel_categories or { (energy_source.fuel_category or "chemical") }) do + for _, category in pairs(energy_source.fuel_categories or { (energy_source.fuel_category or 'chemical') }) do for fuel, _ in pairs(self.fuel_categories[category] or {}) do local fuel_node = self.fg:add_node(fuel, fz_graph.NT_ITEM) self.fg:add_link(fuel_node, recipe_node, LABEL_FUEL) end end - elseif energy_source.type == "fluid" and energy_source.fluid_box.filter ~= "void" then + elseif energy_source.type == 'fluid' and energy_source.fluid_box.filter ~= 'void' then recipe_node:add_label(LABEL_FUEL) local fluid_name = energy_source.fluid_box.filter @@ -512,11 +518,11 @@ function data_parser:add_entity_dependencies(entity, recipe_node, recipe_name, i end end end - elseif energy_source.type == "electric" and not config.ELECTRICITY_PRODUCER_PROTOTYPES:contains(entity.type) then + elseif energy_source.type == 'electric' and not config.ELECTRICITY_PRODUCER_PROTOTYPES:contains(entity.type) then recipe_node:add_label(LABEL_FUEL) local fuel_node = self.fg:get_node(FUEL_ELECTRICITY, fz_graph.NT_ITEM) self.fg:add_link(fuel_node, recipe_node, LABEL_FUEL) - elseif energy_source.type == "heat" then + elseif energy_source.type == 'heat' then recipe_node:add_label(LABEL_FUEL) for _, temp in pairs(self.heat_temps) do @@ -539,25 +545,25 @@ function data_parser:add_entity_dependencies(entity, recipe_node, recipe_name, i end if entity.minable then - for _, res in pairs(py_utils.standardize_products(entity.minable.results, nil, entity.minable.result, entity.minable.count)) do - if res.type == "item" and res.name ~= nil and res.name ~= item.name then + for _, res in pairs(entity.minable.results) do + if res.type == 'item' and res.name ~= nil and res.name ~= item.name then self:add_recipe_result_item(res.name, recipe_name, recipe_node, ingredients) end end end -- Rail stuff need rails - if entity.type == "locomotive" - or entity.type == "cargo-wagon" - or entity.type == "fluid-wagon" - or entity.type == "artillery-wagon" - or entity.type == "train-stop" - or entity.type == "rail-signal" - or entity.type == "rail-chain-signal" + if entity.type == 'locomotive' + or entity.type == 'cargo-wagon' + or entity.type == 'fluid-wagon' + or entity.type == 'artillery-wagon' + or entity.type == 'train-stop' + or entity.type == 'rail-signal' + or entity.type == 'rail-chain-signal' then recipe_node:add_label(LABEL_RAIL) - for i, _ in pairs(data.raw["rail-planner"] or {}) do + for i, _ in pairs(data.raw['rail-planner'] or {}) do if self.items[i] then local r = self.fg:add_node(i, fz_graph.NT_ITEM) self.fg:add_link(r, recipe_node, LABEL_RAIL) @@ -566,7 +572,7 @@ function data_parser:add_entity_dependencies(entity, recipe_node, recipe_name, i end -- Wagons need loco - if entity.type == "cargo-wagon" or entity.type == "fluid-wagon" or entity.type == "artillery-wagon" then + if entity.type == 'cargo-wagon' or entity.type == 'fluid-wagon' or entity.type == 'artillery-wagon' then recipe_node:add_label(LABEL_LOCO) for loco, _ in pairs(data.raw.locomotive or {}) do @@ -580,7 +586,7 @@ function data_parser:add_entity_dependencies(entity, recipe_node, recipe_name, i end -- Fluid wagons need pumps - if entity.type == "fluid-wagon" then + if entity.type == 'fluid-wagon' then recipe_node:add_label(LABEL_PUMP) for pump, _ in pairs(data.raw.pump or {}) do @@ -594,10 +600,10 @@ function data_parser:add_entity_dependencies(entity, recipe_node, recipe_name, i end -- Signals need stations - if entity.type == "rail-signal" or entity.type == "rail-chain-signal" then + if entity.type == 'rail-signal' or entity.type == 'rail-chain-signal' then recipe_node:add_label(LABEL_TRAINSTOP) - for ts, _ in pairs(data.raw["train-stop"] or {}) do + for ts, _ in pairs(data.raw['train-stop'] or {}) do if self.entities[ts] then for i, _ in pairs(self.placed_by[ts] or {}) do local l = self.fg:add_node(i, fz_graph.NT_ITEM) @@ -622,7 +628,7 @@ function data_parser:add_burnt_result_recipe(item) node:add_label(LABEL_CRAFTING_MACHINE) for entity_name, _ in pairs(self.fuel_burners[item.fuel_category] or {}) do - local burner = py_utils.get_prototype("entity", entity_name) + local burner = ENTITY(entity_name) local energy_source = burner.burner or burner.energy_source if energy_source and (energy_source.burnt_inventory_size or 0) > 0 then @@ -643,15 +649,15 @@ end function data_parser:add_boiler_recipe(boiler) - if (boiler.mode or "heat-water-inside") == "output-to-separate-pipe" then + if (boiler.mode or 'heat-water-inside') == 'output-to-separate-pipe' then local out_fluid = boiler.output_fluid_box and boiler.output_fluid_box.filter or boiler.fluid_box.filter local in_fluid = boiler.fluid_box.filter if out_fluid and in_fluid then local recipe = { name = RECIPE_PREFIX_BOILER .. boiler.name, - ingredients = {{ type = "fluid", name = in_fluid, amount = 1, minimum_temperature = boiler.fluid_box.minimum_temperature, maximum_temperature = boiler.fluid_box.maximum_temperature }}, - results = {{ type = "fluid", name = out_fluid, amount = 1, temperature = boiler.target_temperature }}, + ingredients = {{ type = 'fluid', name = in_fluid, amount = 1, minimum_temperature = boiler.fluid_box.minimum_temperature, maximum_temperature = boiler.fluid_box.maximum_temperature }}, + results = {{ type = 'fluid', name = out_fluid, amount = 1, temperature = boiler.target_temperature }}, virtual = true } @@ -660,7 +666,7 @@ function data_parser:add_boiler_recipe(boiler) return node else - error("ERROR: Unsupported feature: Unfiltered boiler") + error('ERROR: Unsupported feature: Unfiltered boiler') end else local fluid = boiler.fluid_box.filter @@ -669,11 +675,11 @@ function data_parser:add_boiler_recipe(boiler) local recipe = { name = RECIPE_PREFIX_BOILER .. boiler.name, ingredients = {{ - type = "fluid", + type = 'fluid', name = fluid, amount = 1, minimum_temperature = boiler.fluid_box.minimum_temperature, maximum_temperature = math.min (boiler.fluid_box.maximum_temperature or data.raw.fluid[fluid].max_temperature, data.raw.fluid[fluid].max_temperature - 1) }}, - results = {{ type = "fluid", name = fluid, amount = 1, temperature = data.raw.fluid[fluid].max_temperature }}, + results = {{ type = 'fluid', name = fluid, amount = 1, temperature = data.raw.fluid[fluid].max_temperature }}, virtual = true } @@ -682,7 +688,7 @@ function data_parser:add_boiler_recipe(boiler) return node else - error("ERROR: Unsupported feature: Unfiltered boiler") + error('ERROR: Unsupported feature: Unfiltered boiler') end end end @@ -695,12 +701,12 @@ function data_parser:add_generator_recipe(generator) local recipe = { name = RECIPE_PREFIX_GENERATOR .. generator.name, ingredients = {}, - results = {{ type = "item", name = FUEL_ELECTRICITY, amount = 1 }}, + results = {{ type = 'item', name = FUEL_ELECTRICITY, amount = 1 }}, virtual = true } if not generator.burns_fluid then - recipe.ingredients = {{ type = "fluid", name = in_fluid, amount = 1, minimum_temperature = generator.fluid_box.minimum_temperature, maximum_temperature = generator.fluid_box.maximum_temperature }} + recipe.ingredients = {{ type = 'fluid', name = in_fluid, amount = 1, minimum_temperature = generator.fluid_box.minimum_temperature, maximum_temperature = generator.fluid_box.maximum_temperature }} end node = self:parse_recipe(nil, recipe, true) @@ -717,7 +723,7 @@ function data_parser:add_offhsore_pump_recipe(pump) local recipe = { name = RECIPE_PREFIX_OFFSHORE .. pump.name, ingredients = {}, - results = {{ type = "fluid", name = pump.fluid, amount = pump.pumping_speed }}, + results = {{ type = 'fluid', name = pump.fluid, amount = pump.pumping_speed }}, virtual = true } @@ -732,7 +738,7 @@ function data_parser:add_simple_generator_recipe(generator) local recipe = { name = RECIPE_PREFIX_GENERATOR .. generator.name, ingredients = {}, - results = {{ type = "item", name = FUEL_ELECTRICITY, amount = 1 }}, + results = {{ type = 'item', name = FUEL_ELECTRICITY, amount = 1 }}, virtual = true } @@ -745,7 +751,7 @@ function data_parser:add_reactor_recipe(reactor) local recipe = { name = RECIPE_PREFIX_REACTOR .. reactor.name, ingredients = {}, - results = {{ type = "fluid", name = FUEL_HEAT, amount = 1, temperature = reactor.heat_buffer.max_temperature }}, + results = {{ type = 'fluid', name = FUEL_HEAT, amount = 1, temperature = reactor.heat_buffer.max_temperature }}, virtual = true } @@ -757,7 +763,7 @@ end function data_parser:add_mining_recipe(entity) local recipe = { name = RECIPE_PREFIX_MINING .. entity.name, - ingredients = entity.minable.required_fluid and {{ type = "fluid", name = entity.minable.required_fluid, amount = entity.minable.fluid_amount }} or {}, + ingredients = entity.minable.required_fluid and {{ type = 'fluid', name = entity.minable.required_fluid, amount = entity.minable.fluid_amount }} or {}, results = entity.minable.results, result = entity.minable.result, result_count = entity.minable.count @@ -765,8 +771,8 @@ function data_parser:add_mining_recipe(entity) local node = self:parse_recipe(nil, recipe, true) - if entity.type == "resource" then - local category = (entity.category or "basic-solid") .. (entity.minable.required_fluid and "+fluid" or "") + if entity.type == 'resource' then + local category = (entity.category or 'basic-solid') .. (entity.minable.required_fluid and '+fluid' or '') if self.mining_categories[category] then for miner, _ in pairs(self.mining_categories[category]) do @@ -774,7 +780,7 @@ function data_parser:add_mining_recipe(entity) end end else - self:add_crafting_machine_link(node, "character") + self:add_crafting_machine_link(node, 'character') end return node @@ -782,7 +788,7 @@ end function data_parser:add_bonus_dependencies(tech_node, effect, entity_type, condition, is_item, suffix) - local recipe = { name = effect.type .. (suffix or ""), ingredients = {}, results = {}, virtual = true } + local recipe = { name = effect.type .. (suffix or ''), ingredients = {}, results = {}, virtual = true } local recipe_node = self:parse_recipe(tech_node.name, recipe, true) self.fg:add_link(tech_node, recipe_node, LABEL_UNLOCK_RECIPE) @@ -807,7 +813,7 @@ end function data_parser:add_rocket_product_recipe(item) local recipe = { name = RECIPE_PREFIX_ROCKET .. item.name, - ingredients = {{ type = "item", name = item.name, amount = 1 }}, + ingredients = {{ type = 'item', name = item.name, amount = 1 }}, results = item.rocket_launch_products or { item.rocket_launch_product }, virtual = true } @@ -817,9 +823,9 @@ function data_parser:add_rocket_product_recipe(item) -- self.fg:add_link(tech_node, node) node:add_label(LABEL_CRAFTING_MACHINE) - for _, entity in pairs(data.raw["rocket-silo"]) do + for _, entity in pairs(data.raw['rocket-silo']) do if (entity.rocket_result_inventory_size or 0) > 0 and entity.fixed_recipe then - local rocket = data.raw["rocket-silo-rocket"][entity.rocket_entity] + local rocket = data.raw['rocket-silo-rocket'][entity.rocket_entity] if rocket and rocket.inventory_size > 0 then self:add_crafting_machine_link(node, entity.name) @@ -834,18 +840,16 @@ end function data_parser:pre_process() -- Starter entities for _, e in pairs(config.STARTING_ENTITIES:enumerate()) do - local entity = py_utils.get_prototype("entity", e, true) - + local entity = ENTITY(e) if entity then - py_utils.insert_double_lookup(self.placed_by, entity.name, entity.name) + insert_double_lookup(self.placed_by, entity.name, entity.name) self:pre_process_entity(entity) end end -- Starter items for _, i in pairs(config.STARTING_ITEMS:enumerate()) do - local item = py_utils.get_prototype("item", i, true) - + local item = ITEM(i) if item then self:pre_process_item(item) end @@ -859,9 +863,9 @@ function data_parser:pre_process() end -- Minables - for _, entity in py_utils.iter_prototypes("entity") do + for _, entity in py.iter_prototypes('entity') do if (entity.script_autoplace or entity.autoplace) and entity.minable and (entity.minable.result or entity.minable.results) then - self:pre_process_entity(entity) + self:pre_process_entity(entity:standardize()) end end @@ -873,11 +877,11 @@ end function data_parser:pre_process_techs() for _, tech in pairs(data.raw.technology) do if tech.enabled ~= false and not tech.hidden then - -- log("Pre-processing tech: " .. tech.name) + -- log('Pre-processing tech: ' .. tech.name) self.techs[tech.name] = true for _, effect in pairs(tech.effects or {}) do - if effect.type == "unlock-recipe" then + if effect.type == 'unlock-recipe' then local recipe = data.raw.recipe[effect.recipe] if recipe then @@ -887,12 +891,12 @@ function data_parser:pre_process_techs() end -- Add dependencies for tech names ending in numbers to the prev tier - local split = string.split(tech.name, "-") + local split = tech.name:split('-') local last = table.last(split) - if string.is_digit(last) and tonumber(last) > 1 then + if last:is_digit() and tonumber(last) > 1 then split[#split] = tostring(tonumber(last) - 1) - local prev_tech = string.join("-", split) + local prev_tech = table.concat(split, '-') if data.raw.technology[prev_tech] then if not tech.dependencies then @@ -902,18 +906,6 @@ function data_parser:pre_process_techs() table.insert(tech.dependencies, prev_tech) end end - - -- for _, pre in pairs(tech.prerequisites or {}) do - -- local pre_tech = data.raw.technology[pre] - - -- if pre_tech and not py_utils.is_py_or_base_tech(pre_tech) then - -- if not tech.dependencies then - -- tech.dependencies = {} - -- end - - -- table.insert(tech.dependencies, pre) - -- end - -- end end end end @@ -926,21 +918,20 @@ function data_parser:pre_process_entity(entity) self.entities[entity.name] = entity - if table.any(entity.flags or {}, function(v) return v == "hidden" end) and not config.STARTING_ENTITIES:contains(entity.name) then + if table.any(entity.flags or {}, function(v) return v == 'hidden' end) and not config.STARTING_ENTITIES:contains(entity.name) then return end - if entity.minable and (entity.minable.result or entity.minable.results) then - for _, res in pairs(py_utils.standardize_products(entity.minable.results, nil, entity.minable.result, entity.minable.count)) do - if res.type == "fluid" then - local fluid = data.raw.fluid[res.name] + if entity.minable then + for _, res in pairs(entity.minable.results) do + if res.type == 'fluid' then + local fluid = FLUID(res.name) if fluid then self:pre_process_fluid(fluid, res.temperature) end else - local item = py_utils.get_prototype("item", res.name) - self:pre_process_item(item) + self:pre_process_item(ITEM(res.name)) end end end @@ -949,22 +940,22 @@ function data_parser:pre_process_entity(entity) local fb_out = 0 for _, fb in pairs(entity.fluid_boxes or {}) do - if type(fb) == "table" then - if fb.production_type == "input" or fb.production_type == "input-output" then + if type(fb) == 'table' then + if fb.production_type == 'input' or fb.production_type == 'input-output' then fb_in = fb_in + 1 - elseif fb.production_type == "output" then + elseif fb.production_type == 'output' then fb_out = fb_out + 1 end end end - -- If handcraft is disabled, we fudge it by adding "crafting" to the real list + -- If handcraft is disabled, we fudge it by adding 'crafting' to the real list local category_list - if (entity.type == "character" + if (entity.type == 'character' and entity.crafting_categories - and not table.invert(entity.crafting_categories)["crafting"]) + and not table.invert(entity.crafting_categories)['crafting']) then - category_list = table.array_combine(entity.crafting_categories, {"crafting"}) + category_list = table.array_combine(entity.crafting_categories, {'crafting'}) else category_list = entity.crafting_categories or {} end @@ -985,22 +976,22 @@ function data_parser:pre_process_entity(entity) end if entity.equipment_grid then - self.entities_with_grid[entity.name] = data.raw["equipment-grid"][entity.equipment_grid] + self.entities_with_grid[entity.name] = data.raw['equipment-grid'][entity.equipment_grid] end local energy_source = entity.burner or entity.energy_source - if energy_source and (entity.burner or energy_source.type == "burner") then - for _, category in pairs(energy_source.fuel_categories or { (energy_source.fuel_category or "chemical") }) do - py_utils.insert_double_lookup(self.fuel_burners, category, entity.name) + if energy_source and (entity.burner or energy_source.type == 'burner') then + for _, category in pairs(energy_source.fuel_categories or { (energy_source.fuel_category or 'chemical') }) do + insert_double_lookup(self.fuel_burners, category, entity.name) end end - if entity.type == "boiler" then + if entity.type == 'boiler' then local filter local temp - if (entity.mode or "heat-water-inside") == "output-to-separate-pipe" then + if (entity.mode or 'heat-water-inside') == 'output-to-separate-pipe' then filter = entity.output_fluid_box.filter or entity.fluid_box.filter temp = entity.target_temperature else @@ -1012,33 +1003,33 @@ function data_parser:pre_process_entity(entity) local fluid = data.raw.fluid[filter] self:pre_process_fluid(fluid, temp) else - error("ERROR: Unsupported feature: Unfiltered boiler") + error('ERROR: Unsupported feature: Unfiltered boiler') end - elseif entity.type == "mining-drill" then + elseif entity.type == 'mining-drill' then for _, category in pairs(entity.resource_categories or {}) do - py_utils.insert_double_lookup(self.mining_categories, category, entity.name) + insert_double_lookup(self.mining_categories, category, entity.name) if entity.input_fluid_box then - py_utils.insert_double_lookup(self.mining_categories, category .. "+fluid", entity.name) + insert_double_lookup(self.mining_categories, category .. '+fluid', entity.name) end end - elseif entity.type == "character" then - py_utils.insert_double_lookup(self.placed_by, entity.name, entity.name) + elseif entity.type == 'character' then + insert_double_lookup(self.placed_by, entity.name, entity.name) for _, category in pairs(entity.mining_categories or {}) do - py_utils.insert_double_lookup(self.mining_categories, category, entity.name) + insert_double_lookup(self.mining_categories, category, entity.name) end - elseif entity.type == "reactor" or entity.type == "heat-interface" then + elseif entity.type == 'reactor' or entity.type == 'heat-interface' then table.insert(self.heat_temps, entity.heat_buffer.max_temperature) end end function data_parser:pre_process_fluid(fluid, temperature) - py_utils.insert_double_lookup(self.fluids, fluid.name, temperature or fluid.default_temperature) + insert_double_lookup(self.fluids, fluid.name, temperature or fluid.default_temperature) if fluid.fuel_value and util.parse_energy(fluid.fuel_value) > 0 then - py_utils.insert_double_lookup(self.fuel_categories, FUEL_FLUID, fluid.name) + insert_double_lookup(self.fuel_categories, FUEL_FLUID, fluid.name) end end @@ -1051,33 +1042,33 @@ function data_parser:pre_process_item(item) self.items[item.name] = item if item.fuel_category and item.fuel_value and util.parse_energy(item.fuel_value) > 0 - and not table.any(item.flags or {}, function(v) return v == "hidden" end) + and not table.any(item.flags or {}, function(v) return v == 'hidden' end) then - py_utils.insert_double_lookup(self.fuel_categories, item.fuel_category, item.name) + insert_double_lookup(self.fuel_categories, item.fuel_category, item.name) end if item.place_result then - py_utils.insert_double_lookup(self.placed_by, item.place_result, item.name) - py_utils.insert_double_lookup(self.place_result, item.name, item.place_result) - self:pre_process_entity(py_utils.get_prototype("entity", item.place_result)) + insert_double_lookup(self.placed_by, item.place_result, item.name) + insert_double_lookup(self.place_result, item.name, item.place_result) + self:pre_process_entity(ENTITY(item.place_result)) end for _, entity_name in pairs(config.ENTITY_SCRIPT_UNLOCKS[item.name] or {}) do - py_utils.insert_double_lookup(self.placed_by, entity_name, item.name) - py_utils.insert_double_lookup(self.place_result, item.name, entity_name) - self:pre_process_entity(py_utils.get_prototype("entity", entity_name)) + insert_double_lookup(self.placed_by, entity_name, item.name) + insert_double_lookup(self.place_result, item.name, entity_name) + self:pre_process_entity(ENTITY(entity_name)) end if item.placed_as_equipment_result then - py_utils.insert_double_lookup(self.placed_by, item.placed_as_equipment_result, item.name) + insert_double_lookup(self.placed_by, item.placed_as_equipment_result, item.name) end - if item.type == "module" then - py_utils.insert_double_lookup(self.module_categories, item.category, item.name) + if item.type == 'module' then + insert_double_lookup(self.module_categories, item.category, item.name) end -- Fucking capsules - if item.type == "capsule" and item.capsule_action.type == "throw" then + if item.type == 'capsule' and item.capsule_action.type == 'throw' then local ap = item.capsule_action.attack_parameters if ap.ammo_type and ap.ammo_type.action then @@ -1089,7 +1080,7 @@ function data_parser:pre_process_item(item) if ad.type then ad = { ad } end for _, d in pairs(ad) do - if d.type == "projectile" then + if d.type == 'projectile' then local pr_action = data.raw.projectile[d.projectile].action if pr_action and pr_action.type then pr_action = { pr_action } end @@ -1104,9 +1095,9 @@ function data_parser:pre_process_item(item) if te.type then te = { te } end for _, tee in pairs(te) do - if tee.type == "create-entity" then - py_utils.insert_double_lookup(self.placed_by, tee.entity_name, item.name) - self:pre_process_entity(py_utils.get_prototype("entity", tee.entity_name)) + if tee.type == 'create-entity' then + insert_double_lookup(self.placed_by, tee.entity_name, item.name) + self:pre_process_entity(ENTITY(tee.entity_name)) end end end @@ -1119,7 +1110,7 @@ function data_parser:pre_process_item(item) end if item.equipment_grid then - self.items_with_grid[item.name] = data.raw["equipment-grid"][item.equipment_grid] + self.items_with_grid[item.name] = data.raw['equipment-grid'][item.equipment_grid] end end @@ -1135,14 +1126,14 @@ function data_parser:pre_process_recipe(recipe) if (recipe.unlock_results ~= false) and not recipe.ignore_in_pypp then for _, res in pairs(py_utils.standardize_products(r.results, nil, r.result, r.result_count)) do - if res.type == "fluid" then + if res.type == 'fluid' then local fluid = data.raw.fluid[res.name] if fluid then self:pre_process_fluid(fluid, res.temperature) end else - local item = py_utils.get_prototype("item", res.name) + local item = ITEM(res.name) self:pre_process_item(item) end end diff --git a/prototypes/functions/fuzzy_graph.lua b/prototypes/functions/fuzzy_graph.lua index f903fe2..8f3bb62 100644 --- a/prototypes/functions/fuzzy_graph.lua +++ b/prototypes/functions/fuzzy_graph.lua @@ -1,6 +1,4 @@ -local graph = require "luagraphs.data.graph" -local table = require "__stdlib__.stdlib.utils.table" - +local graph = require 'luagraphs.data.graph' local fz_graph = {} fz_graph.__index = fz_graph @@ -9,15 +7,15 @@ fz_graph.node = {} fz_graph.node.__index = fz_graph.node -fz_graph.START_NODE_NAME = "__START__" -fz_graph.NT_TECH_HEAD = "tech-h" -fz_graph.NT_TECH_TAIL = "tech-t" -fz_graph.NT_ITEM = "item" -fz_graph.NT_FLUID = "fluid" -fz_graph.NT_RECIPE = "recipe" +fz_graph.START_NODE_NAME = '__START__' +fz_graph.NT_TECH_HEAD = 'tech-h' +fz_graph.NT_TECH_TAIL = 'tech-t' +fz_graph.NT_ITEM = 'item' +fz_graph.NT_FLUID = 'fluid' +fz_graph.NT_RECIPE = 'recipe' -fz_graph.node.property_names = table.array_to_dictionary {"virtual", "factorio_name", "tech_name", "ignore_for_dependencies", "internal", "original_key"} +fz_graph.node.property_names = table.invert{'virtual', 'factorio_name', 'tech_name', 'ignore_for_dependencies', 'internal', 'original_key'} function fz_graph.node.create(name, type, properties) local n = {} @@ -60,7 +58,7 @@ end function fz_graph.node.get_key(name, type) - return type .. "|" .. name + return type .. '|' .. name end @@ -90,7 +88,7 @@ function fz_graph:copy() setmetatable(g, fz_graph) g.graph = self.graph:copy() - g.nodes = table.deep_copy(self.nodes) + g.nodes = table.deepcopy(self.nodes) g.start_node = g.nodes[self.start_node.key] return g @@ -103,7 +101,7 @@ function fz_graph:create_subgraph(node_list) local keys = table.map(node_list, function () return true end) g.graph = self.graph:create_subgraph(keys) - g.nodes = table.deep_copy(node_list) + g.nodes = table.deepcopy(node_list) g.start_node = g.nodes[self.start_node.name] return g @@ -138,7 +136,7 @@ end function fz_graph:add_link(from, to, label) - if not label or label == "" then error("Missing parameter: label") end + if not label or label == '' then error('Missing parameter: label') end if not self:link_exists(from, to, label) then self.graph:addEdge(from.key, to.key, 1, label) @@ -174,7 +172,7 @@ function fz_graph:recursive_remove(filter, logging) found = true if logging then - log(" - Removed from dependency graph: " .. key) + log(' - Removed from dependency graph: ' .. key) end end until not found @@ -214,7 +212,7 @@ function fz_graph:iter_links_from(node, label) function () repeat k, e = next(tab, k) - until (label or "") == "" or not k or e.label == label + until (label or '') == '' or not k or e.label == label return k, e end @@ -229,7 +227,7 @@ function fz_graph:iter_links_to(node, label) function () repeat k, e = next(tab, k) - until (label or "") == "" or not k or e.label == label + until (label or '') == '' or not k or e.label == label return k, e end @@ -237,12 +235,12 @@ end function fz_graph:has_label_from(node, label) - return table.any(self.graph:adj(node.key), function (e) return e.label and e.label ~= "" and (not label or e.label == label) end) + return table.any(self.graph:adj(node.key), function (e) return e.label and e.label ~= '' and (not label or e.label == label) end) end function fz_graph:has_label_to(node, label) - return table.any(self.graph:rev(node.key), function (e) return e.label and e.label ~= "" and (not label or e.label == label) end) + return table.any(self.graph:rev(node.key), function (e) return e.label and e.label ~= '' and (not label or e.label == label) end) end diff --git a/prototypes/functions/fz_topo_sort.lua b/prototypes/functions/fz_topo_sort.lua index 1ae6b3f..ac487c8 100644 --- a/prototypes/functions/fz_topo_sort.lua +++ b/prototypes/functions/fz_topo_sort.lua @@ -1,6 +1,5 @@ -local table = require "__stdlib__.stdlib.utils.table" -local queue = require "__stdlib__.stdlib.misc.queue" -local fz_lazy_bfs = require "prototypes.functions.search.fz_lazy_bfs" +local queue = require 'luagraphs.queue.queue' +local fz_lazy_bfs = require 'prototypes.functions.search.fz_lazy_bfs' local fz_topo = {} fz_topo.__index = fz_topo diff --git a/prototypes/functions/galdoc.lua b/prototypes/functions/galdoc.lua index 448132b..9fe0eca 100644 --- a/prototypes/functions/galdoc.lua +++ b/prototypes/functions/galdoc.lua @@ -1,22 +1,20 @@ -local FUN = require("__pycoalprocessing__/prototypes/functions/functions") - --zinc -FUN.global_item_replacer("ore-zinc", "zinc-ore") -FUN.global_item_replacer("zinc-plate", "zinc-plate-stock") +py.global_item_replacer("ore-zinc", "zinc-ore") +py.global_item_replacer("zinc-plate", "zinc-plate-stock") --lead -FUN.global_item_replacer("ore-lead","lead-ore") -FUN.global_item_replacer("lead-plate", "lead-plate-stock") +py.global_item_replacer("ore-lead","lead-ore") +py.global_item_replacer("lead-plate", "lead-plate-stock") TECHNOLOGY("gm-lead-stock-processing"):remove_pack("chemical-science-pack") TECHNOLOGY("gm-lead-machined-part-processing"):remove_pack("chemical-science-pack") --nickel -FUN.global_item_replacer("ore-nickel","nickel-ore") -FUN.global_item_replacer("nickel-plate", "nickel-plate-stock") +py.global_item_replacer("ore-nickel","nickel-ore") +py.global_item_replacer("nickel-plate", "nickel-plate-stock") --titanium -FUN.global_item_replacer("ore-titanium","titanium-ore") -FUN.global_item_replacer("titanium-plate", "titanium-plate-stock") +py.global_item_replacer("ore-titanium","titanium-ore") +py.global_item_replacer("titanium-plate", "titanium-plate-stock") --Shafts RECIPE("shaft-mk01"):remove_ingredient("solder"):add_ingredient({type = "item", name = "basic-shafting", amount = 2}) \ No newline at end of file diff --git a/prototypes/functions/search/fz_lazy_bfs.lua b/prototypes/functions/search/fz_lazy_bfs.lua index a2b87b4..922d037 100644 --- a/prototypes/functions/search/fz_lazy_bfs.lua +++ b/prototypes/functions/search/fz_lazy_bfs.lua @@ -1,5 +1,4 @@ -local table = require "__stdlib__.stdlib.utils.table" -local queue = require "__stdlib__.stdlib.misc.queue" +local queue = require 'luagraphs.queue.queue' local lazy_bfs = {} lazy_bfs.__index = lazy_bfs diff --git a/prototypes/functions/transitive_reduction.lua b/prototypes/functions/transitive_reduction.lua index 8bafa25..814fa95 100644 --- a/prototypes/functions/transitive_reduction.lua +++ b/prototypes/functions/transitive_reduction.lua @@ -1,5 +1,3 @@ -local table = require "__stdlib__.stdlib.utils.table" - local tr = {} tr.__index = tr diff --git a/prototypes/functions/utils.lua b/prototypes/functions/utils.lua deleted file mode 100644 index 4f57a6d..0000000 --- a/prototypes/functions/utils.lua +++ /dev/null @@ -1,103 +0,0 @@ -local table = require "__stdlib__.stdlib.utils.table" -local string = require "__stdlib__.stdlib.utils.string" -local config = require "__pypostprocessing__.prototypes.config" - -local defines = require "defines" - -local utils = {} - - -function utils.insert_double_lookup(tab, category, item) - if not tab[category] then - tab[category] = {} - end - - tab[category][item] = true -end - - -function utils.get_prototype(parent_type, prototype_name, no_error) - local prototype - - for t, _ in pairs(defines.prototypes[parent_type]) do - prototype = data.raw[t] and data.raw[t][prototype_name] - - if prototype then break end - end - - if not prototype and not no_error then - error('\n\nERROR: Prototype not found: ' .. parent_type .. ' / ' .. prototype_name .. '\n', 0) - end - - return prototype -end - - -function utils.iter_prototypes(parent_type) - local types = defines.prototypes[parent_type] - local t, n, d - - return - function () - repeat - if not t or not n then - n, d, t, _ = nil, nil, next(types, t) - end - - if t then - n, d = next(data.raw[t], n) - end - until n or not t - - return n, d - end -end - - -function utils.standardize_products(products, product, item_name, count) - local function standardize_product(p) - return { - type = p.type or 'item', - name = p.name or p[1], - amount = p.amount or p[2], - probability = p.probability, - amount_min = p.amount_min, - amount_max = p.amount_max, - catalyst_amount = p.catalyst_amount, - temperature = p.temperature, - min_temperature = p.minimum_temperature, - max_temperature = p.maximum_temperature - } - end - - if not products then - products = { product or { item_name, (count or 1) } } - end - - local results = {} - - for _, p in pairs(products) do - table.insert(results, standardize_product(p)) - end - - return results -end - - -function utils.is_py_or_base_tech(tech) - local icons = tech.icons - if not icons then icons = {{ icon = tech.icon }} end - - for _, icon in pairs(icons or {}) do - local mod = icon and table.first(string.split(icon.icon, '/')) - - if not config.PY_GRAPHICS_MODS:contains(mod) and mod ~= '__base__' and mod ~= '__core__' then - return false - end - end - - return true -end - - -return utils \ No newline at end of file diff --git a/prototypes/yafc.lua b/prototypes/yafc.lua index 6810b07..b81b9d9 100644 --- a/prototypes/yafc.lua +++ b/prototypes/yafc.lua @@ -16,19 +16,19 @@ if mods['pyalienlife'] then -- List of all available smartfarming local farms = { - require('__pyalienlife__/scripts/smart-farm/farm-ralesia'), - require('__pyalienlife__/scripts/smart-farm/farm-rennea'), - require('__pyalienlife__/scripts/smart-farm/farm-tuuphra'), - require('__pyalienlife__/scripts/smart-farm/farm-grod'), - require('__pyalienlife__/scripts/smart-farm/farm-yotoi'), - require('__pyalienlife__/scripts/smart-farm/farm-kicalk'), - require('__pyalienlife__/scripts/smart-farm/farm-arum'), - require('__pyalienlife__/scripts/smart-farm/farm-yotoi-fruit'), - use_bioreserve_copy(require('__pyalienlife__/scripts/smart-farm/farm-bioreserve')) + require '__pyalienlife__/scripts/smart-farm/farm-ralesia', + require '__pyalienlife__/scripts/smart-farm/farm-rennea', + require '__pyalienlife__/scripts/smart-farm/farm-tuuphra', + require '__pyalienlife__/scripts/smart-farm/farm-grod', + require '__pyalienlife__/scripts/smart-farm/farm-yotoi', + require '__pyalienlife__/scripts/smart-farm/farm-kicalk', + require '__pyalienlife__/scripts/smart-farm/farm-arum', + require '__pyalienlife__/scripts/smart-farm/farm-yotoi-fruit', + use_bioreserve_copy(require '__pyalienlife__/scripts/smart-farm/farm-bioreserve') } if mods['pyalternativeenergy'] then - farms[#farms+1] = require('__pyalternativeenergy__/scripts/crops/farm-mova') + farms[#farms+1] = require '__pyalternativeenergy__/scripts/crops/farm-mova' end for _, farm in ipairs(farms) do From add8f985f65c7846579ac2c496d32cfac674b96a Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Sat, 25 May 2024 20:05:28 -0500 Subject: [PATCH 02/13] Add new library files to drop-in replace stdlib --- lib/autorecipes.lua | 222 +++++++++++++++++ lib/color.lua | 45 ++++ lib/control-stage.lua | 151 ++++++++++++ lib/data-stage.lua | 442 ++++++++++++++++++++++++++++++++++ lib/defines.lua | 171 +++++++++++++ lib/events.lua | 147 +++++++++++ lib/lib.lua | 20 ++ lib/metas/entity.lua | 73 ++++++ lib/metas/fluid.lua | 18 ++ lib/metas/item.lua | 47 ++++ lib/metas/metas.lua | 77 ++++++ lib/metas/recipe.lua | 220 +++++++++++++++++ lib/metas/technology.lua | 98 ++++++++ lib/metas/tile.lua | 18 ++ lib/placement-restriction.lua | 68 ++++++ lib/string.lua | 28 +++ lib/table.lua | 197 +++++++++++++++ lib/world-generation.lua | 47 ++++ luagraphs/queue/queue.lua | 352 +++++++++++++++++++++++++++ 19 files changed, 2441 insertions(+) create mode 100644 lib/autorecipes.lua create mode 100644 lib/color.lua create mode 100644 lib/control-stage.lua create mode 100644 lib/data-stage.lua create mode 100644 lib/defines.lua create mode 100644 lib/events.lua create mode 100644 lib/lib.lua create mode 100644 lib/metas/entity.lua create mode 100644 lib/metas/fluid.lua create mode 100644 lib/metas/item.lua create mode 100644 lib/metas/metas.lua create mode 100644 lib/metas/recipe.lua create mode 100644 lib/metas/technology.lua create mode 100644 lib/metas/tile.lua create mode 100644 lib/placement-restriction.lua create mode 100644 lib/string.lua create mode 100644 lib/table.lua create mode 100644 lib/world-generation.lua create mode 100644 luagraphs/queue/queue.lua diff --git a/lib/autorecipes.lua b/lib/autorecipes.lua new file mode 100644 index 0000000..a7ee290 --- /dev/null +++ b/lib/autorecipes.lua @@ -0,0 +1,222 @@ +local function ensure_contiguous(tbl) + if not tbl or type(tbl) ~= 'table' then return tbl end + local contiguous_table = {} + for _, v in pairs(tbl) do + if v ~= nil then + contiguous_table[#contiguous_table+1] = v + end + end + return contiguous_table +end + +local function modify_recipe_tables(item, items_table, previous_item_names, result_table) -- TODO: this is spaghetti. needs a refactor + local barrel + if string.match(item.name, '%-barrel') and string.match(item.name, 'empty-barrel') == nil or string.match(item.name, 'empty-milk-barrel') then + barrel = string.gsub(item.name, '%-barrel', '') + end + + local name + if data.raw.item[item.name] or data.raw.module[item.name] or data.raw.fluid[item.name] then + name = item.name + elseif type(item.fallback) == 'string' then + name = item.fallback + item.name = name + elseif type(item.fallback) == 'table' and item.fallback.name then + name = item.fallback.name + item.name = name + if item.fallback.amount then + item.amount = item.fallback.amount + end + elseif data.raw.fluid[barrel] then + name = item.name + end + + if previous_item_names[name] ~= true then + local item_type + if data.raw.item[name] or data.raw.module[name] then + item_type = 'item' + elseif data.raw.fluid[name] then + item_type = 'fluid' + end + item.type = item_type + + if item.amount and type(item.amount) == 'number' then + table.insert(items_table, item) + elseif item.amount_min and item.amount_max then + table.insert(items_table, item) + end + elseif previous_item_names[name] == true then + if item.remove_item and item.remove_item == true then + for p, pre in pairs(items_table) do + if pre.name == name then + if string.match(item.name, '%-barrel') and string.match(item.name, 'empty%-barrel') == nil or string.match(item.name, 'empty%-milk%-barrel') == nil then + local barrel_name + if string.match(item.name, 'barrel') then + barrel_name = 'empty-barrel' + elseif string.match(item.name, 'canister') then + barrel_name = 'empty-fuel-canister' + end + local amount = items_table[p].amount + if result_table and next(result_table) then + if result_table.no_returns == nil or result_table.no_returns and result_table.no_returns[item.name] ~= true then + for _, result in pairs(result_table) do + if result.name == barrel_name then + result.amount = result.amount - amount + end + end + end + end + end + items_table[p] = nil + previous_item_names[name] = nil + end + end + elseif item.amount == nil then + if item.add_amount then + for _, pre in pairs(items_table) do + if pre.name == name then + if pre.amount then + pre.amount = item.add_amount + pre.amount + elseif pre.amount_min and pre.amount_max then + pre.amount_min = pre.amount_min + item.add_amount + pre.amount_max = pre.amount_max + item.add_amount + end + end + end + elseif item.amount_min and item.amount_max then + for _, pre in pairs(items_table) do + if pre.name == name then + pre.amount_min = item.amount_min + pre.amount_max = item.amount_max + end + end + elseif item.subtract_amount then + for _, pre in pairs(items_table) do + if pre.name == name then + pre.amount = pre.amount - item.subtract_amount + end + end + end + elseif item.amount then + for _, pre in pairs(items_table) do + if pre.name == name then + pre.amount = item.amount + end + end + end + end + + --add return item to results if it exists + local return_item + if item.return_item then + local item_type + local name = item.return_item.name + local amount = item.return_item.amount or item.amount or item.add_amount + if data.raw.item[name] or data.raw.module[name] then + item_type = 'item' + elseif data.raw.fluid[name] then + item_type = 'fluid' + end + return_item = {type = item_type, name = name, amount = amount} + table.insert(result_table, return_item) + end + + local return_barrel + if item.return_barrel and item.return_barrel == true then + local item_type = 'item' + local name + if string.match(item.name, 'barrel') then + name = 'empty-barrel' + elseif string.match(item.name, 'canister') then + name = 'empty-fuel-canister' + end + local amount = item.amount or item.add_amount + return_barrel = {type = item_type, name = name, amount = amount} + if next(result_table) then + local has_barrel = false + for _, result in pairs(result_table) do + if result.name == name then + result.amount = result.amount + amount + has_barrel = true + end + end + if has_barrel == false then + table.insert(result_table, return_barrel) + end + else + table.insert(result_table, return_barrel) + end + end +end + +--handles all adjustments for each ingredient and result changes in autorecipe +local function recipe_item_builder(ingredients,results,previous_ingredients,previous_results) + local ing_table = table.deepcopy(previous_ingredients) + local result_table = table.deepcopy(previous_results) + + local previous_ingredient_names = {} + for _, pre in pairs(previous_ingredients) do + previous_ingredient_names[pre.name] = true + end + + local previous_result_names = {} + for _, pre in pairs(previous_results) do + previous_result_names[pre.name] = true + end + + for _, ing in pairs(ingredients) do + modify_recipe_tables(ing, ing_table, previous_ingredient_names, result_table) + end + + for _, result in pairs(results) do + modify_recipe_tables(result, result_table, previous_result_names) + end + + return ensure_contiguous(ing_table), ensure_contiguous(result_table) +end + +---Provides an interface to quickly build tiered recipes. See recipes-auto-brains.lua for an example +---@param params table +py.autorecipes = function(params) + local previous_ingredients = {} + local previous_results = {} + + for _, tier in pairs(params.mats) do + local fixed_ingredients, fixed_results = recipe_item_builder(tier.ingredients, tier.results, previous_ingredients, previous_results) + previous_ingredients = fixed_ingredients + previous_results = fixed_results + + local i, numbered_name = 0, nil + repeat + i = i + 1 + numbered_name = params.name .. '-' .. i + until not data.raw.recipe[numbered_name] + + local recipe_name = tier.name or numbered_name + + local recipe = RECIPE { + type = 'recipe', + name = recipe_name, + category = params.category, + enabled = tier.tech == nil, + energy_required = tier.crafting_speed or params.crafting_speed, + ingredients = fixed_ingredients, + results = fixed_results, + subgroup = params.subgroup, + order = params.order, + allowed_module_categories = params.allowed_module_categories, + icons = tier.icons, + main_product = tier.main_product or params.main_product + } + if tier.tech then recipe:add_unlock(tier.tech) end + + if tier.icon then + data.raw.recipe[recipe_name].icon = tier.icon + if tier.icon_size then + data.raw.recipe[recipe_name].icon_size = tier.icon_size + else + data.raw.recipe[recipe_name].icon_size = 32 + end + end + end +end \ No newline at end of file diff --git a/lib/color.lua b/lib/color.lua new file mode 100644 index 0000000..7e40d4e --- /dev/null +++ b/lib/color.lua @@ -0,0 +1,45 @@ +py.tints = { + {r = 1.0, g = 1.0, b = 0.0, a = 1.0}, + {r = 1.0, g = 0.0, b = 0.0, a = 1.0}, + {r = 0.223, g = 0.490, b = 0.858, a = 1.0}, + {r = 1.0, g = 0.0, b = 1.0, a = 1.0} +} + +py.light_tints = {} +for i, tint in pairs(py.tints) do + py.light_tints[i] = {} + for color, amount in pairs(tint) do + py.light_tints[i][color] = (amount - 0.5) / 2 + 0.5 + end + py.light_tints[i].a = 1 +end + +---@param color Color +---@return Color +function py.color_normalize(color) + local r = color.r or color[1] + local g = color.g or color[2] + local b = color.b or color[3] + local a = color.a or color[4] or 1 + if r > 1 then r = r / 255 end + if g > 1 then g = g / 255 end + if b > 1 then b = b / 255 end + if a > 1 then a = a / 255 end + return {r = r, g = g, b = b, a = a} +end + +---@param a Color +---@param b Color +---@param percent number +---@return Color +function py.color_combine(a, b, percent) + a = py.color_normalize(a) + b = py.color_normalize(b) + + return { + r = a.r * percent + b.r * (1 - percent), + g = a.g * percent + b.g * (1 - percent), + b = a.b * percent + b.b * (1 - percent), + a = a.a * percent + b.a * (1 - percent) + } +end \ No newline at end of file diff --git a/lib/control-stage.lua b/lib/control-stage.lua new file mode 100644 index 0000000..6b712bc --- /dev/null +++ b/lib/control-stage.lua @@ -0,0 +1,151 @@ +-- Adds helper functions for control stage. Shared across all pymods + +local random = math.random + +---Draws a red error icon at the entity's position. +---@param entity LuaEntity +---@param sprite string +---@param time_to_live integer +py.draw_error_sprite = function(entity, sprite, time_to_live) + rendering.draw_sprite{ + sprite = sprite, + x_scale = 0.5, + y_scale = 0.5, + target = entity, + surface = entity.surface, + time_to_live = time_to_live or 30, + render_layer = 'air-entity-info-icon' + } +end + +---Creates a localised string tooltip for allowed modules. +---@param allowed_modules table +---@return LocalisedString +py.generate_allowed_module_tooltip = function(allowed_modules) + local item_prototypes = game.item_prototypes + ---@type LocalisedString + local result = {'', {'gui.module-description'}, '\n'} + for module, _ in pairs(allowed_modules) do + result[#result + 1] = {'', '[font=heading-2][item=' .. module .. '][/font]', ' ', item_prototypes[module].localised_name} + result[#result + 1] = '\n' + end + result[#result] = nil + return result +end + +---Randomizes a position by a factor. +---@param position Position +---@param factor number? +---@return Position +py.randomize_position = function(position, factor) + local x = position.x or position[1] + local y = position.y or position[2] + factor = factor or 1 + return {x = x + factor * (random() - 0.5), y = y + factor * (random() - 0.5)} +end + +---Intended to be called inside a build event. Cancels creation of the entity and returns its item_to_place +---@param entity LuaEntity +---@param player_index integer? +---@param message string? +---@param color Color? +py.cancel_creation = function(entity, player_index, message, color) + local inserted = 0 + local item_to_place = entity.prototype.items_to_place_this[1] + local surface = entity.surface + local position = entity.position + + if player_index then + local player = game.get_player(player_index) + if player.mine_entity(entity, false) then + inserted = 1 + elseif item_to_place then + inserted = player.insert(item_to_place) + end + end + + if inserted == 0 and item_to_place then + surface.spill_item_stack( + position, + item_to_place, + true, + entity.force_index, + false + ) + end + + entity.destroy{raise_destroy = true} + + if not message then return end + + local tick = game.tick + local last_message = global._last_cancel_creation_message or 0 + if last_message + 60 < tick then + surface.create_entity{ + name = 'flying-text', + position = position, + text = message, + render_player_index = player_index, + color = color + } + global._last_cancel_creation_message = game.tick + end +end + +local seconds_per_year = 60 * 60 * 24 * 365.25 +local seconds_per_day = 60 * 60 * 24 +local seconds_per_hour = 60 * 60 +local seconds_per_minute = 60 +---Creates a string representation of a time in seconds. +---@param seconds number? +---@return string? +py.format_large_time = function(seconds) + if not seconds then return end + local result = '' + if seconds >= seconds_per_year then + local years = math.floor(seconds / seconds_per_year) + result = result .. years .. 'y ' + seconds = seconds % seconds_per_year + end + + if seconds >= seconds_per_day or result ~= '' then + local days = math.floor(seconds / seconds_per_day) + result = result .. days .. 'd ' + seconds = seconds % seconds_per_day + end + + if seconds >= seconds_per_hour or result ~= '' then + local hours = math.floor(seconds / seconds_per_hour) + result = result .. hours .. ':' + seconds = seconds % seconds_per_hour + end + + local minutes = math.floor(seconds / seconds_per_minute) + if minutes < 10 then + result = result .. '0' .. minutes .. ':' + else + result = result .. minutes .. ':' + end + seconds = seconds % seconds_per_minute + + seconds = math.ceil(seconds) + if seconds < 10 then + result = result .. '0' .. seconds + else + result = result .. seconds + end + + return result +end + +---Returns the grandparent gui element with the given name. +---@param element LuaGuiElement +---@param name string +---@return LuaGuiElement +py.find_grandparent = function(element, name) + while element do + if element.name == name then return element end + element = element.parent + end + error('Could not find parent gui element with name: ' .. name) +end \ No newline at end of file diff --git a/lib/data-stage.lua b/lib/data-stage.lua new file mode 100644 index 0000000..4b5a621 --- /dev/null +++ b/lib/data-stage.lua @@ -0,0 +1,442 @@ +-- Adds helper functions for data stage. Shared across all pymods + +require 'metas.metas' +require 'autorecipes' + +---Returns a 1x1 empty image. +---@return table +py.empty_image = function() + return { + filename = '__pystellarexpeditiongraphics__/graphics/empty.png', + size = 1, + priority = 'high', + direction_count = 1, + frame_count = 1, + line_length = 1 + } +end + +---Adds a localised string to the prototype's description. +---@param type string +---@param prototype data.AnyPrototype +---@param localised_string LocalisedString +py.add_to_description = function(type, prototype, localised_string) + if prototype.localised_description and prototype.localised_description ~= '' then + prototype.localised_description = {'', prototype.localised_description, '\n', localised_string} + return + end + + local place_result = prototype.place_result or prototype.placed_as_equipment_result + if type == 'item' and place_result then + for _, machine in pairs(data.raw) do + machine = machine[place_result] + if machine and machine.localised_description then + prototype.localised_description = { + '?', + {'', machine.localised_description, '\n', localised_string}, + localised_string + } + return + end + end + + local entity_type = prototype.place_result and 'entity' or 'equipment' + prototype.localised_description = { + '?', + {'', {entity_type .. '-description.' .. place_result}, '\n', localised_string}, + {'', {type .. '-description.' .. prototype.name}, '\n', localised_string}, + localised_string + } + else + prototype.localised_description = { + '?', + {'', {type .. '-description.' .. prototype.name}, '\n', localised_string}, + localised_string + } + end +end + +---adds a glow layer to any prototype with the 'icons' field. +---@param prototype data.AnyPrototype +py.make_item_glowing = function(prototype) + if not prototype then + error('No prototype provided') + end + if prototype.pictures then + for _, picture in pairs(prototype.pictures) do + picture.draw_as_glow = true + end + return + end + if prototype.icon and not prototype.icons then + prototype.icons = {{icon = prototype.icon, icon_size = prototype.icon_size, icon_mipmaps = prototype.icon_mipmaps}} + prototype.icon = nil + end + if not prototype.icons then + error('No icon found for ' .. prototype.name) + end + local pictures = {} + for _, picture in pairs(table.deepcopy(prototype.icons)) do + picture.draw_as_glow = true + local icon_size = picture.icon_size or prototype.icon_size + picture.filename = picture.icon + picture.shift = {0, 0} + picture.width = icon_size + picture.height = icon_size + picture.scale = 16 / icon_size + picture.icon = nil + picture.icon_size = nil + picture.icon_mipmaps = nil + pictures[#pictures + 1] = picture + end + prototype.pictures = pictures +end + +---Creates a new prototype by cloning 'old' and overwriting it with properties from 'new'. Provide 'nil' as a string in order to delete items inside 'old' +---@param old data.AnyPrototype +---@param new table +---@return data.AnyPrototype +py.merge = function(old, new) + old = table.deepcopy(old) + for k, v in pairs(new) do + if v == 'nil' then + old[k] = nil + else + old[k] = v + end + end + return old +end + +---The purpose of the farm_speed functions is to remove the farm building itself +---from the building speed. For example, for xyhiphoe mk1 which has only one animal +---per farm, we want the speed to be equal to 1 xyhiphoe not 2 (farm + module) +---Returns the correct farm speed for a mk1 farm based on number of modules and desired speed using mk1 modules +---@param num_slots integer +---@param desired_speed number +---@return number +function py.farm_speed(num_slots, desired_speed) + -- mk1 modules are 100% bonus speed. The farm itself then counts as much as one module + return desired_speed / (num_slots + 1) +end + +---Returns the correct farm speed for a mk2+ farm based on the number of modules and the mk1 speed +---@param num_slots integer +---@param base_entity_name string +---@return number +function py.farm_speed_derived(num_slots, base_entity_name) + local e = data.raw['assembling-machine'][base_entity_name] + local mk1_slots = e.module_specification.module_slots + local desired_mk1_speed = e.crafting_speed * (mk1_slots + 1) + local speed_improvement_ratio = num_slots / mk1_slots + return (desired_mk1_speed * speed_improvement_ratio) / (num_slots + 1/speed_improvement_ratio) +end + +---Takes two prototype names (both must use the style of IconSpecification with icon = string_path), returns an IconSpecification with the icons as composites +---@param base_prototype string +---@param child_prototype string +---@param shadow_alpha number? +function py.composite_molten_icon(base_prototype, child_prototype, shadow_alpha) + shadow_alpha = shadow_alpha or 0.6 + base_prototype = data.raw.fluid[base_prototype] or data.raw.item[base_prototype] + child_prototype = data.raw.fluid[child_prototype] or data.raw.item[child_prototype] + return { + { + icon = base_prototype.icon, + icon_size = base_prototype.icon_size, + icon_mipmaps = base_prototype.icon_mipmaps + }, + { + icon = child_prototype.icon, + icon_size = child_prototype.icon_size, + icon_mipmaps = base_prototype.icon_mipmaps, + shift = {10, 10}, + scale = 0.65, + tint = {r = 0, g = 0, b = 0, a = shadow_alpha} + }, + { + icon = child_prototype.icon, + icon_size = child_prototype.icon_size, + icon_mipmaps = base_prototype.icon_mipmaps, + shift = {10, 10}, + scale = 0.5, + tint = {r = 1, g = 1, b = 1, a = 1} + }, + } +end + +---Define pipe connection pipe pictures, not all entities use these. +---@param pictures string +---@param shift_north table? +---@param shift_south table? +---@param shift_west table? +---@param shift_east table? +---@param replacements table? +---@return table +py.pipe_pictures = function(pictures, shift_north, shift_south, shift_west, shift_east, replacements) + local new_pictures = { + north = shift_north and + { + filename = '__base__/graphics/entity/' .. pictures .. '/' .. pictures .. '-pipe-N.png', + priority = 'extra-high', + width = 35, + height = 18, + shift = shift_north + } or + py.empty_image(), + south = shift_south and + { + filename = '__base__/graphics/entity/' .. pictures .. '/' .. pictures .. '-pipe-S.png', + priority = 'extra-high', + width = 44, + height = 31, + shift = shift_south + } or + py.empty_image(), + west = shift_west and + { + filename = '__base__/graphics/entity/' .. pictures .. '/' .. pictures .. '-pipe-W.png', + priority = 'extra-high', + width = 19, + height = 37, + shift = shift_west + } or + py.empty_image(), + east = shift_east and + { + filename = '__base__/graphics/entity/' .. pictures .. '/' .. pictures .. '-pipe-E.png', + priority = 'extra-high', + width = 20, + height = 38, + shift = shift_east + } or + py.empty_image() + } + for direction, image in pairs(replacements or {}) do + if not (new_pictures[direction].filename == '__core__/graphics/empty.png') then + new_pictures[direction].filename = image.filename + new_pictures[direction].width = image.width + new_pictures[direction].height = image.height + new_pictures[direction].priority = image.priority or new_pictures[direction].priority + end + end + return new_pictures +end + +---Define pipe connection pipe covers, not all entities use these. +---@param n boolean? +---@param s boolean? +---@param w boolean? +---@param e boolean? +---@return table +py.pipe_covers = function(n, s, w, e) + if (n == nil and s == nil and w == nil and e == nil) then + n, s, e, w = true, true, true, true + end + + n = + n and + { + layers = { + { + filename = '__base__/graphics/entity/pipe-covers/pipe-cover-north.png', + priority = 'extra-high', + width = 64, + height = 64, + hr_version = { + filename = '__base__/graphics/entity/pipe-covers/hr-pipe-cover-north.png', + priority = 'extra-high', + width = 128, + height = 128, + scale = 0.5 + } + }, + { + filename = '__base__/graphics/entity/pipe-covers/pipe-cover-north-shadow.png', + priority = 'extra-high', + width = 64, + height = 64, + draw_as_shadow = true, + hr_version = { + filename = '__base__/graphics/entity/pipe-covers/hr-pipe-cover-north-shadow.png', + priority = 'extra-high', + width = 128, + height = 128, + scale = 0.5, + draw_as_shadow = true + } + } + } + } or + py.empty_image() + e = + e and + { + layers = { + { + filename = '__base__/graphics/entity/pipe-covers/pipe-cover-east.png', + priority = 'extra-high', + width = 64, + height = 64, + hr_version = { + filename = '__base__/graphics/entity/pipe-covers/hr-pipe-cover-east.png', + priority = 'extra-high', + width = 128, + height = 128, + scale = 0.5 + } + }, + { + filename = '__base__/graphics/entity/pipe-covers/pipe-cover-east-shadow.png', + priority = 'extra-high', + width = 64, + height = 64, + draw_as_shadow = true, + hr_version = { + filename = '__base__/graphics/entity/pipe-covers/hr-pipe-cover-east-shadow.png', + priority = 'extra-high', + width = 128, + height = 128, + scale = 0.5, + draw_as_shadow = true + } + } + } + } or + py.empty_image() + s = + s and + { + layers = { + { + filename = '__base__/graphics/entity/pipe-covers/pipe-cover-south.png', + priority = 'extra-high', + width = 64, + height = 64, + hr_version = { + filename = '__base__/graphics/entity/pipe-covers/hr-pipe-cover-south.png', + priority = 'extra-high', + width = 128, + height = 128, + scale = 0.5 + } + }, + { + filename = '__base__/graphics/entity/pipe-covers/pipe-cover-south-shadow.png', + priority = 'extra-high', + width = 64, + height = 64, + draw_as_shadow = true, + hr_version = { + filename = '__base__/graphics/entity/pipe-covers/hr-pipe-cover-south-shadow.png', + priority = 'extra-high', + width = 128, + height = 128, + scale = 0.5, + draw_as_shadow = true + } + } + } + } or + py.empty_image() + w = + w and + { + layers = { + { + filename = '__base__/graphics/entity/pipe-covers/pipe-cover-west.png', + priority = 'extra-high', + width = 64, + height = 64, + hr_version = { + filename = '__base__/graphics/entity/pipe-covers/hr-pipe-cover-west.png', + priority = 'extra-high', + width = 128, + height = 128, + scale = 0.5 + } + }, + { + filename = '__base__/graphics/entity/pipe-covers/pipe-cover-west-shadow.png', + priority = 'extra-high', + width = 64, + height = 64, + draw_as_shadow = true, + hr_version = { + filename = '__base__/graphics/entity/pipe-covers/hr-pipe-cover-west-shadow.png', + priority = 'extra-high', + width = 128, + height = 128, + scale = 0.5, + draw_as_shadow = true + } + } + } + } or + py.empty_image() + + return { north = n, south = s, east = e, west = w } +end + +---Standardizes a product or ingredient prototype to a common format. +---@param p data.IngredientPrototype | data.ProductPrototype +---@return data.IngredientPrototype | data.ProductPrototype +py.standardize_product = function(p) + return { + type = p.type or 'item', + name = p.name or p[1], + amount = p.amount or p[2], + probability = p.probability, + amount_min = p.amount_min, + amount_max = p.amount_max, + catalyst_amount = p.catalyst_amount, + temperature = p.temperature, + min_temperature = p.minimum_temperature, + max_temperature = p.maximum_temperature + } +end + +---Returns an iterator through all prototypes of a given supertype. +---@param parent_type string +---@return function +function py.iter_prototypes(parent_type) + local types = defines.prototypes[parent_type] + local t, n, d + + return function () + repeat + if not t or not n then + n, d, t, _ = nil, nil, next(types, t) + end + + if t then + n, d = next(data.raw[t], n) + end + until n or not t + + return n, d + end +end + +---replace an item/fluid in every recipes ingredients/results +---best used to merge items that are duplicated in mods that should be the same +---@param old string +---@param new string +---@param blackrecipe (string | table)? +py.global_item_replacer = function(old, new, blackrecipe) + if not data.raw.item[old] and not data.raw.fluid[old] then error('Could not find item or fluid ' .. old) end + if not data.raw.item[new] and not data.raw.fluid[new] then error('Could not find item or fluid ' .. new) end + + if type(blackrecipe) == 'string' then blackrecipe = {blackrecipe} end + blackrecipe = table.invert(blackrecipe or {}) + + for _, recipe in pairs(data.raw.recipe) do + if not recipe.ignored_by_recipe_replacement and not blackrecipe[recipe.name] then + recipe:replace_ingredient(old, new) + recipe:replace_result(old, new) + end + end +end + +---@diagnostic disable-next-line: duplicate-set-field +py.on_event = function() end \ No newline at end of file diff --git a/lib/defines.lua b/lib/defines.lua new file mode 100644 index 0000000..aafe8eb --- /dev/null +++ b/lib/defines.lua @@ -0,0 +1,171 @@ +defines.gravitational_constant = 6.67408e-11 -- m^3 kg^-1 s^-2 + +defines.opposite_direction = { + [defines.direction.north] = defines.direction.south, + [defines.direction.northeast] = defines.direction.southwest, + [defines.direction.east] = defines.direction.west, + [defines.direction.southeast] = defines.direction.northwest, + [defines.direction.south] = defines.direction.north, + [defines.direction.southwest] = defines.direction.northeast, + [defines.direction.west] = defines.direction.east, + [defines.direction.northwest] = defines.direction.southeast +} + +defines.color = { + darkgray = { r = 0.6627, g = 0.6627, b = 0.6627, a = 1 }, + aliceblue = { r = 0.9412, g = 0.9725, b = 1.0000, a = 1 }, + antiquewhite = { r = 0.9804, g = 0.9216, b = 0.8431, a = 1 }, + aqua = { r = 0.0000, g = 1.0000, b = 1.0000, a = 1 }, + aquamarine = { r = 0.4980, g = 1.0000, b = 0.8314, a = 1 }, + azure = { r = 0.9412, g = 1.0000, b = 1.0000, a = 1 }, + beige = { r = 0.9608, g = 0.9608, b = 0.8627, a = 1 }, + bisque = { r = 1.0000, g = 0.8941, b = 0.7686, a = 1 }, + black = { r = 0.0000, g = 0.0000, b = 0.0000, a = 1 }, + blanchedalmond = { r = 1.0000, g = 0.9216, b = 0.8039, a = 1 }, + blue = { r = 0.0000, g = 0.0000, b = 1.0000, a = 1 }, + blueviolet = { r = 0.5412, g = 0.1686, b = 0.8863, a = 1 }, + brown = { r = 0.6471, g = 0.1647, b = 0.1647, a = 1 }, + burlywood = { r = 0.8706, g = 0.7216, b = 0.5294, a = 1 }, + cadetblue = { r = 0.3725, g = 0.6196, b = 0.6275, a = 1 }, + chartreuse = { r = 0.4980, g = 1.0000, b = 0.0000, a = 1 }, + chocolate = { r = 0.8235, g = 0.4118, b = 0.1176, a = 1 }, + coral = { r = 1.0000, g = 0.4980, b = 0.3137, a = 1 }, + cornflowerblue = { r = 0.3922, g = 0.5843, b = 0.9294, a = 1 }, + cornsilk = { r = 1.0000, g = 0.9725, b = 0.8627, a = 1 }, + crimson = { r = 0.8627, g = 0.0784, b = 0.2353, a = 1 }, + cyan = { r = 0.0000, g = 1.0000, b = 1.0000, a = 1 }, + darkblue = { r = 0.0000, g = 0.0000, b = 0.5451, a = 1 }, + darkcyan = { r = 0.0000, g = 0.5451, b = 0.5451, a = 1 }, + darkgoldenrod = { r = 0.7216, g = 0.5255, b = 0.0431, a = 1 }, + darkgreen = { r = 0.0000, g = 0.3922, b = 0.0000, a = 1 }, + darkgrey = { r = 0.6627, g = 0.6627, b = 0.6627, a = 1 }, + darkkhaki = { r = 0.7412, g = 0.7176, b = 0.4196, a = 1 }, + darkmagenta = { r = 0.5451, g = 0.0000, b = 0.5451, a = 1 }, + darkolivegreen = { r = 0.3333, g = 0.4196, b = 0.1843, a = 1 }, + darkorange = { r = 1.0000, g = 0.5490, b = 0.0000, a = 1 }, + darkorchid = { r = 0.6000, g = 0.1961, b = 0.8000, a = 1 }, + darkred = { r = 0.5451, g = 0.0000, b = 0.0000, a = 1 }, + darksalmon = { r = 0.9137, g = 0.5882, b = 0.4784, a = 1 }, + darkseagreen = { r = 0.5608, g = 0.7373, b = 0.5608, a = 1 }, + darkslateblue = { r = 0.2824, g = 0.2392, b = 0.5451, a = 1 }, + darkslategray = { r = 0.1843, g = 0.3098, b = 0.3098, a = 1 }, + darkslategrey = { r = 0.1843, g = 0.3098, b = 0.3098, a = 1 }, + darkturquoise = { r = 0.0000, g = 0.8078, b = 0.8196, a = 1 }, + darkviolet = { r = 0.5804, g = 0.0000, b = 0.8275, a = 1 }, + deeppink = { r = 1.0000, g = 0.0784, b = 0.5765, a = 1 }, + deepskyblue = { r = 0.0000, g = 0.7490, b = 1.0000, a = 1 }, + dimgray = { r = 0.4118, g = 0.4118, b = 0.4118, a = 1 }, + dimgrey = { r = 0.4118, g = 0.4118, b = 0.4118, a = 1 }, + dodgerblue = { r = 0.1176, g = 0.5647, b = 1.0000, a = 1 }, + firebrick = { r = 0.6980, g = 0.1333, b = 0.1333, a = 1 }, + floralwhite = { r = 1.0000, g = 0.9804, b = 0.9412, a = 1 }, + forestgreen = { r = 0.1333, g = 0.5451, b = 0.1333, a = 1 }, + fuchsia = { r = 1.0000, g = 0.0000, b = 1.0000, a = 1 }, + gainsboro = { r = 0.8627, g = 0.8627, b = 0.8627, a = 1 }, + ghostwhite = { r = 0.9725, g = 0.9725, b = 1.0000, a = 1 }, + gold = { r = 1.0000, g = 0.8431, b = 0.0000, a = 1 }, + goldenrod = { r = 0.8549, g = 0.6471, b = 0.1255, a = 1 }, + gray = { r = 0.5020, g = 0.5020, b = 0.5020, a = 1 }, + green = { r = 0.0000, g = 1.0000, b = 0.0000, a = 1 }, + greenyellow = { r = 0.6784, g = 1.0000, b = 0.1843, a = 1 }, + grey = { r = 0.5020, g = 0.5020, b = 0.5020, a = 1 }, + honeydew = { r = 0.9412, g = 1.0000, b = 0.9412, a = 1 }, + hotpink = { r = 1.0000, g = 0.4118, b = 0.7059, a = 1 }, + indianred = { r = 0.8039, g = 0.3608, b = 0.3608, a = 1 }, + indigo = { r = 0.2941, g = 0.0000, b = 0.5098, a = 1 }, + ivory = { r = 1.0000, g = 1.0000, b = 0.9412, a = 1 }, + khaki = { r = 0.9412, g = 0.9020, b = 0.5490, a = 1 }, + lavender = { r = 0.9020, g = 0.9020, b = 0.9804, a = 1 }, + lavenderblush = { r = 1.0000, g = 0.9412, b = 0.9608, a = 1 }, + lawngreen = { r = 0.4863, g = 0.9882, b = 0.0000, a = 1 }, + lemonchiffon = { r = 1.0000, g = 0.9804, b = 0.8039, a = 1 }, + lightblue = { r = 0.6784, g = 0.8471, b = 0.9020, a = 1 }, + lightcoral = { r = 0.9412, g = 0.5020, b = 0.5020, a = 1 }, + lightcyan = { r = 0.8784, g = 1.0000, b = 1.0000, a = 1 }, + lightgoldenrodyellow = { r = 0.9804, g = 0.9804, b = 0.8235, a = 1 }, + lightgray = { r = 0.8275, g = 0.8275, b = 0.8275, a = 1 }, + lightgreen = { r = 0.5647, g = 0.9333, b = 0.5647, a = 1 }, + lightgrey = { r = 0.8275, g = 0.8275, b = 0.8275, a = 1 }, + lightpink = { r = 1.0000, g = 0.7137, b = 0.7569, a = 1 }, + lightsalmon = { r = 1.0000, g = 0.6275, b = 0.4784, a = 1 }, + lightseagreen = { r = 0.1255, g = 0.6980, b = 0.6667, a = 1 }, + lightskyblue = { r = 0.5294, g = 0.8078, b = 0.9804, a = 1 }, + lightslategray = { r = 0.4667, g = 0.5333, b = 0.6000, a = 1 }, + lightslategrey = { r = 0.4667, g = 0.5333, b = 0.6000, a = 1 }, + lightsteelblue = { r = 0.6902, g = 0.7686, b = 0.8706, a = 1 }, + lightyellow = { r = 1.0000, g = 1.0000, b = 0.8784, a = 1 }, + lime = { r = 0.0000, g = 0.5020, b = 0.0000, a = 1 }, + limegreen = { r = 0.1961, g = 0.8039, b = 0.1961, a = 1 }, + linen = { r = 0.9804, g = 0.9412, b = 0.9020, a = 1 }, + magenta = { r = 1.0000, g = 0.0000, b = 1.0000, a = 1 }, + maroon = { r = 0.5020, g = 0.0000, b = 0.0000, a = 1 }, + mediumaquamarine = { r = 0.4000, g = 0.8039, b = 0.6667, a = 1 }, + mediumblue = { r = 0.0000, g = 0.0000, b = 0.8039, a = 1 }, + mediumorchid = { r = 0.7294, g = 0.3333, b = 0.8275, a = 1 }, + mediumpurple = { r = 0.5765, g = 0.4392, b = 0.8588, a = 1 }, + mediumseagreen = { r = 0.2353, g = 0.7020, b = 0.4431, a = 1 }, + mediumslateblue = { r = 0.4824, g = 0.4078, b = 0.9333, a = 1 }, + mediumspringgreen = { r = 0.0000, g = 0.9804, b = 0.6039, a = 1 }, + mediumturquoise = { r = 0.2824, g = 0.8196, b = 0.8000, a = 1 }, + mediumvioletred = { r = 0.7804, g = 0.0824, b = 0.5216, a = 1 }, + midnightblue = { r = 0.0980, g = 0.0980, b = 0.4392, a = 1 }, + mintcream = { r = 0.9608, g = 1.0000, b = 0.9804, a = 1 }, + mistyrose = { r = 1.0000, g = 0.8941, b = 0.8824, a = 1 }, + moccasin = { r = 1.0000, g = 0.8941, b = 0.7098, a = 1 }, + navajowhite = { r = 1.0000, g = 0.8706, b = 0.6784, a = 1 }, + navy = { r = 0.0000, g = 0.0000, b = 0.5020, a = 1 }, + oldlace = { r = 0.9922, g = 0.9608, b = 0.9020, a = 1 }, + olive = { r = 0.5020, g = 0.5020, b = 0.0000, a = 1 }, + olivedrab = { r = 0.4196, g = 0.5569, b = 0.1373, a = 1 }, + orange = { r = 1.0000, g = 0.6471, b = 0.0000, a = 1 }, + orangered = { r = 1.0000, g = 0.2706, b = 0.0000, a = 1 }, + orchid = { r = 0.8549, g = 0.4392, b = 0.8392, a = 1 }, + palegoldenrod = { r = 0.9333, g = 0.9098, b = 0.6667, a = 1 }, + palegreen = { r = 0.5961, g = 0.9843, b = 0.5961, a = 1 }, + paleturquoise = { r = 0.6863, g = 0.9333, b = 0.9333, a = 1 }, + palevioletred = { r = 0.8588, g = 0.4392, b = 0.5765, a = 1 }, + papayawhip = { r = 1.0000, g = 0.9373, b = 0.8353, a = 1 }, + peachpuff = { r = 1.0000, g = 0.8549, b = 0.7255, a = 1 }, + peru = { r = 0.8039, g = 0.5216, b = 0.2471, a = 1 }, + pink = { r = 1.0000, g = 0.7529, b = 0.7961, a = 1 }, + plum = { r = 0.8667, g = 0.6275, b = 0.8667, a = 1 }, + powderblue = { r = 0.6902, g = 0.8784, b = 0.9020, a = 1 }, + purple = { r = 0.5020, g = 0.0000, b = 0.5020, a = 1 }, + red = { r = 1.0000, g = 0.0000, b = 0.0000, a = 1 }, + rosybrown = { r = 0.7373, g = 0.5608, b = 0.5608, a = 1 }, + royalblue = { r = 0.2549, g = 0.4118, b = 0.8824, a = 1 }, + saddlebrown = { r = 0.5451, g = 0.2706, b = 0.0745, a = 1 }, + salmon = { r = 0.9804, g = 0.5020, b = 0.4471, a = 1 }, + sandybrown = { r = 0.9569, g = 0.6431, b = 0.3765, a = 1 }, + seagreen = { r = 0.1804, g = 0.5451, b = 0.3412, a = 1 }, + seashell = { r = 1.0000, g = 0.9608, b = 0.9333, a = 1 }, + sienna = { r = 0.6275, g = 0.3216, b = 0.1765, a = 1 }, + silver = { r = 0.7529, g = 0.7529, b = 0.7529, a = 1 }, + skyblue = { r = 0.5294, g = 0.8078, b = 0.9216, a = 1 }, + slateblue = { r = 0.4157, g = 0.3529, b = 0.8039, a = 1 }, + slategray = { r = 0.4392, g = 0.5020, b = 0.5647, a = 1 }, + slategrey = { r = 0.4392, g = 0.5020, b = 0.5647, a = 1 }, + snow = { r = 1.0000, g = 0.9804, b = 0.9804, a = 1 }, + springgreen = { r = 0.0000, g = 1.0000, b = 0.4980, a = 1 }, + steelblue = { r = 0.2745, g = 0.5098, b = 0.7059, a = 1 }, + tan = { r = 0.8235, g = 0.7059, b = 0.5490, a = 1 }, + teal = { r = 0.0000, g = 0.5020, b = 0.5020, a = 1 }, + thistle = { r = 0.8471, g = 0.7490, b = 0.8471, a = 1 }, + tomato = { r = 1.0000, g = 0.3882, b = 0.2784, a = 1 }, + turquoise = { r = 0.2510, g = 0.8784, b = 0.8157, a = 1 }, + violet = { r = 0.9333, g = 0.5098, b = 0.9333, a = 1 }, + wheat = { r = 0.9608, g = 0.8706, b = 0.7020, a = 1 }, + white = { r = 1.0000, g = 1.0000, b = 1.0000, a = 1 }, + whitesmoke = { r = 0.9608, g = 0.9608, b = 0.9608, a = 1 }, + yellow = { r = 1.0000, g = 1.0000, b = 0.0000, a = 1 }, + yellowgreen = { r = 0.6039, g = 0.8039, b = 0.1961, a = 1 } +} + +defines.color = {} + +for _, color in pairs(defines.color) do + color.r = color.r or 1 + color.g = color.g or 1 + color.b = color.b or 1 + color.a = color.a or 1 +end \ No newline at end of file diff --git a/lib/events.lua b/lib/events.lua new file mode 100644 index 0000000..a817be2 --- /dev/null +++ b/lib/events.lua @@ -0,0 +1,147 @@ +local powers_of_two = {} +for i = 0, 20 do + powers_of_two[i] = 2 ^ i +end + +if data then + local delays = {} + for _, n in pairs(powers_of_two) do + delays[#delays + 1] = { + name = 'py-ticked-script-delay-' .. n, + type = 'flying-text', + time_to_live = n, + speed = 0, + } + end + data:extend(delays) + return +end + +local events = {} + +---Drop-in replacement for script.on_event however it supports multiple handlers per event. You can also use 'on_built' 'on_destroyed' and 'on_init' as shortcuts for multiple events. +---@param event defines.events|defines.events[]|string +---@param f function +py.on_event = function(event, f) + if event == 'on_built' then + py.on_event({defines.events.on_built_entity, + defines.events.on_robot_built_entity, + defines.events.script_raised_built, + defines.events.script_raised_revive + }, f) + return + end + if event == 'on_destroyed' then + py.on_event({ + defines.events.on_player_mined_entity, + defines.events.on_robot_mined_entity, + defines.events.on_entity_died, + defines.events.script_raised_destroy + }, f) + return + end + for _, event in pairs(type(event) == 'table' and event or {event}) do + event = tostring(event) + events[event] = events[event] or {} + table.insert(events[event], f) + end +end + +py.on_nth_tick = function(event, f) + events[event] = events[event] or {} + table.insert(events[event], f) +end + +local function one_function_from_many(functions) + local l = #functions + if l == 1 then return functions[1] end + + return function(arg) + for i = 1, l do + functions[i](arg) + end + end +end + +py.finalize_events = function() + for event, functions in pairs(events) do + local f = one_function_from_many(functions) + if type(event) == 'number' then + script.on_nth_tick(event, f) + elseif event == 'on_init' then + script.on_init(f) + script.on_configuration_changed(f) + else + script.on_event(tonumber(event) or event, f) + end + end +end + +_G.gui_events = { + [defines.events.on_gui_click] = {}, + [defines.events.on_gui_confirmed] = {}, + [defines.events.on_gui_text_changed] = {}, + [defines.events.on_gui_checked_state_changed] = {}, + [defines.events.on_gui_selection_state_changed] = {}, + [defines.events.on_gui_checked_state_changed] = {}, + [defines.events.on_gui_elem_changed] = {}, + [defines.events.on_gui_value_changed] = {}, + [defines.events.on_gui_location_changed] = {}, + [defines.events.on_gui_selected_tab_changed] = {}, + [defines.events.on_gui_switch_state_changed] = {} +} +local function process_gui_event(event) + if event.element and event.element.valid then + for pattern, f in pairs(gui_events[event.name]) do + if event.element.name:match(pattern) then f(event); return end + end + end +end + +for event, _ in pairs(gui_events) do + py.on_event(event, process_gui_event) +end + +py.delayed_functions = {} +---use this to execute a script after a delay +---example: +---py.delayed_functions.my_delayed_func = function(param1, param2, param3) ... end +---py.execute_later('my_delayed_func', 60, param1, param2, param3) +---The above code will execute my_delayed_func after waiting for 60 ticks +---@param function_key string +---@param ticks integer +---@param ... any +function py.execute_later(function_key, ticks, ...) + if ticks < 1 or ticks % 1 ~= 0 then error('Invalid delay: ' .. ticks) end + local highest = 1 + for _, n in pairs(powers_of_two) do + if n <= ticks then + highest = n + else break end + end + local flying_text = game.surfaces.nauvis.create_entity{ + name = 'py-ticked-script-delay-' .. highest, + position = {0, 0}, + create_build_effect_smoke = false, + text = '' + } + if not flying_text then error() end + global._delayed_functions = global._delayed_functions or {} + global._delayed_functions[script.register_on_entity_destroyed(flying_text)] = {function_key, ticks - highest, {...}} +end +py.on_event(defines.events.on_entity_destroyed, function(event) + local data = global._delayed_functions[event.registration_number] + if not data then return end + global._delayed_functions[event.registration_number] = nil + + local function_key = data[1] + local ticks = data[2] + + if ticks == 0 then + local f = py.delayed_functions[function_key] + if not f then error('No function found for key: ' .. function_key) end + f(table.unpack(data[3])) + else + py.execute_later(function_key, ticks, table.unpack(data[3])) + end +end) \ No newline at end of file diff --git a/lib/lib.lua b/lib/lib.lua new file mode 100644 index 0000000..aa9f5d2 --- /dev/null +++ b/lib/lib.lua @@ -0,0 +1,20 @@ +_G.py = {} + +require 'table' +require 'string' +require 'defines' +require 'color' +require 'events' +require 'world-generation' + +if data and data.raw and not data.raw.item['iron-plate'] then + py.stage = 'settings' +elseif data and data.raw then + py.stage = 'data' + require 'data-stage' +elseif script then + py.stage = 'control' + require 'control-stage' +else + error('Could not determine load order stage.') +end \ No newline at end of file diff --git a/lib/metas/entity.lua b/lib/metas/entity.lua new file mode 100644 index 0000000..aa4b73e --- /dev/null +++ b/lib/metas/entity.lua @@ -0,0 +1,73 @@ +local collision_mask_util = require '__core__/lualib/collision-mask-util' + +---@class data.EntityPrototype +---@field public standardize fun(self: data.EntityPrototype): data.EntityPrototype +---@field public add_flag fun(self: data.EntityPrototype, flag: string): data.EntityPrototype +---@field public remove_flag fun(self: data.EntityPrototype, flag: string): data.EntityPrototype +---@field public has_flag fun(self: data.EntityPrototype, flag: string): boolean + +ENTITY = setmetatable({}, { + ---@param entity data.EntityPrototype + __call = function(self, entity) + local etype = type(entity) + if etype == 'string' then + for ptype in pairs(defines.prototypes.entity) do + local result = data.raw[ptype][entity] + if result then return result:standardize() end + end + elseif etype == 'table' then + if not entity.type then error('Tried to extend an entity ' .. entity.name .. ' without providing a type') end + data:extend{entity} + return entity:standardize() + else error('Invalid type ' .. etype) end + end +}) + +local metas = {} + +metas.standardize = function(self) + local minable = self.minable + if minable then + if minable.results and type(minable.results) == 'table' then + minable.result = nil + minable.count = nil + elseif minable.result then + minable.results = {{type = 'item', name = minable.result, amount = minable.count or 1}} + else + minable.results = {} + end + + for k, p in pairs(minable.results) do + minable.results[k] = py.standardize_product(p) + end + end + + self.selection_box = self.selection_box or self.collision_box + self.collision_mask = self.collision_mask or collision_mask_util.get_mask(self) + + return self +end + +metas.add_flag = function(self, flag) + if not self.flags then self.flags = {} end + table.insert(self.flags, flag) + return self +end + +metas.remove_flag = function(self, flag) + if not self.flags then return self end + for i, f in pairs(self.flags) do + if f == flag then table.remove(self.flags, i) end + end + return self +end + +metas.has_flag = function(self, flag) + if not self.flags then return false end + for _, f in pairs(self.flags) do + if f == flag then return true end + end + return false +end + +return metas \ No newline at end of file diff --git a/lib/metas/fluid.lua b/lib/metas/fluid.lua new file mode 100644 index 0000000..8f782ff --- /dev/null +++ b/lib/metas/fluid.lua @@ -0,0 +1,18 @@ +FLUID = setmetatable(data.raw.fluid, { + ---@param tech data.FluidPrototype + __call = function(self, fluid) + local ftype = type(fluid) + if ftype == 'string' then + fluid = data.raw.fluid[fluid] + if not fluid then error('Fluid ' .. fluid .. ' does not exist') end + elseif ftype == 'table' then + fluid.type = 'fluid' + data:extend{fluid} + else error('Invalid type ' .. ftype) end + return fluid + end +}) + +local metas = {} + +return metas \ No newline at end of file diff --git a/lib/metas/item.lua b/lib/metas/item.lua new file mode 100644 index 0000000..24b22d3 --- /dev/null +++ b/lib/metas/item.lua @@ -0,0 +1,47 @@ +---@class data.ItemPrototype +---@field public add_flag fun(self: data.ItemPrototype, flag: string): data.ItemPrototype +---@field public remove_flag fun(self: data.ItemPrototype, flag: string): data.ItemPrototype +---@field public has_flag fun(self: data.ItemPrototype, flag: string): boolean + +ITEM = setmetatable({}, { + ---@param item data.ItemPrototype + __call = function(self, item) + local itype = type(item) + if itype == 'string' then + for ptype in pairs(defines.prototypes.item) do + local result = data.raw[ptype][item] + if result then return result end + end + elseif itype == 'table' then + if not item.type then error('Tried to extend an item ' .. item.name .. ' without providing a type') end + data:extend{item} + return item + else error('Invalid type ' .. itype) end + end +}) + +local metas = {} + +metas.add_flag = function(self, flag) + if not self.flags then self.flags = {} end + table.insert(self.flags, flag) + return self +end + +metas.remove_flag = function(self, flag) + if not self.flags then return self end + for i, f in pairs(self.flags) do + if f == flag then table.remove(self.flags, i) end + end + return self +end + +metas.has_flag = function(self, flag) + if not self.flags then return false end + for _, f in pairs(self.flags) do + if f == flag then return true end + end + return false +end + +return metas \ No newline at end of file diff --git a/lib/metas/metas.lua b/lib/metas/metas.lua new file mode 100644 index 0000000..fd4a439 --- /dev/null +++ b/lib/metas/metas.lua @@ -0,0 +1,77 @@ +-- A drop-in replacement for stdlib. Adds global tables for RECIPE TECHNOLOGY ENTITY and ITEM. + +local lib = { + recipe = require 'recipe', + technology = require 'technology', + entity = require 'entity', + item = require 'item', + fluid = require 'fluid', + tile = require 'tile' +} + +---@class data.AnyPrototype +---@field public copy fun(self: data.AnyPrototype, new_name: string | fun(self: data.AnyPrototype): string): data.AnyPrototype +---@field public subgroup_order fun(self: data.AnyPrototype, subgroup: string, order: string): data.AnyPrototype +---@field public set_fields fun(self: data.AnyPrototype, fields: table): data.AnyPrototype +---@field public set fun(self: data.AnyPrototype, field: string, value: any): data.AnyPrototype + +for _, meta in pairs(lib) do + meta.copy = function(self, new_name) + if not new_name then error('No new name provided') end + local copy = table.deepcopy(self) + if type(new_name) == 'function' then new_name = new_name(copy) end + copy.name = new_name + return setmetatable(copy, getmetatable(self)) + end + + meta.subgroup_order = function(self, subgroup, order) + self.subgroup = subgroup + self.order = order + return self + end + + meta.set_fields = function(self, fields) + for k, v in pairs(fields) do + if type(k) ~= 'string' then error('Field name must be a string') end + self[k] = v + end + return self + end + + meta.set = function(self, field, value) + if type(field) ~= 'string' then error('Field name must be a string') end + self[field] = value + return self + end +end + +local metas = {} +metas.recipe = {__index = lib.recipe} +metas.technology = {__index = lib.technology} +for ptype in pairs(defines.prototypes.entity) do + metas[ptype] = {__index = lib.entity} +end +for ptype in pairs(defines.prototypes.item) do + metas[ptype] = {__index = lib.item} +end +metas.fluid = {__index = lib.fluid} + +for ptype, prototypes in pairs(data.raw) do + local meta = metas[ptype] + if meta then + for _, prototype in pairs(prototypes) do + setmetatable(prototype, meta) + end + end +end + +local extend = data.extend +data.extend = function(self, prototypes) + extend(self, prototypes) + for _, prototype in pairs(prototypes) do + local meta = metas[prototype.type] + if meta then + setmetatable(prototype, meta) + end + end +end \ No newline at end of file diff --git a/lib/metas/recipe.lua b/lib/metas/recipe.lua new file mode 100644 index 0000000..341d697 --- /dev/null +++ b/lib/metas/recipe.lua @@ -0,0 +1,220 @@ +---@class data.RecipePrototype +---@field public standardize fun(self: data.RecipePrototype): data.RecipePrototype +---@field public allow_productivity fun(self: data.RecipePrototype): data.RecipePrototype +---@field public add_unlock fun(self: data.RecipePrototype, technology_name: string | string[]): data.RecipePrototype +---@field public remove_unlock fun(self: data.RecipePrototype, technology_name: string | string[]): data.RecipePrototype +---@field public replace_ingredient fun(self: data.RecipePrototype, old_ingredient: string, new_ingredient: string | data.IngredientPrototype, new_amount: integer?): data.RecipePrototype +---@field public add_ingredient fun(self: data.RecipePrototype, ingredient: string | data.IngredientPrototype): data.RecipePrototype +---@field public remove_ingredient fun(self: data.RecipePrototype, ingredient_name: string): data.RecipePrototype +---@field public replace_result fun(self: data.RecipePrototype, old_result: string, new_result: string | data.ProductPrototype, new_amount: integer?): data.RecipePrototype +---@field public add_result fun(self: data.RecipePrototype, result: string | data.ProductPrototype): data.RecipePrototype +---@field public remove_result fun(self: data.RecipePrototype, result_name: string): data.RecipePrototype +---@field public clear_ingredients fun(self: data.RecipePrototype): data.RecipePrototype + +RECIPE = setmetatable(data.raw.recipe, { + ---@param recipe data.RecipePrototype + __call = function(self, recipe) + local rtype = type(recipe) + if rtype == 'string' then + recipe = data.raw.recipe[recipe] + if not recipe then error('Recipe ' .. recipe .. ' does not exist') end + elseif rtype == 'table' then + recipe.type = 'recipe' + data:extend{recipe} + else error('Invalid type ' .. rtype) end + return recipe:standardize() + end +}) + +local metas = {} + +metas.standardize = function(self) + if self.normal then + for k, v in pairs(self.normal) do + if not self[k] then self[k] = v end + end + elseif self.expensive then + for k, v in pairs(self.expensive) do + if not self[k] then self[k] = v end + end + end + self.normal = nil + self.expensive = nil + + if self.results and type(self.results) == 'table' then + self.result = nil + self.result_count = nil + elseif self.result then + self.results = {{type = 'item', name = self.result, amount = self.result_count or 1}} + else + self.results = {} + end + + for k, p in pairs(self.results) do + self.results[k] = py.standardize_product(p) + end + + for k, p in pairs(self.ingredients) do + self.ingredients[k] = py.standardize_product(p) + end + + self.energy_required = self.energy_required or 0.5 + + return self +end + +local productivity_modules = nil +metas.allow_productivity = function(self) + if not productivity_modules then + productivity_modules = {} + for _, module in pairs(data.raw.module) do + if module.name:find('productivity%-module') and module.limitation then + productivity_modules[module] = true + end + end + end + + for module in pairs(productivity_modules) do + table.insert(module.limitation, self.name) + end + + return self +end + +py.allow_productivity = function(recipe_names) + productivity_modules = nil + for _, recipe_name in pairs(recipe_names) do + RECIPE(recipe_name):allow_productivity() + end +end + +metas.add_unlock = function(self, technology_name) + if type(technology_name) == 'table' then + for _, tech in pairs(technology_name) do + self:add_unlock(tech) + end + return self + end + + local technology = data.raw.technology[technology_name] + if not technology then + log('WARNING @ recipe:add_unlock(): Technology ' .. technology_name .. ' does not exist') + return self + end + + if not technology.effects then + technology.effects = {} + end + + table.insert(technology.effects, {type = 'unlock-recipe', recipe = self.name}) + + return self +end + +metas.remove_unlock = function(self, technology_name) + if type(technology_name) == 'table' then + for _, tech in pairs(technology_name) do + self:remove_unlock(tech) + end + return self + end + + local technology = data.raw.technology[technology_name] + if not technology then + log('WARNING @ recipe:remove_unlock(): Technology ' .. technology_name .. ' does not exist') + return self + end + + if not technology.effects then + return self + end + + technology.effects = table.filter(technology.effects, function(effect) + return effect.recipe ~= self.name + end) + + return self +end + +do + local function replacement_helper(ingredients_or_results, old, new, new_amount) + local type = type(new) + if type == 'string' then + for _, ingredient in pairs(ingredients_or_results) do + if ingredient.name == old then + ingredient.name = new + if new_amount then + ingredient.amount = new_amount + ingredient.amount_min = nil + ingredient.amount_max = nil + end + end + end + elseif type == 'table' then + for k, ingredient in pairs(ingredients_or_results) do + if ingredient.name == old then + ingredients_or_results[k] = py.standardize_product(new) + end + end + end + end + + metas.replace_ingredient = function(self, old_ingredient, new_ingredient, new_amount) + self:standardize() + replacement_helper(self.ingredients, old_ingredient, new_ingredient, new_amount) + return self + end + + metas.replace_result = function(self, old_result, new_result) + self:standardize() + replacement_helper(self.results, old_result, new_result, new_amount) + return self + end +end + +metas.add_ingredient = function(self, ingredient) + self:standardize() + table.insert(self.ingredients, py.standardize_product(ingredient)) + return self +end + +metas.add_result = function(self, result) + self:standardize() + table.insert(self.results, py.standardize_product(result)) + return self +end + +metas.remove_ingredient = function(self, ingredient_name) + self:standardize() + local amount_removed = 0 + self.ingredients = table.filter(self.ingredients, function(ingredient) + if ingredient.name == ingredient_name then + local amount = ingredient.amount or (ingredient.amount_min + ingredient.amount_max) / 2 + amount_removed = amount_removed + amount + return false + end + return true + end) + return self, amount_removed +end + +metas.remove_result = function(self, result_name) + self:standardize() + local amount_removed = 0 + self.results = table.filter(self.results, function(result) + if result.name == result_name then + local amount = result.amount or (result.amount_min + result.amount_max) / 2 + amount_removed = amount_removed + amount + return false + end + return true + end) + return self, amount_removed +end + +metas.clear_ingredients = function(self) + self.ingredients = {} + return self +end + +return metas \ No newline at end of file diff --git a/lib/metas/technology.lua b/lib/metas/technology.lua new file mode 100644 index 0000000..a927cdb --- /dev/null +++ b/lib/metas/technology.lua @@ -0,0 +1,98 @@ +---@class data.TechnologyPrototype +---@field public standardize fun(): data.TechnologyPrototype +---@field public add_prereq fun(prereq_technology_name: string): data.TechnologyPrototype +---@field public remove_prereq fun(prereq_technology_name: string): data.TechnologyPrototype +---@field public remove_pack fun(science_pack_name: string): data.TechnologyPrototype +---@field public add_pack fun(science_pack_name: string): data.TechnologyPrototype +---@field dependencies string[] + +TECHNOLOGY = setmetatable(data.raw.technology, { + ---@param tech data.TechnologyPrototype + __call = function(self, technology) + local ttype = type(technology) + if ttype == 'string' then + technology = data.raw.technology[technology] + if not technology then error('Technology ' .. technology .. ' does not exist') end + elseif ttype == 'table' then + technology.type = 'technology' + data:extend{technology} + else error('Invalid type ' .. ttype) end + return technology:standardize() + end +}) + +local metas = {} + +metas.standardize = function(self) + if self.normal then + for k, v in pairs(self.normal) do + if not self[k] then self[k] = v end + end + elseif self.expensive then + for k, v in pairs(self.expensive) do + if not self[k] then self[k] = v end + end + end + self.normal = nil + self.expensive = nil + + if not self.unit then self.unit = {ingredients = {}} end + + for k, p in pairs(self.unit.ingredients) do + self.unit.ingredients[k] = py.standardize_product(p) + end + + self.prerequisites = self.prerequisites or {} + self.dependencies = self.dependencies or {} + self.effects = self.effects or {} + + return self +end + +metas.add_prereq = function(self, prereq_technology_name) + local prereq_technology = data.raw.technology[prereq_technology_name] + if not prereq_technology then + log('WARNING @ technology:add_prereq(): Technology ' .. prereq_technology_name .. ' does not exist') + return self + end + + if not self.prerequisites then + self.prerequisites = {} + end + + table.insert(self.prerequisites, prereq_technology_name) + + return self +end + +metas.remove_prereq = function(self, prereq_technology_name) + if not self.prerequisites then + return self + end + + self.prerequisites = table.filter(self.prerequisites, function(prereq) return prereq ~= prereq_technology_name end) + + return self +end + +metas.remove_pack = function(self, science_pack_name) + if not self.unit then + return self + end + + self.unit.ingredients = table.filter(self.unit.ingredients, function(ingredient) return ingredient.name ~= science_pack_name end) + + return self +end + +metas.add_pack = function(self, science_pack_name) + if not self.unit then + self.unit = {ingredients = {}} + end + + table.insert(self.unit.ingredients, {type = 'item', name = science_pack_name, amount = 1}) + + return self +end + +return metas \ No newline at end of file diff --git a/lib/metas/tile.lua b/lib/metas/tile.lua new file mode 100644 index 0000000..99a964b --- /dev/null +++ b/lib/metas/tile.lua @@ -0,0 +1,18 @@ +TILE = setmetatable(data.raw.tile, { + ---@param tech data.TilePrototype + __call = function(self, tile) + local ftype = type(tile) + if ftype == 'string' then + tile = data.raw.tile[tile] + if not tile then error('Tile ' .. tile .. ' does not exist') end + elseif ftype == 'table' then + tile.type = 'tile' + data:extend{tile} + else error('Invalid type ' .. ftype) end + return tile + end +}) + +local metas = {} + +return metas \ No newline at end of file diff --git a/lib/placement-restriction.lua b/lib/placement-restriction.lua new file mode 100644 index 0000000..ec7e3d2 --- /dev/null +++ b/lib/placement-restriction.lua @@ -0,0 +1,68 @@ +local function parse_restriction_condition(condition) + local function helper() + local type = condition.type + if type == 'placed-on' then + return {'placement-restriction.placed-on', {'entity-name.' .. condition.entity}} + elseif type == 'surface-type' then + return {'placement-restriction.surface-type', {'surface-type.' .. condition.surface_type}} + elseif type == 'surface-tag' then + return {'placement-restriction.surface-tag', {'surface-tag-name.' .. condition.tag}} + elseif type == 'distance' then + return {'placement-restriction.distance', {'surface-distance.' .. condition.distance}} + end + + -- greater than less than + local args + if condition.min_amount and condition.max_amount then + args = {'placement-restriction.' .. type .. '-3', condition.min_amount, condition.max_amount} + elseif condition.max_amount then + args = {'placement-restriction.' .. type .. '-2', condition.max_amount} + elseif condition.min_amount then + args = {'placement-restriction.' .. type .. '-1', condition.min_amount} + else + error('min_amount or max_amount missing from placement restriction of type: ' .. type) + end + + if type == 'atmosphere' then + if not condition.gas then error('No gas provided for atomspheric condition') end + if not data.raw.fluid[condition.gas] then error('Invalid gas: ' .. condition.gas) end + for i = 2, #args do + args[i] = args[i] * 100 + end + table.insert(args, 2, {data.raw.fluid[condition.gas].localised_name or ('fluid-name.' .. condition.gas)}) + table.insert(args, 2, '[fluid=' .. condition.gas .. ']') + end + + return args + end + + local localised_string = helper() + if condition.NOT then localised_string = {'placement-restriction.not', localised_string} end + return localised_string +end + +local function placement_restriction_description_helper(i, restriction, parens) + if i == #restriction then + if data then + return {'placement-restriction.dot', parse_restriction_condition(restriction[i])} + else + return parse_restriction_condition(restriction[i]) + end + end + return { + parens, + parse_restriction_condition(restriction[i]), + {'placement-restriction.' .. restriction[i + 1]}, + placement_restriction_description_helper(i + 2, restriction, parens) + } +end + +py.placement_restriction_description = function(restriction) + if #restriction % 2 == 0 then error('Placement restriction length must be odd') end + local parens = data and 'placement-restriction.parens-dot' or 'placement-restriction.parens' + return {'placement-restriction.header', placement_restriction_description_helper(1, restriction, parens)} +end + +py.distance = function(x, y) + return (x ^ 2 + y ^ 2) ^ 0.5 +end \ No newline at end of file diff --git a/lib/string.lua b/lib/string.lua new file mode 100644 index 0000000..49713a9 --- /dev/null +++ b/lib/string.lua @@ -0,0 +1,28 @@ +-- Adds new functions to the builtin string class. + +---Splits a string into a table of substrings. +---@param s string +---@param seperator string +---@return table +string.split = function(s, seperator) + local result = {} + for match in (s..seperator):gmatch('(.-)'..seperator) do + result[#result + 1] = match + end + return result +end + +---Returns a boolean indicating whether a string is a digit. +---@param s string +---@return boolean +string.is_digit = function(s) + return s:match('%d') ~= nil +end + +---Returns a boolean indicating if the first string starts with the second string. +---@param s string +---@param start string +---@return boolean +string.starts_with = function(s, start) + return s:sub(1, #start) == start +end \ No newline at end of file diff --git a/lib/table.lua b/lib/table.lua new file mode 100644 index 0000000..52d8538 --- /dev/null +++ b/lib/table.lua @@ -0,0 +1,197 @@ +-- Adds new functions to the builtin table class. + +---Returns a new table with the results of calling a provided function on every element in the table. +---@param tbl table +---@param f fun(v: any, k: any, ...: any): any +---@param ... any +---@return table +table.map = function(tbl, f, ...) + local result = {} + for k, v in pairs(tbl) do result[k] = f(v, k, ...) end + return result +end + +---Returns a new table with all elements that pass the test implemented by the provided function. +---@param tbl table +---@param f fun(v: any, k: any, ...: any): boolean +---@param ... any +---@return table +table.filter = function(tbl, f, ...) + local result = {} + local is_array = #tbl > 0 + if is_array then + for i, v in pairs(tbl) do if f(v, i, ...) then result[#result + 1] = v end end + else + for k, v in pairs(tbl) do if f(v, k, ...) then result[k] = v end end + end + return result +end + +---Returns the first element that satisfies the predicate. +---@param tbl table +---@param f fun(v: any, k: any, ...: any): any +---@param ... any +---@return any, any +---@overload fun(tbl: table, v: any): any, any +table.find = function(tbl, f, ...) + if type(f) == 'function' then + for k, v in pairs(tbl) do if f(v, k, ...) then return v, k end end + else + for k, v in pairs(tbl) do if v == f then return v, k end end + end + return nil +end + +---Returns true if any element in the table passes the test implemented by the provided function. +---@param tbl table +---@param f fun(v: any, k: any, ...: any): any +---@param ... any +---@return boolean +---@overload fun(tbl: table, v: any): boolean +table.any = function(tbl, f, ...) + return table.find(tbl, f, ...) ~= nil +end + +---Returns true if all elements in the table pass the test implemented by the provided function. +---@param tbl table +---@param f fun(v: any, k: any, ...: any): any +---@param ... any +---@return boolean +---@overload fun(tbl: table, v: any): boolean +table.all = function(tbl, f, ...) + if type(f) == 'function' then + for k, v in pairs(tbl) do if not f(v, k, ...) then return false end end + else + for k, v in pairs(tbl) do if v ~= f then return false end end + end + return true +end + +---Returns a boolean indicating whether the table has size 0. +---@param tbl table +---@return boolean +table.is_empty = function(tbl) + return next(tbl) == nil +end + +---Returns an array of the table's keys. +---@param tbl table +---@return any[] +table.keys = function(tbl) + local keys = {} + for k, _ in pairs(tbl) do keys[#keys + 1] = k end + return keys +end + +---Returns an array of the table's values. +---@param tbl table +---@return any[] +table.values = function(tbl) + local values = {} + for _, v in pairs(tbl) do table.insert(values, v) end + return values +end + +---Returns the first element of the table. +---@param tbl table +---@return any +table.first = function(tbl) + local _, v = next(tbl) + return v +end + +---Returns the last element of the table. +---@param tbl table +---@return any +table.last = function(tbl) + local size = #tbl + if size == 0 then return nil end + return tbl[size] +end + +---Returns a new table with keys and values swapped. +---@param tbl table +---@return table +table.invert = function(tbl) + local result = {} + for k, v in pairs(tbl) do result[v] = k end + return result +end + +---Returns a new table by merging the provided tables. If a key exists in multiple tables, the value from the last table is used. +---@param ... table +---@return table +table.merge = function(...) + local result = {} + for _, tbl in pairs{...} do + for k, v in pairs(tbl) do result[k] = v end + end + return result +end + +---Returns a new array by merging the provided tables. The values are appended in the order they are provided. +---@param ... table +---@return any[] +table.array_combine = function(...) + local result = {} + for _, tbl in pairs{...} do + for _, v in pairs(tbl) do result[#result + 1] = v end + end + return result +end + +---Reverses an array in-place and returns it. +---@param tbl any[] +---@return any[] +table.reverse = function(tbl) + for i = 1, #tbl / 2 do + tbl[i], tbl[#tbl - i + 1] = tbl[#tbl - i + 1], tbl[i] + end + return tbl +end + +local function shuffle(t) + local keys = {} + local n = 0 + for k in pairs(t) do + n = n + 1 + keys[n] = k + end + + while n > 1 do + local k = math.random(n) + keys[n], keys[k] = keys[k], keys[n] + n = n - 1 + end + + return keys +end +---Like normal pairs(), but in randomized order +---@param t table +---@return fun():any, any +function py.shuffled_pairs(t) + local shuffled_keys = shuffle(t) + local i = 0 + return function() + i = i + 1 + local key = shuffled_keys[i] + if key then + return key, t[key] + end + end +end + +---Returns a new array with duplicates removed. +---@param tbl any[] +---@return any[] +table.dedupe = function(tbl) + local seen = {} + local result = {} + for _, v in pairs(tbl) do + if not seen[v] then + table.insert(result, v) + seen[v] = true + end + end + return result +end \ No newline at end of file diff --git a/lib/world-generation.lua b/lib/world-generation.lua new file mode 100644 index 0000000..2ffaff5 --- /dev/null +++ b/lib/world-generation.lua @@ -0,0 +1,47 @@ +local noise = require 'noise' +local tne = noise.to_noise_expression + +---Returns a random number generator based on another generator. +---@param generator LuaRandomGenerator +---@return LuaRandomGenerator +py.reseed = function(generator) + return game.create_random_generator(generator(341, 2147483647)) +end + +---Sets a noise constant which can be accessed inside a named_noise_expression. WARNING: Prone to floating point inaccuracies, don't use this to transfer worldgen seeds. +---@param i integer +---@param surface LuaSurface +---@param data number +py.set_noise_constant = function(i, surface, data) + local mgs = surface.map_gen_settings + mgs.autoplace_controls = mgs.autoplace_controls or {} + mgs.autoplace_controls['py-autoplace-control-' .. i] = mgs.autoplace_controls['py-autoplace-control-' .. i] or {} + mgs.autoplace_controls['py-autoplace-control-' .. i].richness = data + surface.map_gen_settings = mgs +end + +---Data stage only. Gets a noise constant which can be accessed inside a named_noise_expression. +---@param i integer +py.get_noise_constant = function(i) + return noise.get_control_setting('py-autoplace-control-' .. i).richness_multiplier +end + +---Returns a noise expression which is an approximation of perlin noise. The output ranges from -1.2 to 1.2. +---@param x NoiseExpression +---@param y NoiseExpression +---@param seed integer +---@param zoom number +py.basis_noise = function(x, y, seed, zoom) + return { + type = 'function-application', + function_name = 'factorio-basis-noise', + arguments = { + x = x, + y = y, + seed0 = tne(noise.var('map_seed')), + seed1 = tne(seed), + input_scale = tne(0.9999728452) / zoom, + output_scale = tne(1.2 / 1.7717819213867) + } + } +end \ No newline at end of file diff --git a/luagraphs/queue/queue.lua b/luagraphs/queue/queue.lua new file mode 100644 index 0000000..685545a --- /dev/null +++ b/luagraphs/queue/queue.lua @@ -0,0 +1,352 @@ +---A double queue. +-- Taken from ***Programming in Lua*** [Queues and Double Queues](http://www.lua.org/pil/11.4.html) +-- With modifications from ***https://mods.factorio.com/mod/stdlib*** [stdlib](https://mods.factorio.com/mod/stdlib) +-- and modified to not allow nil values, and returns nil if @{pop_first} or @{pop_last} is used when the queue is empty. +-- @module Misc.Queue +-- @usage local Queue = require '__pypostprocessing__.luagraphs.queue.queue' +-- local q = Queue() -- create a new empty queue +-- q('my value') -- push a value onto the queue +-- q() -- pop the last value off the queue +-- game.print(#q) -- print the number of items in the queue + +local Queue = {} +setmetatable(Queue, Queue) +local t_size = table_size + +local meta = {} + +function Queue.__call(_, ...) + local queue = { first = 1, last = 0, objects = {} } + setmetatable(queue, meta) + for _, push in pairs { ... } do + queue(push) + end + return queue +end + +---Constructs a new Queue object. +-- @param ... mixed, values to push into the queue +-- @treturn @{Queue} a new queue +function Queue.new(...) + return Queue.__call(nil, ...) +end + +---Load global.queue or queues during on_load, as metatables are not persisted. +--

This is only needed if you are using the queue as an object and storing it in global. +-- @tparam table queue (@{Queue},...) +-- @usage global.myqueue1 = Queue.new() +-- script.on_load(function() Queue.load(global.myqueue)) +function Queue.load(queue) + if type(queue) == 'table' and queue.first then + return setmetatable(queue, meta) + end +end + +---Push a new element to the front of the queue. +-- @tparam Queue queue the queue to push an element to +-- @tparam Mixed value the element to push +function Queue.push_first(queue, ...) + for _, value in pairs { ... } do + queue.first = queue.first - 1 + queue.objects[queue.first] = value + end + return queue +end + +---Push a new element to the back of the queue. +-- @tparam Queue queue the queue to push an element to +-- @tparam Mixed ... the element(s) to push +function Queue.push_last(queue, ...) + for _, value in pairs { ... } do + queue.last = queue.last + 1 + queue.objects[queue.last] = value + end + return queue +end + +---Shortcut for @{Queue.push_last} +-- @function Queue.push +Queue.push = Queue.push_last + +---Push a new element to a specific location of the queue. +-- @tparam Queue queue the queue to push an element to +-- @tparam number index the index to push to. +-- @tparam Mixed value the element to push. +function Queue.push_at(queue, index, value) + if index < queue.first then + return Queue.push_first(queue, value) + elseif index > queue.last then + return Queue.push_last(queue, value) + else + table.insert(queue.objects, index, value) + queue.last = queue.last + 1 + end + return queue +end + +function Queue.wrapper(self, func_name, ...) + if Queue[func_name] then + Queue[func_name](self, ...) + end + return self +end + +---Retrieve the element at the front of the queue and remove it from the queue. +-- @tparam Queue queue the queue to retrieve the element from +-- @treturn Mixed value the element at the front of the queue +function Queue.pop_first(queue) + if Queue.is_empty(queue) then + return nil + end + local first = queue.first + local value = queue.objects[first] + queue.objects[first] = nil -- to allow garbage collection + queue.first = first + 1 + return value +end + +---Shortcut for @{Queue.pop_first} +-- @function Queue.pop +Queue.pop = Queue.pop_first + +local function remove(queue, index) + local ret = queue.objects[index] + if ret ~= nil then + for i = index + 1, queue.last do + queue.objects[i - 1] = queue.objects[i] + end + queue.objects[queue.last] = nil + queue.last = queue.last - 1 + end + return ret +end + +---Pop an element at a specific location of the queue. +-- @tparam Queue queue the queue to push an element to +-- @tparam number index the index to push to. +-- @treturn Mixed value the popped element. +function Queue.pop_at(queue, index) + return remove(queue, index) +end + +---Peek at an element in the queue without disturbing the queue. +-- @tparam Queue queue the queue to peek at +-- @tparam number index the index in the queue to peek at +-- @treturn Mixed the value of the peeked element +function Queue.peek_at(queue, index) + return queue.objects[index] +end + +---Return the element at the front of the queue and remove it from the queue. +-- @tparam Queue queue the queue to retrieve the element from +-- @treturn Mixed the element at the front of the queue +function Queue.peek_first(queue) + return queue.objects[queue.first] +end + +---Shortcut for @{Queue.peek_first} +-- @function Queue.peek +Queue.peek = Queue.peek_first + +---Retrieve the element at the back of the queue and remove it from the queue. +-- @tparam Queue queue the queue to retrieve the element from +-- @treturn Mixed the element at the back of the queue +function Queue.pop_last(queue) + if queue.is_empty(queue) then + return nil + end + + local last = queue.last + local value = queue.objects[last] + queue.objects[last] = nil -- to allow garbage collection + queue.last = last - 1 + return value +end + +---Return the element at the back of the queue. +-- @tparam Queue queue the queue to retrieve the element from +-- @treturn Mixed the element at the back of the queue +function Queue.peek_last(queue) + return queue.objects[queue.last] +end + +---Returns the popped value and pushes back into the queue. +-- @tparam Queue queue the queue +-- @return Mixed the value that was popped. +function Queue.pop_and_push(queue) + local ret = queue.pop(queue) + queue.push(queue, ret) + return ret +end + +---Returns the queue after popping the last element and pushing it to the top. +-- @tparam Queue queue the queue +-- @treturn @{Queue} the queue +function Queue.cycle(queue) + return queue.push(queue, queue.pop(queue)) +end + +---Gets the first index which matches the stored data. does not compare inside tables. +function Queue.find(queue, find) + for i, v in pairs(queue) do + if v == find then + return i + end + end +end + +local function _sort_func(a, b) + local lhs = type(a) == 'table' and '{' or tostring(a) + local rhs = type(b) == 'table' and '{' or tostring(b) + return lhs < rhs +end + +---sort and reorder the queue +function Queue.sort(queue, func) + local sorted = {} + for _, v in pairs(queue) do + if v ~= nil then + sorted[#sorted + 1] = v + end + end + table.sort(sorted, func or _sort_func) + queue.objects = sorted + queue.first, queue.last = 1, #queue.objects + return queue +end + +---Returns true if the given queue is empty. +-- @tparam Queue queue the queue to check +-- @treturn boolean true if empty, false otherwise +function Queue.is_empty(queue) + return queue.first > queue.last +end + +---Returns the number of items in the queue. +-- @tparam Queue queue the queue to check +-- @treturn number the number of items in the queue +function Queue.size(queue) + return t_size(queue.objects) +end + +---Shortcut for @{Queue.size} +-- @function Queue.count +Queue.count = Queue.size + +---Return the next element in the queue +-- @tparam Queue queue the queue to check +-- @tparam number|nil index if nil return the first value, else return the next index value +-- @tparam boolean pop pop the value off the queue +-- @treturn number|nil the index +-- @treturn Mixed|nil the value at queue index +function Queue.next(queue, index, pop) + index = not index and queue.first or index + (pop and 0 or 1) + for i = index, queue.last do + local v = queue.objects[i] + if v ~= nil then + return i, pop and Queue.pop_at(queue, i) or v + end + end + return nil, nil +end + +---Return the previous element in the queue +-- @tparam Queue queue the queue to check +-- @tparam number|nil index if nil return the last value, else return the previous index value +-- @tparam boolean pop pop the value off the queue +-- @treturn number|nil the index +-- @treturn Mixed|nil the value at queue index +function Queue.rnext(queue, index, pop) + -- next returns index of next or nil and data, + index = not index and queue.last or (index < queue.first and queue.first or index) - 1 + for i = index, queue.first, -1 do + local v = queue.objects[i] + if v ~= nil then + return i, pop and Queue.pop_at(queue, i) or v + end + end + return nil, nil +end + +local function next_pop(queue, index) + return Queue.next(queue, index, true) +end + +local function rnext_pop(queue, index) + return Queue.rnext(queue, index, true) +end + +---Iterate the queue forward +function Queue.pairs(queue, pop) + return pop and next_pop or Queue.next, queue, nil +end + +---Iterate the queue backwards +function Queue.rpairs(queue, pop) + return pop and rnext_pop or Queue.rnext, queue, nil +end + +do + meta.__class = 'queue' + meta.__len = Queue.size + meta.__unm = Queue.pop + meta.__parent = Queue + meta.__debugline = [[{[}first={first},last={last}{]}]] + + -- Allows queue[3] to return the item at queue.objects[3] + meta.__index = function(self, k) + if type(k) == 'number' then + return self:peek_at(k) + else + local v = rawget(self, k) + if v == nil then + return Queue[k] + end + return v + end + end + + meta.__newindex = function(self, k, v) + if type(k) == 'number' then + if v ~= nil then + self:push_at(k, v) + else + error('Attempt to modify Queue structure') + end + else + rawset(self, k, v) + end + end + + -- Allows queue() to pop_first and queue(data) to push_last + meta.__call = function(self, ...) + if ... then + return self:push(...) + else + return self:pop() + end + end + + meta.__add = function(queue1, queue2) + local new = Queue.new() + local lhs = getmetatable(queue1) == meta and true + local rhs = getmetatable(queue2) == meta and true + if lhs then + for _, v in pairs(queue1.objects) do + new:push(v) + end + else + new:push(queue1) + end + if rhs then + for _, v in pairs(queue2.objects) do + new:push(v) + end + else + new:push(queue2) + end + return new + end +end + +return Queue From 907be4a51de6380743934d715ee8d5f63759c182 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Sat, 25 May 2024 20:44:02 -0500 Subject: [PATCH 03/13] Improve error messaging for new stdlib --- lib/control-stage.lua | 32 ++++++++++++++++++++++++++++++++ lib/metas/entity.lua | 4 ++++ lib/metas/fluid.lua | 2 +- lib/metas/item.lua | 4 ++++ lib/metas/metas.lua | 1 + lib/metas/recipe.lua | 8 ++++++-- lib/metas/tile.lua | 2 +- 7 files changed, 49 insertions(+), 4 deletions(-) diff --git a/lib/control-stage.lua b/lib/control-stage.lua index 6b712bc..6efcce2 100644 --- a/lib/control-stage.lua +++ b/lib/control-stage.lua @@ -148,4 +148,36 @@ py.find_grandparent = function(element, name) element = element.parent end error('Could not find parent gui element with name: ' .. name) +end + +local si_prefixes = { + [0] = '', + 'si-prefix-symbol-kilo', + 'si-prefix-symbol-mega', + 'si-prefix-symbol-giga', + 'si-prefix-symbol-tera', + 'si-prefix-symbol-peta', + 'si-prefix-symbol-exa', + 'si-prefix-symbol-zetta', + 'si-prefix-symbol-yotta' +} +---formats a number into the amount of energy. Requires 'W' or 'J' as the second parameter +---@param energy number +---@param watts_or_joules string +py.format_energy = function(energy, watts_or_joules) + if watts_or_joules == 'W' then + watts_or_joules = 'si-unit-symbol-watt' + energy = energy * 60 + elseif watts_or_joules == 'J' then + watts_or_joules = 'si-unit-symbol-joule' + else + error() + end + + local prefix = 0 + while energy >= 1000 do + energy = energy / 1000 + prefix = prefix + 1 + end + return {'' , string.format('%.1f', energy), ' ', si_prefixes[prefix] and {si_prefixes[prefix]} or '* 10^'..(prefix*3)..' ', {watts_or_joules}} end \ No newline at end of file diff --git a/lib/metas/entity.lua b/lib/metas/entity.lua index aa4b73e..5017965 100644 --- a/lib/metas/entity.lua +++ b/lib/metas/entity.lua @@ -6,6 +6,7 @@ local collision_mask_util = require '__core__/lualib/collision-mask-util' ---@field public remove_flag fun(self: data.EntityPrototype, flag: string): data.EntityPrototype ---@field public has_flag fun(self: data.EntityPrototype, flag: string): boolean +local entity_types = defines.prototypes.entity ENTITY = setmetatable({}, { ---@param entity data.EntityPrototype __call = function(self, entity) @@ -17,9 +18,12 @@ ENTITY = setmetatable({}, { end elseif etype == 'table' then if not entity.type then error('Tried to extend an entity ' .. entity.name .. ' without providing a type') end + if not entity_types[entity.type] then error('Tried to use ENTITY{} on a non-entity: ' .. entity.name) end + data:extend{entity} return entity:standardize() else error('Invalid type ' .. etype) end + error('Entity ' .. tostring(entity) .. ' does not exist') end }) diff --git a/lib/metas/fluid.lua b/lib/metas/fluid.lua index 8f782ff..cdbf441 100644 --- a/lib/metas/fluid.lua +++ b/lib/metas/fluid.lua @@ -4,7 +4,7 @@ FLUID = setmetatable(data.raw.fluid, { local ftype = type(fluid) if ftype == 'string' then fluid = data.raw.fluid[fluid] - if not fluid then error('Fluid ' .. fluid .. ' does not exist') end + if not fluid then error('Fluid ' .. tostring(fluid) .. ' does not exist') end elseif ftype == 'table' then fluid.type = 'fluid' data:extend{fluid} diff --git a/lib/metas/item.lua b/lib/metas/item.lua index 24b22d3..e70bdf9 100644 --- a/lib/metas/item.lua +++ b/lib/metas/item.lua @@ -3,6 +3,7 @@ ---@field public remove_flag fun(self: data.ItemPrototype, flag: string): data.ItemPrototype ---@field public has_flag fun(self: data.ItemPrototype, flag: string): boolean +local item_prototypes = defines.prototypes.item ITEM = setmetatable({}, { ---@param item data.ItemPrototype __call = function(self, item) @@ -14,9 +15,12 @@ ITEM = setmetatable({}, { end elseif itype == 'table' then if not item.type then error('Tried to extend an item ' .. item.name .. ' without providing a type') end + if not item_prototypes[item.type] then error('Tried to use ITEM{} on a non-item: ' .. item.name) end + data:extend{item} return item else error('Invalid type ' .. itype) end + error('Item ' .. tostring(item) .. ' does not exist') end }) diff --git a/lib/metas/metas.lua b/lib/metas/metas.lua index fd4a439..27ae140 100644 --- a/lib/metas/metas.lua +++ b/lib/metas/metas.lua @@ -55,6 +55,7 @@ for ptype in pairs(defines.prototypes.item) do metas[ptype] = {__index = lib.item} end metas.fluid = {__index = lib.fluid} +metas.tile = {__index = lib.tile} for ptype, prototypes in pairs(data.raw) do local meta = metas[ptype] diff --git a/lib/metas/recipe.lua b/lib/metas/recipe.lua index 341d697..d5d6320 100644 --- a/lib/metas/recipe.lua +++ b/lib/metas/recipe.lua @@ -17,7 +17,7 @@ RECIPE = setmetatable(data.raw.recipe, { local rtype = type(recipe) if rtype == 'string' then recipe = data.raw.recipe[recipe] - if not recipe then error('Recipe ' .. recipe .. ' does not exist') end + if not recipe then error('Recipe ' .. tostring(recipe) .. ' does not exist') end elseif rtype == 'table' then recipe.type = 'recipe' data:extend{recipe} @@ -84,7 +84,11 @@ end py.allow_productivity = function(recipe_names) productivity_modules = nil for _, recipe_name in pairs(recipe_names) do - RECIPE(recipe_name):allow_productivity() + if data.raw.recipe[recipe_name] then + RECIPE(recipe_name):allow_productivity() + else + log('WARNING @ allow_productivity(): Recipe ' .. recipe_name .. ' does not exist') + end end end diff --git a/lib/metas/tile.lua b/lib/metas/tile.lua index 99a964b..a3c021f 100644 --- a/lib/metas/tile.lua +++ b/lib/metas/tile.lua @@ -4,7 +4,7 @@ TILE = setmetatable(data.raw.tile, { local ftype = type(tile) if ftype == 'string' then tile = data.raw.tile[tile] - if not tile then error('Tile ' .. tile .. ' does not exist') end + if not tile then error('Tile ' .. tostring(tile) .. ' does not exist') end elseif ftype == 'table' then tile.type = 'tile' data:extend{tile} From a6770e4f2096377fdfdc6e3047111f4ac9f37811 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Sat, 25 May 2024 22:53:01 -0500 Subject: [PATCH 04/13] move the last of the pycp lib into new system --- data-final-fixes.lua | 2 +- lib/autorecipes.lua | 59 ++++++++++++++++++++++ lib/data-stage.lua | 60 ++++++++++++++++++++++ lib/metas/fluid.lua | 4 +- lib/metas/metas.lua | 1 + lib/metas/recipe.lua | 69 ++++++++++++++++++++++++-- lib/metas/technology.lua | 6 +-- lib/metas/tile.lua | 4 +- prototypes/functions/compatibility.lua | 14 +++--- 9 files changed, 200 insertions(+), 19 deletions(-) diff --git a/data-final-fixes.lua b/data-final-fixes.lua index e57e2c3..5cf300e 100644 --- a/data-final-fixes.lua +++ b/data-final-fixes.lua @@ -193,7 +193,7 @@ local function create_tmp_tech(recipe, original_tech, add_dependency) } } - RECIPE(recipe):set_enabled(false) + RECIPE(recipe).enabled = false if original_tech then RECIPE(recipe):remove_unlock(original_tech) diff --git a/lib/autorecipes.lua b/lib/autorecipes.lua index a7ee290..6691add 100644 --- a/lib/autorecipes.lua +++ b/lib/autorecipes.lua @@ -1,3 +1,62 @@ +--((single mode example))-- +--[[ +py.autorecipes { -- is a function call can be many per file is the same as RECIPE{} that is used in the rest of pymods + name = 'single-example', -- recipe name if in single recipe mode *@* + category = 'recipe-category', -- used in input recipe and output if outcategory not provided to set category + singlerecipe = false, --=true: its a single recipe done 1 machine. takes ingredients and outputs the results. --=false: creates 2 recipes. 1 with the ingredients as inputs and outputs an item. 2nd recipe takes the item in and outputs the results. + module_limitations = "ulric", --adds the recipes to a modules allowed recipes table * + subgroup = 'subgroup', -- sets the recipes subgroups for menu organizion + order = 'order', -- sets order for menu organizion + mats = -- stuff needed for each recipe. + { + { + ingredients = -- duh, first time can not be empty or youll get an empty ingredients table + { + {name = 'name', amount = 'amount'*('*!*'), return_item={name='item', amount='amount'*}*('*&*')},-- a single ingredient + {'ingredient'}, -- see above for details + {'ingredient'}, -- see above. no limits to the number of ingredients beyond what the machine is set to + }, + results = -- double duh, same as ingredients first time cant be empty or you get nothing + { + {name='bones', amount = 'amount'*('*!*'),probability = 'probability'**, amount_min = 'amount_min'**'***', amount_max = 'amount_max'**'***'}, + {'result'}, -- see above for details + {'result'}, -- again not limited by this code to number of results + }, + icon = 'icon', --image used as part of a subicon for the item and output recipe* if not provided it will use the baseitems icon as the icon + crafting_speed = 130, -- sets crafting speed of input recipe for both single mode and dual mode. + tech = 'tech', -- tech that unlocks this recipe* + name = 'name' --gives the correct name in rendering, loved it. + }, + { + ingredients = -- same as above but can be empty to reuse the same ingredients as the recipe before this one + { + + }, + results = -- same as above but can be empty to reuse the same results as the recipe before this one + { + + }, + + }, + }, +} +--]] + +-- *: means this is not required for a recipe to work +-- *@*: is only used in single recipe mode as is (i.e. singlerecipe = true) dualmode adds a number +-- *#*: is only used in dual recipe mode (i.e. singlerecipe = false) +-- **: its all or nothing with these 3. if you use probability you have to use min and max +-- ***: amount min and max are optional as by default it is set to 1:1 which will give you perecent chance +-- *&*: return item allows you to set an item to be a result in the input recipe. amount is not need as it defaults to useing the same value as the item its a part of +--[[ +*!*: inggredients and results carry over from the top recipe down. amount can have a few possible entries. ++,-,*,/,R, and numbers. + +,-,*,/: if an item with the same name exist in the mats above this recipe it will preform the set math operation on that amount useing the new value (i.e. old amount + new amount). if this is a new item being added it will perform the math operation on the default value of the item from the items table. +R: will clear this entry from the ingredients/results table it is in +numbers: sets amount to this value no matter what it was before +]]-- +--if you use the icon part its set to want a size 64 icon right now. if you want to use the size 32 ones ill need to add icon size in because i cant detect the icons size in the code + local function ensure_contiguous(tbl) if not tbl or type(tbl) ~= 'table' then return tbl end local contiguous_table = {} diff --git a/lib/data-stage.lua b/lib/data-stage.lua index 4b5a621..c54f43c 100644 --- a/lib/data-stage.lua +++ b/lib/data-stage.lua @@ -438,5 +438,65 @@ py.global_item_replacer = function(old, new, blackrecipe) end end +---adds a small icon to the top right corner of a recipe +---@param recipe data.RecipePrototype +---@param corner table +py.add_corner_icon_to_recipe = function(recipe, corner) + local icon, icon_size, icons + local result = recipe.main_product or recipe.result or recipe.results[1][1] or recipe.results[1].name + + -- Icon size finder + if recipe.icon_size ~= nil then + icon_size = recipe.icon_size + else + icon_size = 32 -- Set default to 32 + end + + -- Icon finder + if recipe.icon ~= nil then -- Found an icon + icon = recipe.icon + end + + if icon == nil then -- (i.e. not found above) + -- Find it from result icon + icon = table.deepcopy(data.raw.item[result].icon) + + -- Confirm icon_size + if data.raw.item[result] and data.raw.item[result].icon_size ~= nil then + icon_size = data.raw.item[result].icon_size + end + end + + if recipe.icons then -- If it's already an icons + icons = recipe.icons + icons[#icons + 1] = corner + elseif data.raw.item[result] and data.raw.item[result].icons then + icons = table.deepcopy(data.raw.item[result].icons) + icons[#icons + 1] = corner + else -- No icons table, use icon found above + if icon == nil then + icon = '__base__/graphics/icons/blueprint.png' + end -- Fallback + + icons = { + {icon = icon, icon_size = icon_size}, + corner + } + end + + -- Ensure icon sizes are installed in each icon level + for i, icon in pairs(icons) do + if not icon.icon_size then + if i == 1 then -- Allow first one to inherit, set all others to 32 + icon.icon_size = icon_size or 32 + else + icon.icon_size = 32 + end + end + end + + return icons +end + ---@diagnostic disable-next-line: duplicate-set-field py.on_event = function() end \ No newline at end of file diff --git a/lib/metas/fluid.lua b/lib/metas/fluid.lua index cdbf441..9b90bf4 100644 --- a/lib/metas/fluid.lua +++ b/lib/metas/fluid.lua @@ -3,8 +3,8 @@ FLUID = setmetatable(data.raw.fluid, { __call = function(self, fluid) local ftype = type(fluid) if ftype == 'string' then - fluid = data.raw.fluid[fluid] - if not fluid then error('Fluid ' .. tostring(fluid) .. ' does not exist') end + if not self[fluid] then error('Fluid ' .. tostring(fluid) .. ' does not exist') end + fluid = self[fluid] elseif ftype == 'table' then fluid.type = 'fluid' data:extend{fluid} diff --git a/lib/metas/metas.lua b/lib/metas/metas.lua index 27ae140..a7bf944 100644 --- a/lib/metas/metas.lua +++ b/lib/metas/metas.lua @@ -21,6 +21,7 @@ for _, meta in pairs(lib) do local copy = table.deepcopy(self) if type(new_name) == 'function' then new_name = new_name(copy) end copy.name = new_name + data:extend{copy} return setmetatable(copy, getmetatable(self)) end diff --git a/lib/metas/recipe.lua b/lib/metas/recipe.lua index d5d6320..ac1e4f5 100644 --- a/lib/metas/recipe.lua +++ b/lib/metas/recipe.lua @@ -10,14 +10,18 @@ ---@field public add_result fun(self: data.RecipePrototype, result: string | data.ProductPrototype): data.RecipePrototype ---@field public remove_result fun(self: data.RecipePrototype, result_name: string): data.RecipePrototype ---@field public clear_ingredients fun(self: data.RecipePrototype): data.RecipePrototype +---@field public multiply_result_amount fun(self: data.RecipePrototype, result_name: string, percent: number): data.RecipePrototype +---@field public multiply_ingredient_amount fun(self: data.RecipePrototype, ingredient_name: string, percent: number): data.RecipePrototype +---@field public add_result_amount fun(self: data.RecipePrototype, result_name: string, increase: number): data.RecipePrototype +---@field public add_ingredient_amount fun(self: data.RecipePrototype, ingredient_name: string, increase: number): data.RecipePrototype RECIPE = setmetatable(data.raw.recipe, { ---@param recipe data.RecipePrototype __call = function(self, recipe) local rtype = type(recipe) if rtype == 'string' then - recipe = data.raw.recipe[recipe] - if not recipe then error('Recipe ' .. tostring(recipe) .. ' does not exist') end + if not self[recipe] then error('Recipe ' .. tostring(recipe) .. ' does not exist') end + recipe = self[recipe] elseif rtype == 'table' then recipe.type = 'recipe' data:extend{recipe} @@ -90,6 +94,10 @@ py.allow_productivity = function(recipe_names) log('WARNING @ allow_productivity(): Recipe ' .. recipe_name .. ' does not exist') end end + + for module in pairs(productivity_modules or {}) do + module.limitation = table.dedupe(module.limitation) + end end metas.add_unlock = function(self, technology_name) @@ -102,7 +110,7 @@ metas.add_unlock = function(self, technology_name) local technology = data.raw.technology[technology_name] if not technology then - log('WARNING @ recipe:add_unlock(): Technology ' .. technology_name .. ' does not exist') + log('WARNING @ \'' .. self.name .. '\':add_unlock(): Technology ' .. technology_name .. ' does not exist') return self end @@ -125,7 +133,7 @@ metas.remove_unlock = function(self, technology_name) local technology = data.raw.technology[technology_name] if not technology then - log('WARNING @ recipe:remove_unlock(): Technology ' .. technology_name .. ' does not exist') + log('WARNING @ \'' .. self.name .. '\':remove_unlock(): Technology ' .. technology_name .. ' does not exist') return self end @@ -221,4 +229,57 @@ metas.clear_ingredients = function(self) return self end +metas.multiply_result_amount = function(self, result_name, percent) + self:standardize() + + for _, result in pairs(self.results) do + if result.name == result_name then + local amount = result.amount or (result.amount_min + result.amount_max) / 2 + result.amount = math.ceil(amount * percent) + return self + end + end + + error('Result ' .. result_name .. ' not found in recipe ' .. self.name) +end + +metas.multiply_ingredient_amount = function(self, ingredient_name, percent) + self:standardize() + + for _, ingredient in pairs(self.ingredients) do + if ingredient.name == ingredient_name then + ingredient.amount = math.ceil(ingredient.amount * percent) + return self + end + end + + error('Ingredient ' .. ingredient_name .. ' not found in recipe ' .. self.name) +end + +metas.add_result_amount = function(self, result_name, increase) + self:standardize() + + for _, result in pairs(self.results) do + if result.name == result_name then + result.amount = result.amount + increase + return self + end + end + + error('Result ' .. result_name .. ' not found in recipe ' .. self.name) +end + +metas.add_ingredient_amount = function(self, ingredient_name, increase) + self:standardize() + + for _, ingredient in pairs(self.ingredients) do + if ingredient.name == ingredient_name then + ingredient.amount = ingredient.amount + increase + return self + end + end + + error('Ingredient ' .. ingredient_name .. ' not found in recipe ' .. self.name) +end + return metas \ No newline at end of file diff --git a/lib/metas/technology.lua b/lib/metas/technology.lua index a927cdb..f9aee96 100644 --- a/lib/metas/technology.lua +++ b/lib/metas/technology.lua @@ -11,8 +11,8 @@ TECHNOLOGY = setmetatable(data.raw.technology, { __call = function(self, technology) local ttype = type(technology) if ttype == 'string' then - technology = data.raw.technology[technology] - if not technology then error('Technology ' .. technology .. ' does not exist') end + if not self[technology] then error('Technology ' .. technology .. ' does not exist') end + technology = self[technology] elseif ttype == 'table' then technology.type = 'technology' data:extend{technology} @@ -52,7 +52,7 @@ end metas.add_prereq = function(self, prereq_technology_name) local prereq_technology = data.raw.technology[prereq_technology_name] if not prereq_technology then - log('WARNING @ technology:add_prereq(): Technology ' .. prereq_technology_name .. ' does not exist') + log('WARNING @ \'' .. self.name .. '\':add_prereq(): Technology ' .. prereq_technology_name .. ' does not exist') return self end diff --git a/lib/metas/tile.lua b/lib/metas/tile.lua index a3c021f..2e42ec4 100644 --- a/lib/metas/tile.lua +++ b/lib/metas/tile.lua @@ -3,8 +3,8 @@ TILE = setmetatable(data.raw.tile, { __call = function(self, tile) local ftype = type(tile) if ftype == 'string' then - tile = data.raw.tile[tile] - if not tile then error('Tile ' .. tostring(tile) .. ' does not exist') end + if not self[tile] then error('Tile ' .. tostring(tile) .. ' does not exist') end + tile = self[tile] elseif ftype == 'table' then tile.type = 'tile' data:extend{tile} diff --git a/prototypes/functions/compatibility.lua b/prototypes/functions/compatibility.lua index 16376eb..a1c0cdd 100644 --- a/prototypes/functions/compatibility.lua +++ b/prototypes/functions/compatibility.lua @@ -85,7 +85,7 @@ if mods['deadlock_stacked_recipes'] then end if mods['LightedPolesPlus'] then - RECIPE('lighted-small-electric-pole'):add_unlock('optics'):remove_unlock('creosote'):set_enabled(false) + RECIPE('lighted-small-electric-pole'):add_unlock('optics'):remove_unlock('creosote').enabled = false if mods['pyalternativeenergy'] then RECIPE('lighted-medium-electric-pole'):remove_unlock('optics'):add_unlock('electric-energy-distribution-1') RECIPE('lighted-big-electric-pole'):remove_unlock('optics'):add_unlock('electric-energy-distribution-2') @@ -386,12 +386,12 @@ if mods['yi_railway'] and mods['pyindustry'] then end if mods['Rocket-Silo-Construction'] then - RECIPE('rsc-construction-stage1'):set_enabled(false):add_unlock('rocket-silo') - RECIPE('rsc-construction-stage2'):set_enabled(false):add_unlock('rocket-silo') - RECIPE('rsc-construction-stage3'):set_enabled(false):add_unlock('rocket-silo') - RECIPE('rsc-construction-stage4'):set_enabled(false):add_unlock('rocket-silo') - RECIPE('rsc-construction-stage5'):set_enabled(false):add_unlock('rocket-silo') - RECIPE('rsc-construction-stage6'):set_enabled(false):add_unlock('rocket-silo') + RECIPE('rsc-construction-stage1').enabled = false:add_unlock('rocket-silo') + RECIPE('rsc-construction-stage2').enabled = false:add_unlock('rocket-silo') + RECIPE('rsc-construction-stage3').enabled = false:add_unlock('rocket-silo') + RECIPE('rsc-construction-stage4').enabled = false:add_unlock('rocket-silo') + RECIPE('rsc-construction-stage5').enabled = false:add_unlock('rocket-silo') + RECIPE('rsc-construction-stage6').enabled = false:add_unlock('rocket-silo') if mods['pyindustry'] and mods['pycoalprocessing'] then RECIPE('rsc-excavation-site'):replace_ingredient('pipe', 'niobium-pipe') From f821de74db0abddc253b39c993d2c967e8d49003 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Sun, 26 May 2024 13:12:02 -0500 Subject: [PATCH 05/13] Move effectivity module lib into pypp --- data-updates.lua | 2 +- lib/control-stage.lua | 8 +++ lib/data-stage.lua | 22 +++++++++ lib/metas/metas.lua | 16 ++++-- lib/metas/recipe.lua | 16 ++++-- lib/placement-restriction.lua | 68 -------------------------- locale/en/locale.cfg | 5 +- prototypes/functions/compatibility.lua | 12 ++--- 8 files changed, 63 insertions(+), 86 deletions(-) delete mode 100644 lib/placement-restriction.lua diff --git a/data-updates.lua b/data-updates.lua index 7f5717e..9025039 100644 --- a/data-updates.lua +++ b/data-updates.lua @@ -5,7 +5,7 @@ local function set_underground_recipe(underground, belt, prev_underground, prev_ if prev_underground then prev_dist = data.raw['underground-belt'][prev_underground].max_distance + 1 local recipe = data.raw.recipe[belt]:standardize() - local belt_count = recipe[1].amount + local belt_count = recipe.ingredients[1].amount local fluid = false for _, ing in pairs(recipe.ingredients) do diff --git a/lib/control-stage.lua b/lib/control-stage.lua index 6efcce2..a47a7cc 100644 --- a/lib/control-stage.lua +++ b/lib/control-stage.lua @@ -180,4 +180,12 @@ py.format_energy = function(energy, watts_or_joules) prefix = prefix + 1 end return {'' , string.format('%.1f', energy), ' ', si_prefixes[prefix] and {si_prefixes[prefix]} or '* 10^'..(prefix*3)..' ', {watts_or_joules}} +end + +---Returns the distance from 0,0 +---@param x number +---@param y number +---@return number +py.distance = function(x, y) + return (x ^ 2 + y ^ 2) ^ 0.5 end \ No newline at end of file diff --git a/lib/data-stage.lua b/lib/data-stage.lua index c54f43c..f50d37b 100644 --- a/lib/data-stage.lua +++ b/lib/data-stage.lua @@ -498,5 +498,27 @@ py.add_corner_icon_to_recipe = function(recipe, corner) return icons end +---Blocks green modules from being used in an array of recipes categories +---@param recipe_categories string[] +py.disallow_effectivity = function(recipe_categories) + local modules = {} + for _, module in pairs(data.raw.module) do + if module.name:find('effectivity') then + table.insert(modules, module) + module.limitation_blacklist = module.limitation_blacklist or {} + if not module.limitation_message_key then module.limitation_message_key = 'effectivity-module-limitation-message' end + end + end + + recipe_categories = table.invert(recipe_categories) + for _, recipe in pairs(data.raw.recipe) do + if recipe_categories[recipe.category] then + for _, module in pairs(modules) do + table.insert(module.limitation_blacklist, recipe.name) + end + end + end +end + ---@diagnostic disable-next-line: duplicate-set-field py.on_event = function() end \ No newline at end of file diff --git a/lib/metas/metas.lua b/lib/metas/metas.lua index a7bf944..e3e44f3 100644 --- a/lib/metas/metas.lua +++ b/lib/metas/metas.lua @@ -10,18 +10,20 @@ local lib = { } ---@class data.AnyPrototype ----@field public copy fun(self: data.AnyPrototype, new_name: string | fun(self: data.AnyPrototype): string): data.AnyPrototype +---@field public copy fun(self: data.AnyPrototype, new_name: (string | fun(self: data.AnyPrototype): string)?): data.AnyPrototype ---@field public subgroup_order fun(self: data.AnyPrototype, subgroup: string, order: string): data.AnyPrototype ---@field public set_fields fun(self: data.AnyPrototype, fields: table): data.AnyPrototype ---@field public set fun(self: data.AnyPrototype, field: string, value: any): data.AnyPrototype +---@field public delete fun(self: data.AnyPrototype) for _, meta in pairs(lib) do meta.copy = function(self, new_name) - if not new_name then error('No new name provided') end local copy = table.deepcopy(self) - if type(new_name) == 'function' then new_name = new_name(copy) end - copy.name = new_name - data:extend{copy} + if new_name then + if type(new_name) == 'function' then new_name = new_name(copy) end + copy.name = new_name + data:extend{copy} + end return setmetatable(copy, getmetatable(self)) end @@ -44,6 +46,10 @@ for _, meta in pairs(lib) do self[field] = value return self end + + meta.delete = function(self) + data.raw[self.type][self.name] = nil + end end local metas = {} diff --git a/lib/metas/recipe.lua b/lib/metas/recipe.lua index ac1e4f5..8b29ac5 100644 --- a/lib/metas/recipe.lua +++ b/lib/metas/recipe.lua @@ -5,7 +5,7 @@ ---@field public remove_unlock fun(self: data.RecipePrototype, technology_name: string | string[]): data.RecipePrototype ---@field public replace_ingredient fun(self: data.RecipePrototype, old_ingredient: string, new_ingredient: string | data.IngredientPrototype, new_amount: integer?): data.RecipePrototype ---@field public add_ingredient fun(self: data.RecipePrototype, ingredient: string | data.IngredientPrototype): data.RecipePrototype ----@field public remove_ingredient fun(self: data.RecipePrototype, ingredient_name: string): data.RecipePrototype +---@field public remove_ingredient fun(self: data.RecipePrototype, ingredient_name: string): data.RecipePrototype, integer ---@field public replace_result fun(self: data.RecipePrototype, old_result: string, new_result: string | data.ProductPrototype, new_amount: integer?): data.RecipePrototype ---@field public add_result fun(self: data.RecipePrototype, result: string | data.ProductPrototype): data.RecipePrototype ---@field public remove_result fun(self: data.RecipePrototype, result_name: string): data.RecipePrototype @@ -236,11 +236,14 @@ metas.multiply_result_amount = function(self, result_name, percent) if result.name == result_name then local amount = result.amount or (result.amount_min + result.amount_max) / 2 result.amount = math.ceil(amount * percent) + result.amount_min = nil + result.amount_max = nil return self end end - error('Result ' .. result_name .. ' not found in recipe ' .. self.name) + log('WARNING @ \'' .. self.name .. '\':multiply_result_amount(): Result ' .. result_name .. ' not found') + return self end metas.multiply_ingredient_amount = function(self, ingredient_name, percent) @@ -253,7 +256,8 @@ metas.multiply_ingredient_amount = function(self, ingredient_name, percent) end end - error('Ingredient ' .. ingredient_name .. ' not found in recipe ' .. self.name) + log('WARNING @ \'' .. self.name .. '\':multiply_ingredient_amount(): Ingredient ' .. ingredient_name .. ' not found') + return self end metas.add_result_amount = function(self, result_name, increase) @@ -266,7 +270,8 @@ metas.add_result_amount = function(self, result_name, increase) end end - error('Result ' .. result_name .. ' not found in recipe ' .. self.name) + log('WARNING @ \'' .. self.name .. '\':add_result_amount(): Result ' .. result_name .. ' not found') + return self end metas.add_ingredient_amount = function(self, ingredient_name, increase) @@ -279,7 +284,8 @@ metas.add_ingredient_amount = function(self, ingredient_name, increase) end end - error('Ingredient ' .. ingredient_name .. ' not found in recipe ' .. self.name) + log('WARNING @ \'' .. self.name .. '\':add_ingredient_amount(): Ingredient ' .. ingredient_name .. ' not found') + return self end return metas \ No newline at end of file diff --git a/lib/placement-restriction.lua b/lib/placement-restriction.lua deleted file mode 100644 index ec7e3d2..0000000 --- a/lib/placement-restriction.lua +++ /dev/null @@ -1,68 +0,0 @@ -local function parse_restriction_condition(condition) - local function helper() - local type = condition.type - if type == 'placed-on' then - return {'placement-restriction.placed-on', {'entity-name.' .. condition.entity}} - elseif type == 'surface-type' then - return {'placement-restriction.surface-type', {'surface-type.' .. condition.surface_type}} - elseif type == 'surface-tag' then - return {'placement-restriction.surface-tag', {'surface-tag-name.' .. condition.tag}} - elseif type == 'distance' then - return {'placement-restriction.distance', {'surface-distance.' .. condition.distance}} - end - - -- greater than less than - local args - if condition.min_amount and condition.max_amount then - args = {'placement-restriction.' .. type .. '-3', condition.min_amount, condition.max_amount} - elseif condition.max_amount then - args = {'placement-restriction.' .. type .. '-2', condition.max_amount} - elseif condition.min_amount then - args = {'placement-restriction.' .. type .. '-1', condition.min_amount} - else - error('min_amount or max_amount missing from placement restriction of type: ' .. type) - end - - if type == 'atmosphere' then - if not condition.gas then error('No gas provided for atomspheric condition') end - if not data.raw.fluid[condition.gas] then error('Invalid gas: ' .. condition.gas) end - for i = 2, #args do - args[i] = args[i] * 100 - end - table.insert(args, 2, {data.raw.fluid[condition.gas].localised_name or ('fluid-name.' .. condition.gas)}) - table.insert(args, 2, '[fluid=' .. condition.gas .. ']') - end - - return args - end - - local localised_string = helper() - if condition.NOT then localised_string = {'placement-restriction.not', localised_string} end - return localised_string -end - -local function placement_restriction_description_helper(i, restriction, parens) - if i == #restriction then - if data then - return {'placement-restriction.dot', parse_restriction_condition(restriction[i])} - else - return parse_restriction_condition(restriction[i]) - end - end - return { - parens, - parse_restriction_condition(restriction[i]), - {'placement-restriction.' .. restriction[i + 1]}, - placement_restriction_description_helper(i + 2, restriction, parens) - } -end - -py.placement_restriction_description = function(restriction) - if #restriction % 2 == 0 then error('Placement restriction length must be odd') end - local parens = data and 'placement-restriction.parens-dot' or 'placement-restriction.parens' - return {'placement-restriction.header', placement_restriction_description_helper(1, restriction, parens)} -end - -py.distance = function(x, y) - return (x ^ 2 + y ^ 2) ^ 0.5 -end \ No newline at end of file diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg index 4d10e77..73e9a98 100644 --- a/locale/en/locale.cfg +++ b/locale/en/locale.cfg @@ -19,4 +19,7 @@ consistency-check-complete=Technology verification finished, have a nice day. yarm-remote-viewer=Character [recipe-description] -affected-by-productivity=[font=default-semibold][color=255,230,192]Affected by productivity[/color][/font] \ No newline at end of file +affected-by-productivity=[font=default-semibold][color=255,230,192]Affected by productivity[/color][/font] + +[item-limitation] +effectivity-module-limitation-message=Effectivity modules cannot be used in this machine. \ No newline at end of file diff --git a/prototypes/functions/compatibility.lua b/prototypes/functions/compatibility.lua index a1c0cdd..1139ec7 100644 --- a/prototypes/functions/compatibility.lua +++ b/prototypes/functions/compatibility.lua @@ -386,12 +386,12 @@ if mods['yi_railway'] and mods['pyindustry'] then end if mods['Rocket-Silo-Construction'] then - RECIPE('rsc-construction-stage1').enabled = false:add_unlock('rocket-silo') - RECIPE('rsc-construction-stage2').enabled = false:add_unlock('rocket-silo') - RECIPE('rsc-construction-stage3').enabled = false:add_unlock('rocket-silo') - RECIPE('rsc-construction-stage4').enabled = false:add_unlock('rocket-silo') - RECIPE('rsc-construction-stage5').enabled = false:add_unlock('rocket-silo') - RECIPE('rsc-construction-stage6').enabled = false:add_unlock('rocket-silo') + RECIPE('rsc-construction-stage1'):add_unlock('rocket-silo').enabled = false + RECIPE('rsc-construction-stage2'):add_unlock('rocket-silo').enabled = false + RECIPE('rsc-construction-stage3'):add_unlock('rocket-silo').enabled = false + RECIPE('rsc-construction-stage4'):add_unlock('rocket-silo').enabled = false + RECIPE('rsc-construction-stage5'):add_unlock('rocket-silo').enabled = false + RECIPE('rsc-construction-stage6'):add_unlock('rocket-silo').enabled = false if mods['pyindustry'] and mods['pycoalprocessing'] then RECIPE('rsc-excavation-site'):replace_ingredient('pipe', 'niobium-pipe') From 64e4aa457c7f16e0d8a6dc794ba1ff71ea40f086 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Sun, 26 May 2024 13:19:49 -0500 Subject: [PATCH 06/13] Fix defines.color --- lib/defines.lua | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/defines.lua b/lib/defines.lua index aafe8eb..5349b08 100644 --- a/lib/defines.lua +++ b/lib/defines.lua @@ -159,13 +159,4 @@ defines.color = { whitesmoke = { r = 0.9608, g = 0.9608, b = 0.9608, a = 1 }, yellow = { r = 1.0000, g = 1.0000, b = 0.0000, a = 1 }, yellowgreen = { r = 0.6039, g = 0.8039, b = 0.1961, a = 1 } -} - -defines.color = {} - -for _, color in pairs(defines.color) do - color.r = color.r or 1 - color.g = color.g or 1 - color.b = color.b or 1 - color.a = color.a or 1 -end \ No newline at end of file +} \ No newline at end of file From 22bc03674fe8e3c5afa7566fc5172daf4ed05145 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Sun, 26 May 2024 19:49:26 -0500 Subject: [PATCH 07/13] Improve error messaging and logging --- lib/data-stage.lua | 18 +++++++++++++++--- lib/metas/entity.lua | 7 +++++++ lib/metas/item.lua | 7 +++++++ lib/metas/recipe.lua | 10 +++++++++- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/data-stage.lua b/lib/data-stage.lua index f50d37b..1208fb9 100644 --- a/lib/data-stage.lua +++ b/lib/data-stage.lua @@ -379,12 +379,24 @@ py.pipe_covers = function(n, s, w, e) end ---Standardizes a product or ingredient prototype to a common format. ----@param p data.IngredientPrototype | data.ProductPrototype +---@param p data.IngredientPrototype | data.ProductPrototype | string ---@return data.IngredientPrototype | data.ProductPrototype py.standardize_product = function(p) + if type(p) == 'string' then p = {p, 1} end + local name = p.name or p[1] + local type = p.type + if not type and name then + if data.raw.fluid[name] then + log(name) + type = 'fluid' + else + type = 'item' + end + end + return { - type = p.type or 'item', - name = p.name or p[1], + type = type, + name = name, amount = p.amount or p[2], probability = p.probability, amount_min = p.amount_min, diff --git a/lib/metas/entity.lua b/lib/metas/entity.lua index 5017965..646a7df 100644 --- a/lib/metas/entity.lua +++ b/lib/metas/entity.lua @@ -24,6 +24,13 @@ ENTITY = setmetatable({}, { return entity:standardize() else error('Invalid type ' .. etype) end error('Entity ' .. tostring(entity) .. ' does not exist') + end, + __index = function(self, entity_name) + for ptype in pairs(defines.prototypes.entity) do + local result = data.raw[ptype][entity_name] + if result then return result:standardize() end + end + return nil end }) diff --git a/lib/metas/item.lua b/lib/metas/item.lua index e70bdf9..7b0927f 100644 --- a/lib/metas/item.lua +++ b/lib/metas/item.lua @@ -21,6 +21,13 @@ ITEM = setmetatable({}, { return item else error('Invalid type ' .. itype) end error('Item ' .. tostring(item) .. ' does not exist') + end, + __index = function(self, item_name) + for ptype in pairs(defines.prototypes.item) do + local result = data.raw[ptype][item_name] + if result then return result end + end + return nil end }) diff --git a/lib/metas/recipe.lua b/lib/metas/recipe.lua index 8b29ac5..7c8ce64 100644 --- a/lib/metas/recipe.lua +++ b/lib/metas/recipe.lua @@ -180,13 +180,21 @@ do metas.replace_result = function(self, old_result, new_result) self:standardize() replacement_helper(self.results, old_result, new_result, new_amount) + if self.main_product == old_result then + self.main_product = type(new_result) == 'string' and new_result or new_result[1] or new_result.name + end return self end end metas.add_ingredient = function(self, ingredient) self:standardize() - table.insert(self.ingredients, py.standardize_product(ingredient)) + ingredient = py.standardize_product(ingredient) + if not FLUID[ingredient.name] and not ITEM[ingredient.name] then + log('WARNING @ \'' .. self.name .. '\':add_ingredient(): Ingredient ' .. ingredient.name .. ' does not exist') + else + table.insert(self.ingredients, ingredient) + end return self end From df7fe9a9c64b4491c577cea553841c7b184cf8b4 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Sun, 26 May 2024 20:34:28 -0500 Subject: [PATCH 08/13] Stop the events lib from being loaded in data stage --- lib/control-stage.lua | 2 ++ lib/data-stage.lua | 1 - lib/events.lua | 6 ++++++ lib/lib.lua | 1 - 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/control-stage.lua b/lib/control-stage.lua index a47a7cc..4f99da5 100644 --- a/lib/control-stage.lua +++ b/lib/control-stage.lua @@ -2,6 +2,8 @@ local random = math.random +require 'events' + ---Draws a red error icon at the entity's position. ---@param entity LuaEntity ---@param sprite string diff --git a/lib/data-stage.lua b/lib/data-stage.lua index 1208fb9..a7f9ded 100644 --- a/lib/data-stage.lua +++ b/lib/data-stage.lua @@ -387,7 +387,6 @@ py.standardize_product = function(p) local type = p.type if not type and name then if data.raw.fluid[name] then - log(name) type = 'fluid' else type = 'item' diff --git a/lib/events.lua b/lib/events.lua index a817be2..917569a 100644 --- a/lib/events.lua +++ b/lib/events.lua @@ -63,7 +63,10 @@ local function one_function_from_many(functions) end end +local finalized = false py.finalize_events = function() + if finalized then error('Events already finalized') end + local i = 0 for event, functions in pairs(events) do local f = one_function_from_many(functions) if type(event) == 'number' then @@ -74,7 +77,10 @@ py.finalize_events = function() else script.on_event(tonumber(event) or event, f) end + i = i + 1 end + finalized = true + log('Finalized ' .. i .. ' events for ' .. script.mod_name) end _G.gui_events = { diff --git a/lib/lib.lua b/lib/lib.lua index aa9f5d2..d0013ce 100644 --- a/lib/lib.lua +++ b/lib/lib.lua @@ -4,7 +4,6 @@ require 'table' require 'string' require 'defines' require 'color' -require 'events' require 'world-generation' if data and data.raw and not data.raw.item['iron-plate'] then From b48c510ae1b924368d7269d00733de9cd2fac612 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Sun, 26 May 2024 20:52:52 -0500 Subject: [PATCH 09/13] Set enabled=false when using recipe:add_unlock --- lib/metas/recipe.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/metas/recipe.lua b/lib/metas/recipe.lua index 7c8ce64..ab2c68b 100644 --- a/lib/metas/recipe.lua +++ b/lib/metas/recipe.lua @@ -120,6 +120,8 @@ metas.add_unlock = function(self, technology_name) table.insert(technology.effects, {type = 'unlock-recipe', recipe = self.name}) + self.enabled = false + return self end From 33d80c6fed99a19fe441791e853d8be2c38e1296 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Mon, 27 May 2024 01:04:52 -0500 Subject: [PATCH 10/13] Fix crash --- lib/events.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/events.lua b/lib/events.lua index 917569a..5afb6c6 100644 --- a/lib/events.lua +++ b/lib/events.lua @@ -136,6 +136,7 @@ function py.execute_later(function_key, ticks, ...) global._delayed_functions[script.register_on_entity_destroyed(flying_text)] = {function_key, ticks - highest, {...}} end py.on_event(defines.events.on_entity_destroyed, function(event) + if not global._delayed_functions then return end local data = global._delayed_functions[event.registration_number] if not data then return end global._delayed_functions[event.registration_number] = nil From 1cf2b6d5dcc0f913e94646434270d884a46f09fe Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Mon, 27 May 2024 14:29:13 -0500 Subject: [PATCH 11/13] Fix fluidbox_index not being preserved in py.standardize_product --- lib/data-stage.lua | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/lib/data-stage.lua b/lib/data-stage.lua index a7f9ded..07d2a2f 100644 --- a/lib/data-stage.lua +++ b/lib/data-stage.lua @@ -384,27 +384,20 @@ end py.standardize_product = function(p) if type(p) == 'string' then p = {p, 1} end local name = p.name or p[1] - local type = p.type - if not type and name then + if not p.type and name then if data.raw.fluid[name] then - type = 'fluid' + p.type = 'fluid' else - type = 'item' + p.type = 'item' end end - return { - type = type, - name = name, - amount = p.amount or p[2], - probability = p.probability, - amount_min = p.amount_min, - amount_max = p.amount_max, - catalyst_amount = p.catalyst_amount, - temperature = p.temperature, - min_temperature = p.minimum_temperature, - max_temperature = p.maximum_temperature - } + p.name = name + p.amount = p.amount or p[2] or 1 + p[1] = nil + p[2] = nil + + return p end ---Returns an iterator through all prototypes of a given supertype. From 3f015cc7a09d53a77f5526d52590ede6e56c6a5e Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Mon, 27 May 2024 16:26:00 -0500 Subject: [PATCH 12/13] Fix ticked delay --- lib/data-stage.lua | 12 ++++++++++++ lib/events.lua | 10 +++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/data-stage.lua b/lib/data-stage.lua index 07d2a2f..38b2181 100644 --- a/lib/data-stage.lua +++ b/lib/data-stage.lua @@ -524,5 +524,17 @@ py.disallow_effectivity = function(recipe_categories) end end +local delays = {} +for i = 0, 20 do + local n = 2 ^ i + delays[#delays + 1] = { + name = 'py-ticked-script-delay-' .. n, + type = 'flying-text', + time_to_live = n, + speed = 0, + } +end +data:extend(delays) + ---@diagnostic disable-next-line: duplicate-set-field py.on_event = function() end \ No newline at end of file diff --git a/lib/events.lua b/lib/events.lua index 5afb6c6..1e262e4 100644 --- a/lib/events.lua +++ b/lib/events.lua @@ -1,8 +1,3 @@ -local powers_of_two = {} -for i = 0, 20 do - powers_of_two[i] = 2 ^ i -end - if data then local delays = {} for _, n in pairs(powers_of_two) do @@ -63,6 +58,11 @@ local function one_function_from_many(functions) end end +local powers_of_two = {} +for i = 0, 20 do + powers_of_two[i] = 2 ^ i +end + local finalized = false py.finalize_events = function() if finalized then error('Events already finalized') end From 4f5289da29adf81d0edad5380737efaabea08f52 Mon Sep 17 00:00:00 2001 From: notnotmelon Date: Mon, 27 May 2024 20:43:32 -0500 Subject: [PATCH 13/13] Move empty image into pypp to prevent crashes when running without pyse --- empty.png | Bin 0 -> 384 bytes lib/data-stage.lua | 2 +- lib/table.lua | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 empty.png diff --git a/empty.png b/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..e869d5978a02d52b1a04c5abcdf4c749c7aaebc2 GIT binary patch literal 384 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G!U;i$lZxy-8q?;Kn_c~qpu?a z!^VE@KZ&eBK4*bPWHAE+w;%{J)~Ccz1ZoiVba4!+hmdKI;Vst0F}BObpQYW literal 0 HcmV?d00001 diff --git a/lib/data-stage.lua b/lib/data-stage.lua index 38b2181..c0c0bb6 100644 --- a/lib/data-stage.lua +++ b/lib/data-stage.lua @@ -7,7 +7,7 @@ require 'autorecipes' ---@return table py.empty_image = function() return { - filename = '__pystellarexpeditiongraphics__/graphics/empty.png', + filename = '__pypostprocessing__/empty.png', size = 1, priority = 'high', direction_count = 1, diff --git a/lib/table.lua b/lib/table.lua index 52d8538..d0fa6e9 100644 --- a/lib/table.lua +++ b/lib/table.lua @@ -166,7 +166,7 @@ local function shuffle(t) return keys end ----Like normal pairs(), but in randomized order +---Like normal pairs(), but in deterministic randomized order ---@param t table ---@return fun():any, any function py.shuffled_pairs(t)