Page 1 of 1
An equivalent to LuaRecipePrototype::get_product_quality for rocket_launch_products
Posted: Sat Jun 27, 2026 7:28 pm
by Shemp
Factorio 2.1 added the ability for recipes to require/produce items that have a different quality to the recipe's quality, and LuaRecipePrototype has handy functions for
ingredients and
products to calculate their quality at runtime.
Since 2.0, it is possible to launch quality items to orbit in a Rocket silo, and get the corresponding quality of items returned as rocket_launch_products. In 2.1, quality_min/max/change can affect this process as well.
I propose a new function for LuaItemPrototype, which would act similarly to
LuaRecipePrototype::get_product_quality:
Code: Select all
get_rocket_launch_product_quality(product_index, item_quality?) → LuaQualityPrototype
Quality of the product given when the launched item is of a specific quality.
Parameters
product_index :: uint32
item_quality :: QualityID?
Defaults to normal.
Re: An equivalent to LuaRecipePrototype::get_product_quality for rocket_launch_products
Posted: Mon Jun 29, 2026 5:23 pm
by Shemp
I wrote a (bad) implementation of this in Lua which accepts any
ItemProduct obtained at runtime, which you can use in the likely event that this request doesn't get added.
Code: Select all
--[[ getProductQuality: LuaQualityPrototype/string, LuaForce?,
QualityID?, QualityID?, uint32?
-> LuaQualityPrototype
Work out what quality you will get from an ItemProduct when the process
starts at a given quality.
Ignores the possibility of a quality roll, i.e. using quality modules.
"force" can be omitted if the process doesn't account for it, e.g. spoilage
--]]
local function getProductQuality(recipeQuality, force,
qualityMin, qualityMax, qualityChange)
if type(recipeQuality) == "string" then
recipeQuality = prototypes.quality[recipeQuality]
end
if not recipeQuality then recipeQuality = prototypes.quality.normal end
-- I think this takes place before min/max
qualityChange = qualityChange or 0
while qualityChange ~= 0 do
local target
if qualityChange > 0 then
target = recipeQuality.next
qualityChange = qualityChange - 1
else
target = recipeQuality.previous
qualityChange = qualityChange + 1
end
if target and
-- When force is provided, quality_change must respect whether
-- a quality is unlocked by that force
(not force or force.is_quality_unlocked(target)) then
recipeQuality = target
else
break
end
-- When qualityChange reaches 0, the loop will end
end
if type(qualityMin) == "string" then
qualityMin = prototypes.quality[qualityMin]
end
if type(qualityMax) == "string" then
qualityMax = prototypes.quality[qualityMax]
end
-- If only one of the pair is defined, obtain the other by walking to
-- the end of the quality chain
if qualityMin and not qualityMax then
qualityMax = qualityMin
while qualityMax.next do
qualityMax = qualityMax.next
end
elseif qualityMax and not qualityMin then
qualityMin = qualityMax
while qualityMin.previous do
qualityMin = qualityMin.previous
end
end
-- We're not doing quality clamping and we're finished.
if not qualityMin then return recipeQuality end
local lowBound = recipeQuality
local highBound = recipeQuality
local foundMin = false
local foundMax = false
-- Reach out from both ends of our starting quality to see if we touch
-- either side of quality_min/max
while true do
if highBound.name == qualityMax.name then
foundMax = true
break
end
if highBound.next then
highBound = highBound.next
else
break
end
end
while true do
if lowBound.name == qualityMin.name then
foundMin = true
break
end
if lowBound.previous then
lowBound = lowBound.previous
else
break
end
end
if foundMin and foundMax then
-- We're inside the range and don't need to clamp
return recipeQuality
elseif foundMin then
-- We're above the range, so clamp to the max
return qualityMax
else
-- This also covers the case of being in a different chain
return qualityMin
end
end