Module:GameData: Difference between revisions

From the Dyson Sphere Program Wiki
(Ensure Technologies are Title Cased (Curse you Polymer chemical engineering!))
(Add Dark Fog drop to list of recipes making items)
 
(13 intermediate revisions by the same user not shown)
Line 11: Line 11:
Fractionate='Fractionator',
Fractionate='Fractionator',
Research='Matrix Lab',
Research='Matrix Lab',
Mine='Mining Machine',
Gas='Orbital Collector',
Ocean='Water Pump',
Oil='Oil Extractor',
}
}
local OilSeepID = 7
-- exported functions
-- recipesMaking
-- print out table rows of all recipes making an item with full information
-- the recipe, the building, technology to unlock it, etc.


function funcs.recipesMaking(frame)
function funcs.recipesMaking(frame)
name = frame:getParent().args[1]
return recipesMakingName(frame:getParent().args[1], frame)
item = itemByName(name)
recipes = recipesMakingByID(item.ID)
return tableRecipes(recipes, frame)
end
end


function funcs.recipesMakingDirect(frame)
function funcs.recipesMakingDirect(frame)
name = frame.args[1]
return recipesMakingName(frame.args[1], frame)
item = itemByName(name)
recipes = recipesMakingByID(item.ID)
return tableRecipes(recipes, frame)
end
end
function recipesMakingName(name, frame)
local rtable = {
frame:expandTemplate{title='ProductionChainTable/head', args={}},
}
local item = itemByName(name)
for _, recipe in ipairs(recipesMakingByID(item.ID)) do
table.insert(rtable, recipeRow(recipe, frame))
end
local vein = veinMakingByID(item.ID)
if vein ~= nil then
table.insert(rtable, veinRow(item, vein, frame))
end
for _, giant in ipairs(deduplicateGiants(gasGiantsMakingByID(item.ID))) do
table.insert(rtable, gasGiantRow(item, giant, frame))
end
if isItemOcean(item.ID) then
table.insert(rtable, oceanRow(item, frame))
end
if isItemFogDrop(item) then
table.insert(rtable, fogDropRow(item, frame))
end
table.insert(rtable, '|}')
return table.concat(rtable, '\n')
end
-- recipesUsing
-- print out table rows of all recipes using an item with full information, like recipesMaking
-- split output into components and buildings


function funcs.recipesUsing(frame)
function funcs.recipesUsing(frame)
output = {}
return recipesUsingName(frame:getParent().args[1], frame)
name = frame:getParent().args[1]
end
item = itemByName(name)
 
components, buildings = recipesUsingByID(item.ID)
function funcs.recipesUsingDirect(frame)
return recipesUsingName(frame.args[1], frame)
end
 
function recipesUsingName(name, frame)
local output = {}
local header = frame:expandTemplate{title='ProductionChainTable/head', args={}}
local item = itemByName(name)
local components = {}
local buildings = {}
for _, r in ipairs(recipesUsingByID(item.ID)) do
if r.GridIndex >= 2000 then
table.insert(buildings, r)
else
table.insert(components, r)
end
end
if next(components) then
if next(components) then
table.insert(output, '=== Components ===')
if next(buildings) then
table.insert(output, tableRecipes(components, frame))
table.insert(output, '=== Components ===')
end
table.insert(output, header)
for _, recipe in ipairs(components) do
table.insert(output, recipeRow(recipe, frame))
end
table.insert(output, '|}')
end
end
if next(buildings) then
if next(buildings) then
table.insert(output, '=== Buildings ===')
if next(components) then
table.insert(output, tableRecipes(buildings, frame))
table.insert(output, '=== Buildings ===')
end
table.insert(output, header)
for _, recipe in ipairs(buildings) do
table.insert(output, recipeRow(recipe, frame))
end
table.insert(output, '|}')
end
end
return table.concat(output, '\n')
return table.concat(output, '\n')
end
end


function funcs.recipesUsingDirect(frame)
-- itemRecipes
output = {}
-- print out just recipe information on how to make an item
name = frame.args[1]
-- intended for use in ItemInfo boxes
item = itemByName(name)
 
components, buildings = recipesUsingByID(item.ID)
function funcs.itemRecipes(frame)
if next(components) then
return itemRecipesForName(frame:getParent().args[1], frame)
table.insert(output, '=== Components ===')
end
table.insert(output, tableRecipes(components, frame))
function funcs.itemRecipesDirect(frame)
return itemRecipesForName(frame.args[1], frame)
end
 
function itemRecipesForName(name, frame)
local item = itemByName(name)
local output = {}
for _, r in ipairs(recipesMakingByID(item.ID)) do
table.insert(output, itemRecipe(r, frame))
end
local vein = veinMakingByID(item.ID)
if vein then
table.insert(output, veinRecipe(item, vein, frame))
end
for _, giant in ipairs(deduplicateGiants(gasGiantsMakingByID(item.ID))) do
table.insert(output, gasGiantRecipe(giant, frame))
end
end
if next(buildings) then
if isItemOcean(item.ID) then
table.insert(output, '=== Buildings ===')
table.insert(output, oceanRecipe(item, frame))
table.insert(output, tableRecipes(buildings, frame))
end
end
return table.concat(output, '\n')
return table.concat(output, '\n')
end
end


function tableRecipes(recipes, frame)
-- itemField
result = {}
-- print a field from an item
for _, recipe in pairs(recipes) do
-- used to extract details about an item
itemdata = {}
 
itemdata.CraftTime = string.format('%s s', tostring(recipe.TimeSpend/60))
function funcs.itemField(frame)
for i, itemid in ipairs(recipe.Results) do
return itemFieldByName(frame:getParent().args[1], frame:getParent().args[2])
itemdata[string.format('Out%d', i)] = itemByID(itemid).Name
end
itemdata[string.format('Out%dQty', i)] = tostring(recipe.ResultCounts[i])
function funcs.itemFieldDirect(frame)
end
return itemFieldByName(frame.args[1], frame.args[2])
for i, itemid in ipairs(recipe.Items) do
end
itemdata[string.format('In%d', i)] = itemByID(itemid).Name
 
itemdata[string.format('In%dQty', i)] = tostring(recipe.ItemCounts[i])
function itemFieldByName(itemname, fieldname)
end
local item = itemByName(itemname)
data = {
return item[fieldname]
Building = machines[recipe.Type],
end
Recipe = frame:expandTemplate{title='ItemRecipe', args=itemdata},
 
-- make full rows and recipe data for tables
 
function recipeRow(recipe, frame)
local rdata = {
Building = titleCase(machines[recipe.Type]),
}
if recipe.Type == 'Fractionate' then
rdata.Recipe = fractionateRecipe(recipe, frame)
else
rdata.Recipe = itemRecipe(recipe, frame)
end
if recipe.Handcraft then
rdata.Replicator = 'Yes'
else
rdata.Replicator = 'No'
end
local tech = technologyForRecipeID(recipe.ID)
if tech ~= nil then
rdata.Technology = titleCase(tech.Name)
else
rdata.Technology = ''
end
local productive = not recipe.NonProductive
for i, itemid in ipairs(recipe.Items) do
productive = productive and itemByID(itemid).Productive
end
if productive then
rdata.Proliferator = 'Yes'
else
rdata.Proliferator = 'Speed'
end
return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=rdata}
end
 
function itemRecipe(recipe, frame)
local rdata = {
CraftTime = string.format('%s s', tostring(recipe.TimeSpend/60)),
}
for i, itemid in ipairs(recipe.Results) do
rdata[string.format('Out%d', i)] = titleCase(itemByID(itemid).Name)
rdata[string.format('Out%dQty', i)] = tostring(recipe.ResultCounts[i])
end
for i, itemid in ipairs(recipe.Items) do
rdata[string.format('In%d', i)] = titleCase(itemByID(itemid).Name)
rdata[string.format('In%dQty', i)] = tostring(recipe.ItemCounts[i])
end
return frame:expandTemplate{title='ItemRecipe', args=rdata}
end
 
function fractionateRecipe(recipe, frame)
local ratio = 100 * recipe.ResultCounts[1] / recipe.ItemCounts[1]
local rdata = {
CraftTime = tostring(ratio) .. '%',
}
for i, itemid in ipairs(recipe.Results) do
rdata[string.format('Out%d', i)] = titleCase(itemByID(itemid).Name)
rdata[string.format('Out%dQty', i)] = '1'
end
for i, itemid in ipairs(recipe.Items) do
rdata[string.format('In%d', i)] = titleCase(itemByID(itemid).Name)
rdata[string.format('In%dQty', i)] = '1'
end
return frame:expandTemplate{title='ItemRecipe', args=rdata}
end
 
function veinRow(item, vein, frame)
if vein.ID == OilSeepID then -- I don't have a better test for oil right now
local extractor = itemByName(machines.Oil)
local extractorTech = technologyForRecipeID(recipesMakingByID(extractor.ID)[1].ID)
local oildata = {
Building = titleCase(extractor.Name),
Replicator = 'No',
Technology = titleCase(extractorTech.Name),
Recipe = oilRecipe(item, vein, frame),
Proliferator='No',
}
return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=oildata}
else
local miner = itemByName(machines.Mine)
local minerTech = technologyForRecipeID(recipesMakingByID(miner.ID)[1].ID)
local veindata = {
Building = titleCase(miner.Name),
Replicator = 'Mine',
Technology = titleCase(minerTech.Name),
Recipe = veinRecipe(item, vein, frame),
Proliferator='No',
}
}
if recipe.Handcraft then
return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=veindata}
data.Replicator = 'Yes'
end
else
end
data.Replicator = 'No'
 
end
function veinRecipe(item, vein, frame)
tech = technologyForRecipeID(recipe.ID)
local vname = string.gsub(vein.Name, '%sVeins$', ' Vein')
if tech ~= nil then
local idata = {
data.Technology = titleCase(tech.Name)
In1 = titleCase(vname),
else
In1Qty = '1',
data.Technology = ''
Out1 = titleCase(item.Name),
end
Out1Qty = '1',
table.insert(result, '| ' .. frame:expandTemplate{title='ProductionChain', args=data})
CraftTime = '2 s',
}
return frame:expandTemplate{title='ItemRecipe', args=idata}
end
 
function oilRecipe(item, vein, frame)
local odata = {
In1 = titleCase(vein.Name),
In1Qty = '',
Out1 = titleCase(item.Name),
Out1Qty = '1',
CraftTime = '?',
}
return frame:expandTemplate{title='ItemRecipe', args=odata}
end
 
function gasGiantRow(item, giant, frame)
local collector = itemByName(machines.Gas)
local collectorTech = technologyForRecipeID(recipesMakingByID(collector.ID)[1].ID)
local giantdata = {
Building = titleCase(collector.Name),
Replicator = 'Mine',
Technology = titleCase(collectorTech.Name),
Recipe = gasGiantRecipe(giant, frame),
Proliferator='No',
}
return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=giantdata}
end
 
function gasGiantRecipe(gas, frame)
local gdata = {
CraftTime = '?',
In1 = titleCase(gas.DisplayName),
In1Qty = '',
}
for i, productID in ipairs(gas.GasItems) do
local product = itemByID(productID)
gdata[string.format('Out%d', i)] = titleCase(product.Name)
gdata[string.format('Out%dQty', i)] = '1'
end
end
prefix = frame:expandTemplate{title='ProductionChainTable/head', args={}}
return frame:expandTemplate{title='ItemRecipe', args=gdata}
return string.format('%s\n%s\n|}', prefix, table.concat(result, '\n|-\n'))
end
end
function fogDropRow(item, frame)
local fogdata = {
Building = 'Raider',
Replicator = 'No',
Recipe = fogDropRecipe(item, frame),
Proliferator = 'No',
}
return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=fogdata}
end
function fogDropRecipe(item, frame)
local prob = item.EnemyDropRange.y * 300
local level = item.EnemyDropLevel * 3
local dropMin = item.EnemyDropCount * 2 * (item.EnemyDropLevel / 10 + 1)
local dropMax = item.EnemyDropCount * 4
local fogdata = {
CraftTime = string.format('%.2f%%', prob),
Out1 = item.Name,
Out1Qty = string.format('%.1f-%.1f', dropMin, dropMax),
In1 = 'Raider',
In1Qty = string.format('Lvl %d+', level),
}
return frame:expandTemplate{title='ItemRecipe', args=fogdata}
end
function oceanRow(item, frame)
local pump = itemByName(machines.Ocean)
local pumpTech = technologyForRecipeID(recipesMakingByID(pump.ID)[1].ID)
local oceandata = {
Building = titleCase(pump.Name),
Replicator = 'No',
Technology = titleCase(pumpTech.Name),
Recipe = oceanRecipe(item, frame),
Proliferator='No',
}
return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=oceandata}
end
function oceanRecipe(item, frame)
local odata = {
CraftTime = '1.2 s',
Out1 = titleCase(item.Name),
Out1Qty = 1,
In1 = titleCase(item.MiningFrom),
In1Qty = '',
}
return frame:expandTemplate{title='ItemRecipe', args=odata}
end
-- get data from protosets


function itemByName(name)
function itemByName(name)
for _, item in pairs(protosets.ItemProtoSet.dataArray) do
local lame = string.lower(name)
if item.Name == name then
for _, item in ipairs(protosets.ItemProtoSet.dataArray) do
if string.lower(item.Name) == lame then
return item
return item
end
end
Line 102: Line 353:
end
end
function itemByID(id)
function itemByID(id)
for _, item in pairs(protosets.ItemProtoSet.dataArray) do
for _, item in ipairs(protosets.ItemProtoSet.dataArray) do
if item.ID == id then
if item.ID == id then
return item
return item
Line 111: Line 362:


function recipesMakingByID(id)
function recipesMakingByID(id)
result = {}
local result = {}
for _, recipe in pairs(protosets.RecipeProtoSet.dataArray) do
for _, recipe in ipairs(protosets.RecipeProtoSet.dataArray) do
for _, itemid in pairs(recipe.Results) do
for _, itemid in ipairs(recipe.Results) do
if itemid == id then
if itemid == id then
table.insert(result, recipe)
table.insert(result, recipe)
Line 123: Line 374:
end
end
function recipesUsingByID(id)
function recipesUsingByID(id)
components = {}
local recipes = {}
buildings = {}
for _, recipe in ipairs(protosets.RecipeProtoSet.dataArray) do
for _, recipe in pairs(protosets.RecipeProtoSet.dataArray) do
for _, itemid in ipairs(recipe.Items) do
for _, itemid in pairs(recipe.Items) do
if itemid == id then
if itemid == id then
if itemByID(recipe.Results[1]).CanBuild then
table.insert(recipes, recipe)
table.insert(buildings, recipe)
else
table.insert(components, recipe)
end
break
break
end
end
end
end
end
end
return components, buildings
return recipes
end
end


function technologyForRecipeID(id)
function technologyForRecipeID(id)
for _, tech in pairs(protosets.TechProtoSet.dataArray) do
for _, tech in ipairs(protosets.TechProtoSet.dataArray) do
for _, recipeid in pairs(tech.UnlockRecipes) do
for _, recipeid in ipairs(tech.UnlockRecipes) do
if recipeid == id then
if recipeid == id then
return tech
return tech
Line 150: Line 396:
return nil
return nil
end
end
function veinMakingByID(id)
for _, vein in ipairs(protosets.VeinProtoSet.dataArray) do
if vein.MiningItem == id then
return vein
end
end
return nil
end
function gasGiantsMakingByID(id)
local giants = {}
for _, planet in ipairs(protosets.ThemeProtoSet.dataArray) do
for _, item in ipairs(planet.GasItems) do
if item == id then
table.insert(giants, planet)
break
end
end
end
return giants
end
function isItemOcean(id)
for _, planet in ipairs(protosets.ThemeProtoSet.dataArray) do
if id == planet.WaterItemId then
return true
end
end
return false
end
function isItemFogDrop(item)
return item.EnemyDropRange.y > 0
end
-- sorting/deduplication
function deduplicateGiants(giants)
local dedup = {}
for _, planet in ipairs(giants) do
new = true
for _, exist in ipairs(dedup) do
if string.lower(planet.DisplayName) == string.lower(exist.DisplayName) then
new = false
break
end
end
if new then table.insert(dedup, planet) end
end
return dedup
end
-- display formatting


function titleCase(str)
function titleCase(str)
start, _ = string.gsub(str, '^%l', string.upper)
local start = string.gsub(str, '^%l', string.upper)
title, _ = string.gsub(start, '%s+%l', string.upper)
local title = string.gsub(start, '[-%s]%l', string.upper)
return title
local mk = string.gsub(title, 'MK.I', 'Mk.I')
return mk
end
end


return funcs
return funcs

Latest revision as of 00:07, 19 July 2024

Source data for this module is stored at Module:GameData/protosets.json

Exported Functions

Each function in this module is exported twice: one as-is for use in templates, and once with a Direct suffix for use directly on pages using {{#invoke|GameData|functionDirect|...}}.

recipesMaking

Arguments: Item Name

Print recipes making an item, given by name. This produces a full table with headers.

Example invocation:

{{#invoke:GameData|recipesMakingDirect|Sulfuric Acid}}
Recipe Building Replicator? Technology
Icon Sulfuric Acid.png
4
6 s
Icon Refined Oil.png
6
Icon Stone.png
8
Icon Water.png
4
Icon Chemical Plant.pngIcon Quantum Chemical Plant.png
Tech Basic Chemical Engineering.png
Icon Sulfuric Acid.png
1
1.2 s
Icon Sulfuric Acid Ocean.png
Icon Water Pump.png
Tech Fluid Storage Encapsulation.png

recipesUsing

Arguments: Item Name

Print recipes using an item as an ingredient. Output is separated into two full tables: recipes which create components, and recipes which create buildings. This is selected by the CanBuild property of the produced items.

Example invocation:

{{#invoke:GameData|recipesUsingDirect|Copper Ingot}}

Components

Recipe Building Replicator? Technology
Icon Magnetic Coil.png
2
1 s
Icon Magnet.png
2
Icon Copper Ingot.png
1
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png N/A
Icon Thruster.png
1
4 s
Icon Steel.png
2
Icon Copper Ingot.png
3
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Thruster.png
Icon Circuit Board.png
2
1 s
Icon Iron Ingot.png
2
Icon Copper Ingot.png
1
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png N/A
Icon Microcrystalline Component.png
1
2 s
Icon High-Purity Silicon.png
2
Icon Copper Ingot.png
1
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Semiconductor Material.png
Icon Particle Container.png
1
4 s
Icon Electromagnetic Turbine.png
2
Icon Copper Ingot.png
2
Icon Graphene.png
2
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Magnetic Particle Trap.png
Icon Particle Container.png
1
4 s
Icon Unipolar Magnet.png
10
Icon Copper Ingot.png
2
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Magnetic Particle Trap.png
Icon Magnum Ammo Box.png
1
1 s
Icon Copper Ingot.png
3
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Weapon System.png
Icon Shell Set.png
1
1.5 s
Icon Copper Ingot.png
9
Icon Combustible Unit.png
2
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Implosion Cannon.png
Icon Missile Set.png
1
2 s
Icon Copper Ingot.png
6
Icon Circuit Board.png
3
Icon Combustible Unit.png
2
Icon Engine.png
1
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Missile Turret.png
Icon Engine.png
1
3 s
Icon Magnetic Coil.png
1
Icon Copper Ingot.png
2
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Engine.png

Buildings

Recipe Building Replicator? Technology
Icon Solar Panel.png
1
6 s
Icon Copper Ingot.png
10
Icon High-Purity Silicon.png
10
Icon Circuit Board.png
5
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Solar Collection.png
Icon Geothermal Power Station.png
1
6 s
Icon Steel.png
15
Icon Copper Ingot.png
20
Icon Photon Combiner.png
4
Icon Super-Magnetic Ring.png
1
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Geothermal Extraction.png
Icon Jammer Tower.png
1
5 s
Icon Copper Ingot.png
12
Icon Plasma Exciter.png
9
Icon Diamond.png
6
Icon Processor.png
3
Icon Assembling Machine Mk.I.pngIcon Assembling Machine Mk.II.pngIcon Assembling Machine Mk.III.pngIcon Re-composing Assembler.png
Tech Jammer Tower.png

itemRecipes

Arguments: Item Name

Print recipes making an item. This does not produce a full table, and is intended for use in ItemInfo boxes such as at Graphene/ItemInfo.

itemField

Arguments: Item Name, Field Name

Print a field from an item, with the field to print as an argument. You can look at Module:GameData/protosets.json and scroll down or search (Ctrl+F) to the ItemProtoSet to browse fields.

Example invocation:

{{#invoke:GameData|itemFieldDirect|Magnetic Coil|Description}}

It is an extremely useful basic electromagnetic component.

{{#invoke:GameData|itemFieldDirect|Hydrogen|StackSize}}

20


local funcs = {}
local protosets = mw.loadJsonData('Module:GameData/protosets.json')
local machines = {
	Smelt='Smelter',
	Assemble='Assembling Machine',
	Refine='Oil Refinery',
	Chemical='Chemical Plant',
	Exchange='Energy Exchanger',
	Particle='Miniature Particle Collider',
	PhotonStore='Ray Receiver',
	Fractionate='Fractionator',
	Research='Matrix Lab',
	Mine='Mining Machine',
	Gas='Orbital Collector',
	Ocean='Water Pump',
	Oil='Oil Extractor',
}
local OilSeepID = 7

-- exported functions

-- recipesMaking
-- print out table rows of all recipes making an item with full information
-- the recipe, the building, technology to unlock it, etc.

function funcs.recipesMaking(frame)
	return recipesMakingName(frame:getParent().args[1], frame)
end

function funcs.recipesMakingDirect(frame)
	return recipesMakingName(frame.args[1], frame)
end

function recipesMakingName(name, frame)
	local rtable = {
		frame:expandTemplate{title='ProductionChainTable/head', args={}},
	}
	local item = itemByName(name)
	for _, recipe in ipairs(recipesMakingByID(item.ID)) do
		table.insert(rtable, recipeRow(recipe, frame))
	end
	local vein = veinMakingByID(item.ID)
	if vein ~= nil then
		table.insert(rtable, veinRow(item, vein, frame))
	end
	for _, giant in ipairs(deduplicateGiants(gasGiantsMakingByID(item.ID))) do
		table.insert(rtable, gasGiantRow(item, giant, frame))
	end
	if isItemOcean(item.ID) then
		table.insert(rtable, oceanRow(item, frame))
	end
	if isItemFogDrop(item) then
		table.insert(rtable, fogDropRow(item, frame))
	end
	table.insert(rtable, '|}')
	return table.concat(rtable, '\n')
end

-- recipesUsing
-- print out table rows of all recipes using an item with full information, like recipesMaking
-- split output into components and buildings

function funcs.recipesUsing(frame)
	return recipesUsingName(frame:getParent().args[1], frame)
end

function funcs.recipesUsingDirect(frame)
	return recipesUsingName(frame.args[1], frame)
end

function recipesUsingName(name, frame)
	local output = {}
	local header = frame:expandTemplate{title='ProductionChainTable/head', args={}}
	local item = itemByName(name)
	local components = {}
	local buildings = {}
	for _, r in ipairs(recipesUsingByID(item.ID)) do
		if r.GridIndex >= 2000 then
			table.insert(buildings, r)
		else
			table.insert(components, r)
		end
	end
	if next(components) then
		if next(buildings) then
			table.insert(output, '=== Components ===')
		end
		table.insert(output, header)
		for _, recipe in ipairs(components) do
			table.insert(output, recipeRow(recipe, frame))
		end
		table.insert(output, '|}')
	end
	if next(buildings) then
		if next(components) then
			table.insert(output, '=== Buildings ===')
		end
		table.insert(output, header)
		for _, recipe in ipairs(buildings) do
			table.insert(output, recipeRow(recipe, frame))
		end
		table.insert(output, '|}')
	end
	return table.concat(output, '\n')
end

-- itemRecipes
-- print out just recipe information on how to make an item
-- intended for use in ItemInfo boxes

function funcs.itemRecipes(frame)
	return itemRecipesForName(frame:getParent().args[1], frame)
end
function funcs.itemRecipesDirect(frame)
	return itemRecipesForName(frame.args[1], frame)
end

function itemRecipesForName(name, frame)
	local item = itemByName(name)
	local output = {}
	for _, r in ipairs(recipesMakingByID(item.ID)) do
		table.insert(output, itemRecipe(r, frame))
	end
	local vein = veinMakingByID(item.ID)
	if vein then
		table.insert(output, veinRecipe(item, vein, frame))
	end
	for _, giant in ipairs(deduplicateGiants(gasGiantsMakingByID(item.ID))) do
		table.insert(output, gasGiantRecipe(giant, frame))
	end
	if isItemOcean(item.ID) then
		table.insert(output, oceanRecipe(item, frame))
	end
	return table.concat(output, '\n')
end

-- itemField
-- print a field from an item
-- used to extract details about an item

function funcs.itemField(frame)
	return itemFieldByName(frame:getParent().args[1], frame:getParent().args[2])
end
function funcs.itemFieldDirect(frame)
	return itemFieldByName(frame.args[1], frame.args[2])
end

function itemFieldByName(itemname, fieldname)
	local item = itemByName(itemname)
	return item[fieldname]
end

-- make full rows and recipe data for tables

function recipeRow(recipe, frame)
	local rdata = {
		Building = titleCase(machines[recipe.Type]),
	}
	if recipe.Type == 'Fractionate' then
		rdata.Recipe = fractionateRecipe(recipe, frame)
	else
		rdata.Recipe = itemRecipe(recipe, frame)
	end
	if recipe.Handcraft then
		rdata.Replicator = 'Yes'
	else
		rdata.Replicator = 'No'
	end
	local tech = technologyForRecipeID(recipe.ID)
	if tech ~= nil then
		rdata.Technology = titleCase(tech.Name)
	else
		rdata.Technology = ''
	end
	local productive = not recipe.NonProductive
	for i, itemid in ipairs(recipe.Items) do
		productive = productive and itemByID(itemid).Productive
	end
	if productive then
		rdata.Proliferator = 'Yes'
	else
		rdata.Proliferator = 'Speed'
	end
	return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=rdata}
end

function itemRecipe(recipe, frame)
	local rdata = {
		CraftTime = string.format('%s s', tostring(recipe.TimeSpend/60)),
	}
	for i, itemid in ipairs(recipe.Results) do
		rdata[string.format('Out%d', i)] = titleCase(itemByID(itemid).Name)
		rdata[string.format('Out%dQty', i)] = tostring(recipe.ResultCounts[i])
	end
	for i, itemid in ipairs(recipe.Items) do
		rdata[string.format('In%d', i)] = titleCase(itemByID(itemid).Name)
		rdata[string.format('In%dQty', i)] = tostring(recipe.ItemCounts[i])
	end
	return frame:expandTemplate{title='ItemRecipe', args=rdata}
end

function fractionateRecipe(recipe, frame)
	local ratio = 100 * recipe.ResultCounts[1] / recipe.ItemCounts[1]
	local rdata = {
		CraftTime = tostring(ratio) .. '%',
	}
	for i, itemid in ipairs(recipe.Results) do
		rdata[string.format('Out%d', i)] = titleCase(itemByID(itemid).Name)
		rdata[string.format('Out%dQty', i)] = '1'
	end
	for i, itemid in ipairs(recipe.Items) do
		rdata[string.format('In%d', i)] = titleCase(itemByID(itemid).Name)
		rdata[string.format('In%dQty', i)] = '1'
	end
	return frame:expandTemplate{title='ItemRecipe', args=rdata}
end

function veinRow(item, vein, frame)
	if vein.ID == OilSeepID then -- I don't have a better test for oil right now
		local extractor = itemByName(machines.Oil)
		local extractorTech = technologyForRecipeID(recipesMakingByID(extractor.ID)[1].ID)
		local oildata = {
			Building = titleCase(extractor.Name),
			Replicator = 'No',
			Technology = titleCase(extractorTech.Name),
			Recipe = oilRecipe(item, vein, frame),
			Proliferator='No',
		}
		return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=oildata}
	else
		local miner = itemByName(machines.Mine)
		local minerTech = technologyForRecipeID(recipesMakingByID(miner.ID)[1].ID)
		local veindata = {
			Building = titleCase(miner.Name),
			Replicator = 'Mine',
			Technology = titleCase(minerTech.Name),
			Recipe = veinRecipe(item, vein, frame),
			Proliferator='No',
		}
		return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=veindata}
	end
end

function veinRecipe(item, vein, frame)
	local vname = string.gsub(vein.Name, '%sVeins$', ' Vein')
	local idata = {
		In1 = titleCase(vname),
		In1Qty = '1',
		Out1 = titleCase(item.Name),
		Out1Qty = '1',
		CraftTime = '2 s',
	}
	return frame:expandTemplate{title='ItemRecipe', args=idata}
end

function oilRecipe(item, vein, frame)
	local odata = {
		In1 = titleCase(vein.Name),
		In1Qty = '',
		Out1 = titleCase(item.Name),
		Out1Qty = '1',
		CraftTime = '?',
	}
	return frame:expandTemplate{title='ItemRecipe', args=odata}
end

function gasGiantRow(item, giant, frame)
	local collector = itemByName(machines.Gas)
	local collectorTech = technologyForRecipeID(recipesMakingByID(collector.ID)[1].ID)
	local giantdata = {
		Building = titleCase(collector.Name),
		Replicator = 'Mine',
		Technology = titleCase(collectorTech.Name),
		Recipe = gasGiantRecipe(giant, frame),
		Proliferator='No',
	}
	return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=giantdata}
end

function gasGiantRecipe(gas, frame)
	local gdata = {
		CraftTime = '?',
		In1 = titleCase(gas.DisplayName),
		In1Qty = '',
	}
	for i, productID in ipairs(gas.GasItems) do
		local product = itemByID(productID)
		gdata[string.format('Out%d', i)] = titleCase(product.Name)
		gdata[string.format('Out%dQty', i)] = '1'
	end
	return frame:expandTemplate{title='ItemRecipe', args=gdata}
end

function fogDropRow(item, frame)
	local fogdata = {
		Building = 'Raider',
		Replicator = 'No',
		Recipe = fogDropRecipe(item, frame),
		Proliferator = 'No',
	}
	return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=fogdata}
end

function fogDropRecipe(item, frame)
	local prob = item.EnemyDropRange.y * 300
	local level = item.EnemyDropLevel * 3
	local dropMin = item.EnemyDropCount * 2 * (item.EnemyDropLevel / 10 + 1)
	local dropMax = item.EnemyDropCount * 4
	local fogdata = {
		CraftTime = string.format('%.2f%%', prob),
		Out1 = item.Name,
		Out1Qty = string.format('%.1f-%.1f', dropMin, dropMax),
		In1 = 'Raider',
		In1Qty = string.format('Lvl %d+', level),
	}
	return frame:expandTemplate{title='ItemRecipe', args=fogdata}
end

function oceanRow(item, frame)
	local pump = itemByName(machines.Ocean)
	local pumpTech = technologyForRecipeID(recipesMakingByID(pump.ID)[1].ID)
	local oceandata = {
		Building = titleCase(pump.Name),
		Replicator = 'No',
		Technology = titleCase(pumpTech.Name),
		Recipe = oceanRecipe(item, frame),
		Proliferator='No',
	}
	return '|-\n| ' .. frame:expandTemplate{title='ProductionChain', args=oceandata}
end

function oceanRecipe(item, frame)
	local odata = {
		CraftTime = '1.2 s',
		Out1 = titleCase(item.Name),
		Out1Qty = 1,
		In1 = titleCase(item.MiningFrom),
		In1Qty = '',
	}
	return frame:expandTemplate{title='ItemRecipe', args=odata}
end

-- get data from protosets

function itemByName(name)
	local lame = string.lower(name)
	for _, item in ipairs(protosets.ItemProtoSet.dataArray) do
		if string.lower(item.Name) == lame then
			return item
		end
	end
	error('No item named ' .. name)
end
function itemByID(id)
	for _, item in ipairs(protosets.ItemProtoSet.dataArray) do
		if item.ID == id then
			return item
		end
	end
	error('No item with ID ' .. id)
end

function recipesMakingByID(id)
	local result = {}
	for _, recipe in ipairs(protosets.RecipeProtoSet.dataArray) do
		for _, itemid in ipairs(recipe.Results) do
			if itemid == id then
				table.insert(result, recipe)
				break
			end
		end
	end
	return result
end
function recipesUsingByID(id)
	local recipes = {}
	for _, recipe in ipairs(protosets.RecipeProtoSet.dataArray) do
		for _, itemid in ipairs(recipe.Items) do
			if itemid == id then
				table.insert(recipes, recipe)
				break
			end
		end
	end
	return recipes
end

function technologyForRecipeID(id)
	for _, tech in ipairs(protosets.TechProtoSet.dataArray) do
		for _, recipeid in ipairs(tech.UnlockRecipes) do
			if recipeid == id then
				return tech
			end
		end
	end
	return nil
end

function veinMakingByID(id)
	for _, vein in ipairs(protosets.VeinProtoSet.dataArray) do
		if vein.MiningItem == id then
			return vein
		end
	end
	return nil
end

function gasGiantsMakingByID(id)
	local giants = {}
	for _, planet in ipairs(protosets.ThemeProtoSet.dataArray) do
		for _, item in ipairs(planet.GasItems) do
			if item == id then
				table.insert(giants, planet)
				break
			end
		end
	end
	return giants
end

function isItemOcean(id)
	for _, planet in ipairs(protosets.ThemeProtoSet.dataArray) do
		if id == planet.WaterItemId then
			return true
		end
	end
	return false
end

function isItemFogDrop(item)
	return item.EnemyDropRange.y > 0
end

-- sorting/deduplication

function deduplicateGiants(giants)
	local dedup = {}
	for _, planet in ipairs(giants) do
		new = true
		for _, exist in ipairs(dedup) do
			if string.lower(planet.DisplayName) == string.lower(exist.DisplayName) then
				new = false
				break
			end
		end
		if new then table.insert(dedup, planet) end
	end
	return dedup
end

-- display formatting

function titleCase(str)
	local start = string.gsub(str, '^%l', string.upper)
	local title = string.gsub(start, '[-%s]%l', string.upper)
	local mk = string.gsub(title, 'MK.I', 'Mk.I')
	return mk
end

return funcs
🍪 We use cookies to keep session information to provide you a better experience.