Module:RenderInfobox

From Satisfactory Wiki
Jump to navigation Jump to search


This module is used to render automatic infoboxes. Parameters not found in Docs.json have to be supplied manually.

Invocation

The preferred way of invocating this module is through the dedicated infobox templates: {{Infobox item}} and {{Infobox building}}. Parameters are documented on their respective Doc pages.


require("Module:DocsUtils")

local p = {}

-- Hover tooltips for select labels
loc.tooltips = {
    maxSpeed = "Applies on flat terrain, can be exceeded if going downhill",
    time0_50 = "Time to reach 50 km/h on flat terrain",
    damagePerSecond = "Damage per second",
}

-- Group names
loc.groups = {
    item = "Item",
    liquid = "Liquid",
    gas = "Gas",
    unknown = "Unknown",
    building = "Building",
    vehicle = "Vehicle",
    fuel = "Fuel",
    equipment = "Equipment",
    ingredients = "Ingre­dients",
    dimensions = "Dimensions",
}

-- Row labels (may be wiki links)
loc.labels = {
    unlockedBy = "Unlocked by",
    className = "Class name",

    stackSize = "Stack size",
    radioactive = "[[Radioactivity|Radioactive]]",
    sinkPoints = "[[AWESOME Sink|Sink points]]",
    energy = "Energy",
    stackEnergy = "Stack energy",
    abbreviation = "Abbrev­iation",
    fluidColor = "Fluid color",
    equipmentSlot = "Equipment slot",
    ammo = "Ammo",
    damage = "[[Health|Damage]]",
    magSize = "Magazine size",
    rateOfFire = "Rate of fire",
    reloadTime = "Reload time",
    damagePerSecond = "DPS",
    range = "Range",

    powerUsage = "[[Power|Power usage]]",
    powerGenerated = "[[Power|Power generated]]",
    fuelBurnRate = "Fuel burn rate",
    supplementPerMinute = "Supplement input rate",
    overclockable = "[[Clock speed|Overclock­able]]",
    inventorySize = "Inventory size",
    beltInputs = "Conveyor<br/>inputs",
    beltOutputs = "Conveyor<br/>outputs",
    pipeInputs = "Pipeline<br/>inputs",
    pipeOutputs = "Pipeline<br/>outputs",
    maxSpeed = "Max speed",
    time0_50 = "0-50 km/h",
    size_width = "Width",
    size_length = "Length",
    size_height = "Height",
    size_area = "Area",

    yes = "Yes",
    no = "No",
}

loc.categories = {
	items = "[[Category:Items]]",
	fluids = "[[Category:Fluids]]",
	liquids = "[[Category:Liquids]]",
	gases = "[[Category:Gases]]",
	buildings = "[[Category:Buildings]]",
	vehicles = "[[Category:Vehicles]]",
	fuels = "[[Category:Fuels]]",
	radioactive = "[[Category:Radioactive]]",
}

loc.fuelSort = function(a, b)
    if a.isVehicle ~= b.isVehicle then
        return not a.isVehicle
    elseif not a.isVehicle then
        return a.buildingName < b.buildingName
    else
        return a.buildingName > b.buildingName
    end
end

-- Generate a single <data> row. Automatically get label from loc.labels and format the value.
-- - parameter - string - parameter name from the data JSON
-- - branchValues - table
-- - unit - string - unit to show after the values
-- - nowiki - boolean - whether to escape the values
local function infoboxRow(parameter, branchValues, unit, nowiki)
    local output = formatBranchDiff(getBranchValues(parameter, branchValues.stable, branchValues.experimental), nil, nowiki)
    if output ~= "" and unit ~= nil then
        output = output .. unit
    end
    return "<data><label>" .. loc.labels[parameter] .. "</label><default>" .. output .. "</default></data>"
end


function p.generateItemInfobox(frame)
    local targetClassName = frame:getParent().args.className
	local target = getObjectFromClassName(targetClassName, itemsJSON)
	if target.stable == nil and target.experimental == nil then
		return tostring(mw.html.create("strong"):addClass("error"):wikitext("Item infobox error: invalid target"):allDone())
	end

    -- image name
    local itemLink, _ = getLinkAndName(targetClassName, target.stable, target.experimental)

    -- form
    local forms = getBranchValues("form", target.stable, target.experimental)
    forms.stable = forms.stable ~= nil and (forms.stable == "solid" and loc.groups.item or (forms.stable == "liquid" and loc.groups.liquid or (forms.stable == "gas" and loc.groups.gas or loc.groups.item))) or nil
    forms.experimental = forms.experimental ~= nil and (forms.experimental == "solid" and loc.groups.item or (forms.experimental == "liquid" and loc.groups.liquid or (forms.experimental == "gas" and loc.groups.gas or loc.groups.item))) or nil
    forms.text = formatBranchDiff(forms)
    forms.eitherIsSolid = forms.stable == loc.groups.item or forms.experimental == loc.groups.item
    forms.eitherIsFluid = forms.stable == loc.groups.liquid or forms.experimental == loc.groups.liquid or forms.stable == loc.groups.gas or forms.experimental == loc.groups.gas

    -- fuel
    local fuelValues = getBranchValues("energy", target.stable, target.experimental)
    local fuelSection = ""
    if fuelValues.stable ~= 0 or fuelValues.experimental ~= 0 then
        local fuelConsumers = {}
        local fuelConsumersOrder = {}

        -- list the item's energy
        fuelSection = "<data><label>" .. loc.labels.energy .. "</label><default>" .. formatBranchDiff(fuelValues) .. loc.units.mj .. "</default></data>"

        -- if the item is a solid fuel, list the energy per stack
        if (target.stable ~= nil and target.stable.form == "solid") or (target.experimental ~= nil and target.experimental.form == "solid") then
            fuelSection = fuelSection .. "<data><label>" .. loc.labels.stackEnergy .. "</label><default>" .. formatBranchDiff(
                {stable = fuelValues.stable ~= nil and target.stable.form == "solid" and fuelValues.stable * target.stable.stackSize or nil,
                experimental = fuelValues.experimental ~= nil and target.experimental.form == "solid" and fuelValues.experimental * target.experimental.stackSize or nil},
                target.stable ~= nil and target.experimental ~= nil and target.stable.form ~= target.experimental.form) .. loc.units.mj .. "</default></data>"
        end

        -- list the buildings that consume the item as fuel
        for _,branchBuildings in pairs(buildingsJSON) do
            for _,building in pairs(branchBuildings) do
                for _,fuelProcess in pairs(building.burnsFuel) do
                    if fuelProcess.fuel == targetClassName then
                        if fuelConsumers[building.className] == nil then
                            fuelConsumers[building.className] = {}
                        end
                        if building.stable then
                            fuelConsumers[building.className].stable = building
                        end
                        if building.experimental then
                            fuelConsumers[building.className].experimental = building
                        end
                    end
                end
            end
        end

        -- determine the order of the fuel consumers by retrieving names from their classNames, then sorting alphabetically
        for className,_ in pairs(fuelConsumers) do
            local buildingLink, buildingName = getLinkAndName(className, fuelConsumers[className].stable, fuelConsumers[className].experimental)
            table.insert(fuelConsumersOrder, {className = className, buildingName = buildingName, buildingLink = buildingLink, isVehicle = fuelConsumers[className].stable ~= nil and fuelConsumers[className].stable.isVehicle or fuelConsumers[className].experimental ~= nil and fuelConsumers[className].experimental.isVehicle})
        end
        table.sort(fuelConsumersOrder, loc.fuelSort)

        -- list the fuel consumption of each building in both branches, in both per minute and per cycle
        for _,namedBuilding in pairs(fuelConsumersOrder) do
            local branchBuilding = fuelConsumers[namedBuilding.className]
            local buildingPowerGeneration = getBranchValues("powerGenerated", branchBuilding.stable, branchBuilding.experimental)

            if buildingPowerGeneration.stable == 0 then
                buildingPowerGeneration.stable = nil
            end
            if buildingPowerGeneration.experimental == 0 then
                buildingPowerGeneration.experimental = nil
            end
            -- calculate the fuel consumption per minute and per cycle
            if buildingPowerGeneration.stable ~= nil and buildingPowerGeneration.stable ~= 0 and fuelValues.stable ~= nil then
                buildingPowerGeneration.stable = fuelValues.stable/buildingPowerGeneration.stable
                buildingPowerGeneration.stablePerMin = formatNumber(60/buildingPowerGeneration.stable)
                buildingPowerGeneration.stable = formatNumber(buildingPowerGeneration.stable)
            end
            if buildingPowerGeneration.experimental ~= nil and fuelValues.experimental ~= nil then
                buildingPowerGeneration.experimental = fuelValues.experimental/buildingPowerGeneration.experimental
                buildingPowerGeneration.experimentalPerMin = formatNumber(60/buildingPowerGeneration.experimental)
                buildingPowerGeneration.experimental = formatNumber(buildingPowerGeneration.experimental)
            end

            -- manually do EA/EX formatting
            local fuelConsumption = ""
            -- if it's the same for both branches, list it only once
            if buildingPowerGeneration.stable ~= nil and buildingPowerGeneration.stable == buildingPowerGeneration.experimental then
                fuelConsumption = "<li>'''" .. buildingPowerGeneration.stable .. loc.units.sec .. " (" .. buildingPowerGeneration.stablePerMin .. loc.units.pmin .. ")'''</li>"
            else
                -- if it's different for each branch, list it twice, formatted for each branch
                if buildingPowerGeneration.stable ~= nil then
                    fuelConsumption = fuelConsumption .. "<li>{{EA|'''" .. buildingPowerGeneration.stable .. loc.units.sec .. "''' (" .. buildingPowerGeneration.stablePerMin .. loc.units.pmin .. ")}}</li>"
                end
                if buildingPowerGeneration.experimental ~= nil then
                    fuelConsumption = fuelConsumption .. "<li>{{EX|'''" .. buildingPowerGeneration.experimental .. loc.units.sec .. "''' (" .. buildingPowerGeneration.experimentalPerMin .. loc.units.pmin .. ")}}</li>"
                end
            end

            fuelSection = fuelSection .. "<data><default><ul><li>{{ItemLink|" .. namedBuilding.buildingLink .. "|" .. namedBuilding.buildingName .. "}}<ul>" .. fuelConsumption .. "</ul><li/></ul></default></data>"
        end

        -- wrap in a group, end the fuel consumers list and data row
        fuelSection = [=[<group collapse="open"><header>]=] .. loc.groups.fuel .. [=[</header>]=] .. fuelSection .. [=[</group>]=]
    end

    -- infobox
    local xml = [=[<infobox>
<title><default>]=] .. formatBranchDiff(getBranchValues("name", target.stable, target.experimental), true) .. [=[</default></title>
<image><default>]=] .. frame:callParserFunction("#setmainimage", itemLink .. ".png") .. [=[</default></image>
<data><default><i>]=]  .. formatBranchDiff(getBranchValues("description", target.stable, target.experimental)) .. [=[</i></default></data>
]=] .. infoboxRow("unlockedBy", target) .. [=[
]=] .. infoboxRow("className", target) .. [=[

<group collapse="open"><header>]=] .. forms.text .. [=[</header>
]=] .. (forms.eitherIsSolid and infoboxRow("stackSize", target) or "") .. [=[
]=] .. infoboxRow("radioactive", target) .. [=[
]=] .. (forms.eitherIsSolid and infoboxRow("sinkPoints", target) or "") .. [=[
]=] .. infoboxRow("abbreviation", target) .. [=[
]=] .. (forms.eitherIsFluid and infoboxRow("fluidColor", target, nil, true) or "") .. [=[

</group>]=] .. fuelSection .. [=[

<group collapse="open"><header>]=] .. loc.groups.equipment .. [=[</header>
<data><label>]=] .. loc.labels.equipmentSlot .. [=[</label><default>]=] .. (frame:getParent().args.equipmentSlot or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.ammo .. [=[</label><default>]=] .. (frame:getParent().args.ammo or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.damage .. [=[</label><default>]=] .. (frame:getParent().args.damage or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.magSize .. [=[</label><default>]=] .. (frame:getParent().args.magSize or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.rateOfFire .. [=[</label><default>]=] .. (not (frame:getParent().args.rateOfFire == nil or frame:getParent().args.rateOfFire == "") and (frame:getParent().args.rateOfFire .. "/s") or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.reloadTime .. [=[</label><default>]=] .. (not (frame:getParent().args.reloadTime == nil or frame:getParent().args.reloadTime == "") and (frame:getParent().args.reloadTime .. loc.units.sec) or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.damagePerSecond .. [=[</label><default>]=] .. (frame:getParent().args.damagePerSecond or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.range .. [=[</label><default>]=] .. (frame:getParent().args.range or "") .. [=[</default></data>
</group>
</infobox>]=]

	-- categories
	local categories = ""
	if not (frame:getParent().args.nocat ~= nil and frame:getParent().args.nocat == "1") then
		-- individual ifs to cover branch differences
		if forms.stable == loc.groups.item or forms.experimental == loc.groups.item then
			categories = categories .. loc.categories.items
		end
		if forms.stable == loc.groups.liquid or forms.experimental == loc.groups.liquid then
			categories = categories .. loc.categories.liquids .. loc.categories.fluids
		end
		if forms.stable == loc.groups.gas or forms.experimental == loc.groups.gas then
			categories = categories .. loc.categories.gases .. loc.categories.fluids
		end
		if fuelValues.stable ~= 0 or fuelValues.experimental ~= 0 then
			categories = categories .. loc.categories.fuels
		end
		local radioactiveValues = getBranchValues("radioactive", target.stable, target.experimental)
		if radioactiveValues.stable ~= 0 or radioactiveValues.experimental ~= 0 then
			categories = categories .. loc.categories.radioactive
		end
		
		
	end

    return mw.getCurrentFrame():preprocess(xml) .. categories
end


function p.generateBuildingInfobox(frame)
	local targetClassName = frame:getParent().args.className
	local target = getObjectFromClassName(targetClassName, buildingsJSON)
	if target.stable == nil and target.experimental == nil then
		return tostring(mw.html.create("strong"):addClass("error"):wikitext("Building infobox error: invalid target"):allDone())
	end

    -- image name
    local buildingLink, _ = getLinkAndName(targetClassName, target.stable, target.experimental)

    -- fuel
    local fuelSection = ""
    local fuelValues = getBranchValues("burnsFuel", target.stable, target.experimental)

    if (fuelValues.stable ~= nil and fuelValues.stable[1] ~= nil) or (fuelValues.experimental ~= nil and fuelValues.experimental[1] ~= nil) then
        local fuels = {}
        local fuelsOrder = {}

        -- if a fuel is burnable in a branch, set its name and energy
        for branchName,branch in pairs(getBranchValues("burnsFuel", target.stable, target.experimental)) do
            for _,fuel in pairs(branch) do
                if fuels[fuel.fuel] == nil then
                    fuels[fuel.fuel] = {fuel = {}, energy = {}, supplement = {}, byproduct = {}, byproductAmount = {}}
                end
                local fuelItem = getObjectFromClassName(fuel.fuel, itemsJSON)
                fuels[fuel.fuel].fuel[branchName] = fuel.fuel
                fuels[fuel.fuel].energy[branchName] = fuelItem[branchName].energy
                fuels[fuel.fuel].supplement[branchName] = fuel.supplement
                fuels[fuel.fuel].byproduct[branchName] = fuel.byproduct
                fuels[fuel.fuel].byproductAmount[branchName] = fuel.byproductAmount
            end
        end

        -- order fuels by their energy
        for fuelClassName,_ in pairs(fuels) do
            table.insert(fuelsOrder, fuelClassName)
        end
        table.sort(fuelsOrder, function(a,b) return (fuels[a].energy.stable or fuels[a].energy.experimental or 0) < (fuels[b].energy.stable or fuels[b].energy.experimental or 0) end)

        -- generate infobox fuel section
        for _,fuelClassName in pairs(fuelsOrder) do
        	local supplement = fuels[fuelClassName].supplement.stable or fuels[fuelClassName].supplement.experimental
        	local byproduct = fuels[fuelClassName].byproduct.stable or fuels[fuelClassName].byproduct.experimental
            fuelSection = fuelSection .. "<li>'''" .. generateItemLink(fuelClassName, fuels[fuelClassName].fuel.stable, fuels[fuelClassName].fuel.experimental, false) .. "'''"
            fuelSection = fuelSection .. (supplement and (" +&nbsp;" .. generateItemLink(supplement, fuels[fuelClassName].supplement.stable, fuels[fuelClassName].supplement.experimental, false)) or "") .. "</li>"
            fuelSection = fuelSection .. (byproduct and ("<ul><li>→&nbsp;" .. formatBranchDiff(fuels[fuelClassName].byproductAmount) .. "× " .. generateItemLink(byproduct, fuels[fuelClassName].byproduct.stable, fuels[fuelClassName].byproduct.experimental, false) .. "</li></ul>") or "")
        end
        fuelSection = [=[<group collapse="open"><header>]=] .. loc.groups.fuel .. [=[</header>]=] .. infoboxRow("supplementPerMinute", target, loc.units.pmin) .. [=[<data><default><ul>]=] .. fuelSection .. [=[</ul></default></data></group>]=]
    end

    -- ingredients
    local ingredientSection = ""
    local recipes = {}

    -- get all recipes that produce the target building (should be only one, or two if EA/EX)
    for _,branchRecipes in pairs(recipesJSON) do
        for _,recipe in pairs(branchRecipes) do
            for _,product in pairs(recipe.products) do
                if targetClassName == product.item then
                    if recipe.stable then
                        recipes.stable = recipe
                    end
                    if recipe.experimental then
                        recipes.experimental = recipe
                    end
                end
            end
        end
    end

    -- if there is only one recipe, show ingredients in a group
    if (recipes.stable == recipes.experimental) or (recipes.stable == nil and recipes.experimental ~= nil) or (recipes.stable ~= nil and recipes.experimental == nil) then
        recipes.stable = recipes.stable or recipes.experimental
        for _,ingredient in pairs(recipes.stable.ingredients) do
            local itemLink, itemName = getLinkAndName(ingredient.item, true, true)
            ingredientSection = ingredientSection .. "<data><default>'''" .. formatNumber(ingredient.amount) .. " &times; {{ItemLink|" .. itemLink .. "|" .. itemName .. "}}'''</default></data>"
        end
        ingredientSection = [=[<group collapse="open"><header>]=] .. loc.groups.ingredients .. [=[</header>]=] .. ingredientSection .. [=[</group>]=]

    -- else use a tabber section to show branch recipes completely separately
    elseif recipes.stable ~= nil and recipes.stable ~= recipes.experimental then
        ingredientSection = "<panel>"
        -- ensure stable is always first
        for _,branchName in ipairs({"stable", "experimental"}) do
            local recipe = recipes[branchName]
            if recipe ~= nil then
                ingredientSection = ingredientSection .. [=[<section><label>]=] .. loc.branches[branchName] .. [=[</label><group collapse="open"><header>]=] .. loc.groups.ingredients .. [=[</header>]=]

                -- list ingredients in individual data rows
                for _,ingredient in pairs(recipe.ingredients) do
                    local itemLink, itemName = getLinkAndName(ingredient.item, true, true)
                    ingredientSection = ingredientSection .. "<data><default>'''" .. formatNumber(ingredient.amount) .. " &times; {{ItemLink|" .. itemLink .. "|" .. itemName .. "}}'''</default></data>"
                end
            end

            ingredientSection = ingredientSection .. "</group></section>"
        end
        ingredientSection = ingredientSection .. "</panel>"
    end

    -- whether is a vehicle or a building
    local isVehicle = getBranchValues("isVehicle", target.stable, target.experimental)
    isVehicle = {(isVehicle.stable or isVehicle.experimental) and loc.groups.vehicle or loc.groups.building}
    local powerGenerated = formatBranchDiff(getBranchValues("powerGenerated", target.stable, target.experimental))
    if powerGenerated ~= "" then
    	powerGenerated = powerGenerated .. loc.units.mw
    end

    -- infobox
    local xml = [=[<infobox>
<title><default>]=] .. formatBranchDiff(getBranchValues("name", target.stable, target.experimental), true) .. [=[</default></title>
<image><default>]=] .. frame:callParserFunction("#setmainimage", buildingLink .. ".png") .. [=[</default></image>
<data><default><i>]=]  .. formatBranchDiff(getBranchValues("description", target.stable, target.experimental)) .. [=[</i></default></data>
]=] .. infoboxRow("unlockedBy", target) .. [=[
]=] .. infoboxRow("className", target) .. [=[

<group collapse="open"><header>]=] .. isVehicle[1] .. [=[</header>
]=] .. (isVehicle[1] == loc.groups.building and infoboxRow("powerUsage", target, loc.units.mw) or "") .. [=[
<data><label>]=] .. (isVehicle[1] == loc.groups.vehicle and loc.labels.fuelBurnRate or loc.labels.powerGenerated) .. [=[</label><default>]=] .. powerGenerated .. [=[</default></data>
<data><label>]=] .. loc.labels.overclockable .. [=[</label><default>]=] .. (not (frame:getParent().args.overclockable == nil or frame:getParent().args.overclockable == "") and (frame:getParent().args.overclockable == "1" and loc.labels.yes or loc.labels.no) or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.inventorySize .. [=[</label><default>]=] .. (frame:getParent().args.inventorySize or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.beltInputs .. [=[</label><default>]=] .. (frame:getParent().args.beltInputs or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.beltOutputs .. [=[</label><default>]=] .. (frame:getParent().args.beltOutputs or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.pipeInputs .. [=[</label><default>]=] .. (frame:getParent().args.pipeInputs or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.pipeOutputs .. [=[</label><default>]=] .. (frame:getParent().args.pipeOutputs or "") .. [=[</default></data>
<data><label><span title="]=] .. loc.tooltips.maxSpeed .. [=[">]=] .. loc.labels.maxSpeed .. [=[</span></label><default>]=] .. (not (frame:getParent().args.maxSpeed == nil or frame:getParent().args.maxSpeed == "") and (frame:getParent().args.maxSpeed .. loc.units.kmh) or "") .. [=[</default></data>
<data><label><span title="]=] .. loc.tooltips.time0_50 .. [=[">]=] .. loc.labels.time0_50 .. [=[</span></label><default>]=] .. (not (frame:getParent().args.time0_50 == nil or frame:getParent().args.time0_50 == "") and (frame:getParent().args.time0_50 .. loc.units.sec) or "") .. [=[</default></data>
</group>

<group collapse="open"><header>]=] .. loc.groups.dimensions .. [=[</header>
<data><label>]=] .. loc.labels.size_width .. [=[</label><default>]=] .. (not (frame:getParent().args.size_width == nil or frame:getParent().args.size_width == "") and (frame:getParent().args.size_width .. loc.units.m) or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.size_length .. [=[</label><default>]=] .. (not (frame:getParent().args.size_length == nil or frame:getParent().args.size_length == "") and (frame:getParent().args.size_length .. loc.units.m) or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.size_height .. [=[</label><default>]=] .. (not (frame:getParent().args.size_height == nil or frame:getParent().args.size_height == "") and (frame:getParent().args.size_height .. loc.units.m) or "") .. [=[</default></data>
<data><label>]=] .. loc.labels.size_area .. [=[</label><default>]=] .. ((tonumber(frame:getParent().args.size_width) and tonumber(frame:getParent().args.size_length)) and (tonumber(frame:getParent().args.size_width)*tonumber(frame:getParent().args.size_length) .. loc.units.m2) or "") .. [=[</default></data>
<data><default>]=] .. (frame:getParent().args.size_note or "") .. [=[</default></data>
</group>]=] .. fuelSection .. ingredientSection .. [=[
</infobox>]=]

    -- categories
	local categories = ""
	if not (frame:getParent().args.nocat ~= nil and frame:getParent().args.nocat == "1") then
		if isVehicle[1] == loc.groups.vehicle then
			categories = loc.categories.vehicles
		else
			categories = loc.categories.buildings
		end
	end

    return mw.getCurrentFrame():preprocess(xml) .. categories
end


return p