Jump to content

Module:UI: Difference between revisions

From The Perfect Tower II
Exe boss (talk | contribs)
Add inline styles (workaround until this Wiki gets Extension:TemplateStyles); remove `pixel‑image` class from the root node
No edit summary
Line 6: Line 6:
local slot = require("Module:Factory slot").slot;
local slot = require("Module:Factory slot").slot;


-- Workaround until this Wiki gets [[mw:Extension:TemplateStyles]]
local formatNumber = require("Module:FormatNumber").format;
local styles = {
[".factoryui"] = [[
display: block;
background-color: #CF3F3F;
color: #FFF;
padding: 1em;
]],


[".factoryui-row"] = [[
-- The look of the Factory UIs is in [[Template:Crafting/styles.css]] (TemplateStyles).
display: flex;
local STYLES = "Template:Crafting/styles.css";
]],
 
[".factoryui-crafting .factoryui-input"] = [[
display: flex;
flex-direction: column;
line-height: 0;
margin-bottom: 1.5em;
]],
 
[".factoryui-crafting .factoryui-output"] = [[
display: flex;
align-items: center;
]],
 
[".factoryui-crafting .factoryui-cost"] = [[
margin-left: 1.5em;
]],
};


local p = {};
local p = {};
Line 149: Line 124:
local root = mw.html.create("div")
local root = mw.html.create("div")
:addClass("factoryui factoryui-crafting")
:addClass("factoryui factoryui-crafting")
:cssText(styles[".factoryui"]);
:wikitext(mw.getCurrentFrame():extensionTag("templatestyles", "", { src = STYLES }));


-- Input grid
-- Input grid
do
do
local input = root:tag("div")
local input = root:tag("div")
:addClass("factoryui-input")
:addClass("factoryui-input");
:cssText(styles[".factoryui-crafting .factoryui-input"]);


local rows = 0;
local rows = 0;
Line 161: Line 135:
rows = rows + 1;
rows = rows + 1;
local rowHtml = input:tag("div")
local rowHtml = input:tag("div")
:addClass("factoryui-row")
:addClass("factoryui-row");
:cssText(styles[".factoryui-row"]);


local cols = 0;
local cols = 0;
Line 187: Line 160:


local rowHtml = input:tag("div")
local rowHtml = input:tag("div")
:addClass("factoryui-row")
:addClass("factoryui-row");
:cssText(styles[".factoryui-row"]);


for _ = 1, unlockedCols do
for _ = 1, unlockedCols do
Line 201: Line 173:
rows = rows + 1;
rows = rows + 1;
local rowHtml = input:tag("div")
local rowHtml = input:tag("div")
:addClass("factoryui-row locked")
:addClass("factoryui-row locked");
:cssText(styles[".factoryui-row"]);


for _ = 1, 8 do
for _ = 1, 8 do
Line 213: Line 184:
do
do
local output = root:tag("div")
local output = root:tag("div")
:addClass("factoryui-output")
:addClass("factoryui-output");
:cssText(styles[".factoryui-crafting .factoryui-output"]);


output:node(createSlot(args, "Output", "O", "factoryslot-large"));
output:node(createSlot(args, "Output", "O", "factoryslot-large"));


-- The cost follows the reader's number format (scientific or named), like {{Number}}
local cost = tonumber(args.cost or 0);
output:tag("span")
output:tag("span")
:addClass("factoryui-cost")
:addClass("factoryui-cost")
:cssText(styles[".factoryui-crafting .factoryui-cost"])
:wikitext(
:wikitext(
p.formatDoubleInteger(args.cost or 0),
(cost and cost == cost) and formatNumber(math.max(cost, 0)) or p.formatDoubleInteger(args.cost),
" [[File:Red resource.png|x25px|link=]]"
" [[File:Red resource.png|x25px|link=]]"
)
)

Revision as of 22:14, 6 October 2026

Draws Factory interfaces. It has one so far, the crafting grid, which Module:Recipe table calls for {{Crafting}} (through Module:Crafting). Inspired by Minecraft Wiki's Module:UI. The look is in Template:Crafting/styles.css (TemplateStyles), which the grid adds itself.

  • crafting(args): the 8×4 input grid, the output slot and the cost.
    • The inputs are A1 to H4 (columns A to H, rows 1 to 4) and the output is Output. Each takes an item stack as described in Module:Factory slot, which draws the slots. A slot's link is A1link and so on, Olink for the output.
    • The smallest rectangle that holds all inputs, at least 2×2, is drawn from the top-left corner as open slots; the rest of the 8×4 grid is drawn as locked slots.
    • cost is the cost in Factory resources, formatted by Module:FormatNumber, so it follows the reader's number format (scientific notation or named numbers). It defaults to 0, and negative values show as 0.
  • formatDoubleInteger(amount, nonNegative): an older formatter for counts (1.5E6, ∞). The grid only calls it when cost isn't a number, and then it returns a "NaN" error.



-- Used to render Factory UIs
-- Inspired by Minecraft Wiki's [[Module:UI]]
require("Module:No globals");
local checkTypeMulti = require("libraryUtil").checkTypeMulti;
--- @type fun(args: table): MwHtml
local slot = require("Module:Factory slot").slot;

local formatNumber = require("Module:FormatNumber").format;

-- The look of the Factory UIs is in [[Template:Crafting/styles.css]] (TemplateStyles).
local STYLES = "Template:Crafting/styles.css";

local p = {};

-- Columns for Crafting grids
local COLUMNS = { "A", "B", "C", "D", "E", "F", "G", "H" }

--- Creates a Factory UI slot from the arguments named by `name`,
--- `(name or prefix) .. "link"`, and `(name or prefix) .. "title"`
---
--- @param args table
--- @param name string
--- @param prefix? string
--- @param class? string
--- @param default? string
local function createSlot(args, name, prefix, class, default)
	return slot{
		args[name],
		link	= args[(prefix or name) .. "link"],
		title	= args[(prefix or name) .. "title"],
		class	= class,
		default	= default,
	};
end

--- @param amount number Non-negative double precision float
local function implFormatDoubleInteger(amount)
	if (amount >= math.huge) then
		return "∞";
	end

	amount = math.floor(amount);
	if (amount < 1e6) then
		-- less than a million
		return tostring(amount);
	end

	local exp = 3;
	while true do
		exp = exp + 3;
		amount = math.floor(amount / 1e3 + 0.5);

		if (amount < 1e6) then
			--- @type string, string
			local result, b = tostring(amount):match("^(%d-%d)(%d%d%d)$");
			b = b:gsub("0+$", "");
			if (b ~= "") then
				result = result .. "." .. b;
			end
			result = result .. "e" .. exp;
			return result;
		end
	end
end

--- Formats a non-negative double precision integer value
---
--- @param amount string|number
--- @param nonNegative? boolean
--- @return string|nil
function p.formatDoubleInteger(amount, nonNegative)
	checkTypeMulti('"Module:UI".formatDoubleInteger', 1, amount, { "string", "number" });
	amount = tonumber(amount);
	if (amount == nil or amount ~= amount) then
		return mw.text.tag("strong", { class="error" }, "NaN");
	end

	if (nonNegative and amount < 0) then
		amount = 0;
	end

	local result = implFormatDoubleInteger(math.abs(amount));
	if (amount < 0) then
		result = "-" .. result;
	end
	return result;
end


--- Crafting grid (size scales with Factory tier)
---
--- @param args table
function p.crafting(args)
	--- @type integer, integer
	local minRow, maxRow = math.huge, -math.huge;

	--- @type integer, integer
	local minCol, maxCol = math.huge, -math.huge;

	for row = 1, 4 do
		for col = 1, 8 do
			local name = COLUMNS[col] .. row;
			if ((args[name] or "") ~= "") then
				minRow = math.min(minRow, row);
				maxRow = math.max(maxRow, row);

				minCol = math.min(minCol, col);
				maxCol = math.max(maxCol, col);
			end
		end
	end

	local unlockedRows = maxRow - minRow + 1;
	local unlockedCols = maxCol - minCol + 1;

	-- Minimum crafting grid size is 2×2
	if (unlockedRows < 2) then
		unlockedRows = 2;
	end
	if (unlockedCols < 2) then
		unlockedCols = 2;
	end

	local root = mw.html.create("div")
		:addClass("factoryui factoryui-crafting")
		:wikitext(mw.getCurrentFrame():extensionTag("templatestyles", "", { src = STYLES }));

	-- Input grid
	do
		local input = root:tag("div")
			:addClass("factoryui-input");

		local rows = 0;
		for row = minRow, maxRow do
			rows = rows + 1;
			local rowHtml = input:tag("div")
				:addClass("factoryui-row");

			local cols = 0;
			for col = minCol, maxCol do
				cols = cols + 1;

				local name = COLUMNS[col] .. row;
				rowHtml:node(createSlot(args, name));
			end

			while cols < unlockedCols do
				cols = cols + 1;
				rowHtml:node(slot{});
			end

			while cols < 8 do
				cols = cols + 1;
				rowHtml:node(slot{ class = "locked" });
			end
		end

		while rows < unlockedRows do
			rows = rows + 1;

			local rowHtml = input:tag("div")
				:addClass("factoryui-row");

			for _ = 1, unlockedCols do
				rowHtml:node(slot{});
			end
			for _ = unlockedCols + 1, 8 do
				rowHtml:node(slot{ class = "locked" });
			end
		end

		while rows < 4 do
			rows = rows + 1;
			local rowHtml = input:tag("div")
				:addClass("factoryui-row locked");

			for _ = 1, 8 do
				rowHtml:node(slot{ class = "locked" });
			end
		end
	end

	-- Output slot and Resource cost
	do
		local output = root:tag("div")
			:addClass("factoryui-output");

		output:node(createSlot(args, "Output", "O", "factoryslot-large"));

		-- The cost follows the reader's number format (scientific or named), like {{Number}}
		local cost = tonumber(args.cost or 0);
		output:tag("span")
			:addClass("factoryui-cost")
			:wikitext(
				(cost and cost == cost) and formatNumber(math.max(cost, 0)) or p.formatDoubleInteger(args.cost),
				" [[File:Red resource.png|x25px|link=]]"
			)
	end

	return root;
end

return p;