Module:Storage grid
From Space Station 14 Wiki
Module documentation
|
---|
View or edit this documentation • (about module documentation) |
Implements {{Storage grid}}.
local p = {}
local getArgs = require('Module:Arguments').getArgs
local empty_slot_image = "InterfacePlasmafireStorage tile empty.png"
-- =================================
local function assert_not_nil(value, error_message)
if value == nil then
if error_message == nil then
error("value is nil")
else
error(error_message)
end
end
end
local function numeric_table_length(t)
local count = 0
for _ in ipairs(t) do count = count + 1 end
return count
end
local function table_length(t)
local count = 0
for _ in pairs(t) do count = count + 1 end
return count
end
local function map_table(t, f)
local newt = {}
for k, v in pairs(t) do
newt[k] = f(k, v, t)
end
return newt
end
local function trim_map_cb(key, value, tbl)
end
local function tonumber_map_cb(key, value, tbl)
return tonumber(value)
end
-- =================================
-- Attempts to convert a table to a grid.
-- The table is expected to be 4 items long, with each item being a number.
-- Throws an error otherwise.
local function table_to_grid(tbl, mode)
if numeric_table_length(tbl) ~= 4 then
error("failed to convert a table to a grid: expected 4 items, found: " + numeric_table_length(tbl))
end
-- validate each item
for i, value in ipairs(tbl) do
if type(value) ~= "number" then
error("failed to convert a table to a grid: found a non-number item '" .. value .. "' at index '" .. i .. "'")
end
end
if mode == "position x position" then
return {
x = tbl[1],
y = tbl[2],
w = tbl[3],
h = tbl[4],
}
elseif mode == "position x size" then
return {
x = tbl[1],
y = tbl[2],
w = tbl[3] - tbl[1] + 1,
h = tbl[4] - tbl[2] + 1,
}
else
error("failed to convert a table to a grid: unknown mode: " + mode)
end
end
-- Parses grids from the `args` coming from a template call.
-- Returns a num table with Grid values.
-- Each Grid is a table with `x`, `y`, `w` and `h` numeric properties.
--
-- Throws error if parsing fails.
local function parse_grids(args, mode)
local grids = {}
for _, grid_str in ipairs(args) do
local split = mw.text.split(grid_str, ",%s*")
local number_mapped = map_table(split, tonumber_map_cb)
local grid = table_to_grid(number_mapped, mode)
table.insert(grids, grid)
end
return grids
end
-- Generates a grid that wraps all the grids together.
-- The resulting grid would be of size enough to fit all grids inside.
local function generate_wrapper_grid(grids)
if numeric_table_length(grids) == 0 then
return { x = 0, y = 0, w = 0, h = 0 }
end
local minX = 99999999999999
local maxX = -99999999999999
local minY = 99999999999999
local maxY = -99999999999999
for _, grid in ipairs(grids) do
if grid.x < minX then minX = grid.x end
if grid.x + grid.w > maxX then maxX = grid.x + grid.w end
if grid.y < minY then minY = grid.y end
if grid.y + grid.h > maxY then maxY = grid.y + grid.h end
end
return {
x = minX,
y = minY,
w = maxX - minX,
h = maxY - minY
}
end
-- Converts XY coords to flat index.
local function xy_to_i(x, y, width)
return y * width + x
end
-- Converts flat index to XY coords.
local function i_to_xy(i, width)
local y = math.floor(i / width)
return {
i - y * width,
y
}
end
-- =================================
function p.main(frame)
local args = getArgs(frame)
local mode = args.mode and string.lower(args.mod) or "position x position"
-- mode aliases
if mode == "pos pos" then mode = "position x position" end
if mode == "pos size" then mode = "position x size" end
-- =========================
local grids = parse_grids(args, mode)
local wrapper_grid = generate_wrapper_grid(grids)
-- A map from grid index (xy-based, 0-based) to whether that xy is filled.
local grid_i_to_filled = {}
-- Mark filled grid cells to lookup later.
for _, grid in ipairs(grids) do
for x = grid.x, grid.x + grid.w - 1 do
for y = grid.y, grid.y + grid.h - 1 do
grid_i_to_filled[xy_to_i(x, y, wrapper_grid.w)] = true
end
end
end
local grid_el = mw.html.create('div')
:addClass("storage-grid")
:css("grid-template-columns", "repeat(" .. wrapper_grid.w .. ", 1fr)")
:css("grid-template-rows", "repeat(" .. wrapper_grid.h .. ", 1fr)")
for i = 1, wrapper_grid.w * wrapper_grid.h do
-- -1 index because its zero based for that map
if grid_i_to_filled[i - 1] then
-- if cell is filled, mark it with 'f' (for filled) class.
grid_el:wikitext("[[File: " .. empty_slot_image .. "|class=f]]")
else
grid_el:tag('div')
end
end
return grid_el:allDone()
end
return p