An equivalent to LuaRecipePrototype::get_product_quality for rocket_launch_products

Place to ask discuss and request the modding support of Factorio. Don't request mods here.
User avatar
Shemp
Long Handed Inserter
Long Handed Inserter
Posts: 51
Joined: Mon Jan 19, 2026 6:51 pm
Contact:

An equivalent to LuaRecipePrototype::get_product_quality for rocket_launch_products

Post 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.
User avatar
Shemp
Long Handed Inserter
Long Handed Inserter
Posts: 51
Joined: Mon Jan 19, 2026 6:51 pm
Contact:

Re: An equivalent to LuaRecipePrototype::get_product_quality for rocket_launch_products

Post 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. :lol:

Code: Select all

--[[ getProductQuality: ItemProduct, LuaQualityPrototype/string
                        -> 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.
--]]
local function getProductQuality(itemProduct, recipeQuality)
    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
    local qualityChange = itemProduct.quality_change 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 then
            recipeQuality = target
        else
            break
        end
        -- When qualityChange reaches 0, the loop will end
    end

    -- We're assuming the API will only return LuaQualityPrototype or nil
    local qualityMin = itemProduct.quality_min
    local qualityMax = itemProduct.quality_max

    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
Post Reply

Return to “Modding interface requests”