Модуль:Serialization/EntityTableSelector: различия между версиями
Материал из Space Station 14 Вики
Pok (обсуждение | вклад) Нет описания правки |
Pok (обсуждение | вклад) Нет описания правки |
||
| Строка 190: | Строка 190: | ||
end | end | ||
return "{{предмет|".. id .."|" .. amount .. " <span>" .. prob .. "</span>|wrapper=1|repository=1}}" | |||
end | end | ||
Версия от 06:06, 28 марта 2026
Для документации этого модуля может быть создана страница Модуль:Serialization/EntityTableSelector/doc
local p = {}
local getArgs = require('Module:Arguments').getArgs
local JsonPaths = require('Module:JsonPaths')
local entityTableDataCache
local function trim(value)
return mw.text.trim(value or "")
end
local function deepCopy(source)
if type(source) ~= "table" then
return source
end
local copy = {}
for key, value in pairs(source) do
copy[key] = deepCopy(value)
end
return copy
end
local function deepMerge(target, source)
if type(target) ~= "table" or type(source) ~= "table" then
return
end
for key, value in pairs(source) do
if type(value) == "table" and type(target[key]) == "table" then
deepMerge(target[key], value)
else
target[key] = deepCopy(value)
end
end
end
local function loadEntityTableData()
if entityTableDataCache ~= nil then
return entityTableDataCache
end
local moduleName = JsonPaths.get("prototype/entityTable.json")
local ok, data = pcall(mw.loadData, moduleName)
if not ok or type(data) ~= "table" then
entityTableDataCache = {}
return entityTableDataCache
end
entityTableDataCache = data
return entityTableDataCache
end
local function resolveEntry(data, id)
if type(data) ~= "table" or id == nil or id == "" then
return nil
end
local base = type(data["default"]) == "table" and deepCopy(data["default"]) or nil
local idsTable = type(data.id) == "table" and data.id or nil
local specific = idsTable and idsTable[id] or data[id]
if type(specific) ~= "table" then
return base
end
if base then
deepMerge(base, specific)
return base
end
return deepCopy(specific)
end
local function cloneSelector(value)
if type(value) ~= "table" then
return nil
end
local selector = deepCopy(value)
selector["!type"] = selector["!type"] or ""
return selector
end
local function normalizeSelector(value)
if type(value) ~= "table" then
return nil
end
if type(value["!type"]) == "string" and value["!type"] ~= "" then
return cloneSelector(value)
end
for key, inner in pairs(value) do
if type(key) == "string" and string.sub(key, 1, 6) == "!type:" and type(inner) == "table" then
local selector = deepCopy(inner)
selector["!type"] = string.sub(key, 7)
return selector
end
end
if type(value.table) == "table" then
return normalizeSelector(value.table)
end
return nil
end
local function normalizePercent(value, multiplyByHundred)
if type(value) ~= "number" then
return ""
end
local percent = multiplyByHundred and (value * 100) or value
if percent == math.floor(percent) then
percent = math.floor(percent)
end
return tostring(percent) .. "%"
end
local function parseRange(rangeText)
if type(rangeText) ~= "string" then
return nil
end
local min, max = rangeText:match("(%d+),%s*(%d+)")
min = tonumber(min)
max = tonumber(max)
if not min or not max then
return nil
end
return min + 1, max + 1
end
local function getSelectorValue(selector)
selector = normalizeSelector(selector) or selector
if type(selector) ~= "table" then
return nil
end
if selector.value ~= nil then
return selector.value
end
if type(selector.range) == "string" then
local min, max = parseRange(selector.range)
if min and max then
return tostring(min) .. "-" .. tostring(max)
end
end
return nil
end
local function formatAmount(amountSelector)
local value = getSelectorValue(amountSelector)
if value == nil or value == 1 or value == "1" then
return ""
end
return " [" .. tostring(value) .. "]"
end
local function formatRolls(rollSelector)
local value = getSelectorValue(rollSelector)
if value == nil then
return ""
end
return "[" .. tostring(value) .. "]"
end
local function formatContent(selector)
if type(selector) ~= "table" or trim(selector.id) == "" then
return "Ошибка: отсутствует id у элемента."
end
local id = selector.id
local amount = formatAmount(selector.amount)
local prob = ""
if type(selector.prob) == "number" then
prob = normalizePercent(selector.prob, true)
elseif type(selector.weight) == "number" then
prob = normalizePercent(selector.weight, false)
end
return "{{предмет|".. id .."|" .. amount .. " <span>" .. prob .. "</span>|wrapper=1|repository=1}}"
end
local renderSelector
local renderTableById
local function renderChildren(children, visited)
if type(children) ~= "table" then
return ""
end
local result = {}
for _, child in ipairs(children) do
local selector = normalizeSelector(child)
if selector then
local rendered = renderSelector(selector, visited, false)
if rendered ~= "" then
result[#result + 1] = rendered
end
end
end
return table.concat(result)
end
local function renderAllSelector(selector, visited)
return renderChildren(selector.children, visited)
end
local function renderNestedSelector(selector, visited, wrapped)
if trim(selector.tableId) == "" then
return ""
end
local result = {}
local suffix = ""
if wrapped then
local rolls = formatRolls(selector.rolls)
local prob = ""
if rolls ~= "" then
suffix = suffix .. ", максимум может выпасть: " .. rolls
end
if type(selector.prob) == "number" then
prob = " <div>" .. normalizePercent(selector.prob, true) .. "</div>"
elseif type(selector.weight) == "number" then
prob = " <div>" .. normalizePercent(selector.weight, false) .. "</div>"
end
suffix = suffix .. prob
if suffix ~= "" then
result[#result + 1] = "{{LinkCard/Сollapsible|название=Группа предметов" .. suffix .. "|содержание="
end
end
result[#result + 1] = renderTableById(selector.tableId, visited)
if wrapped and suffix ~= "" then
result[#result + 1] = "}}"
end
return table.concat(result)
end
local function renderGroupSelector(selector, visited)
if type(selector.children) ~= "table" then
return ""
end
local wrapperStart = ""
local wrapperEnd = ""
if selector.weight ~= nil and selector.weight ~= "default" then
wrapperStart = "{{LinkCard/Сollapsible|название=Группа предметов " .. normalizePercent(selector.weight, false) .. "|содержание="
wrapperEnd = "}}"
elseif selector.weight == nil then
wrapperStart = "{{LinkCard/Сollapsible|название=Может выпасть лишь один из:|содержание="
wrapperEnd = "}}"
end
local result = {}
for _, child in ipairs(selector.children) do
local childSelector = normalizeSelector(child)
if childSelector then
if childSelector["!type"] == "AllSelector" then
local rendered = renderAllSelector(childSelector, visited)
if rendered ~= "" then
result[#result + 1] = "{{LinkCard/Сollapsible|название=Выпадают только вместе:|содержание=" .. rendered .. "}}"
end
elseif childSelector["!type"] == "GroupSelector" then
result[#result + 1] = renderGroupSelector(childSelector, visited)
elseif childSelector["!type"] == "NestedSelector" then
result[#result + 1] = renderNestedSelector(childSelector, visited, true)
elseif childSelector["!type"] == "EntSelector" then
result[#result + 1] = formatContent(childSelector)
end
end
end
return wrapperStart .. table.concat(result) .. wrapperEnd
end
renderTableById = function(tableId, visited)
visited = visited or {}
tableId = trim(tableId)
if tableId == "" then
return ""
end
if visited[tableId] then
return ""
end
visited[tableId] = true
local data = loadEntityTableData()
local entry = resolveEntry(data, tableId)
local selector = entry and normalizeSelector(entry.table) or nil
if not selector then
return "Таблица не найдена."
end
return renderSelector(selector, visited, false)
end
renderSelector = function(selector, visited, wrapped)
selector = normalizeSelector(selector)
if not selector then
return ""
end
if selector["!type"] == "EntSelector" then
return formatContent(selector)
elseif selector["!type"] == "AllSelector" then
return renderAllSelector(selector, visited)
elseif selector["!type"] == "GroupSelector" then
return renderGroupSelector(selector, visited)
elseif selector["!type"] == "NestedSelector" then
return renderNestedSelector(selector, visited, wrapped ~= false)
end
return ""
end
function p.main(frame)
local args = getArgs(frame, { removeBlanks = false })
local jsonText = mw.text.unstripNoWiki(args[1] or args.json or "")
if trim(jsonText) == "" then
return "Не указан JSON."
end
local ok, data = pcall(mw.text.jsonDecode, jsonText)
if not ok or type(data) ~= "table" then
return "Некорректный JSON."
end
local selector = normalizeSelector(data)
if not selector then
return "Не удалось определить тип селектора."
end
return frame:preprocess(renderSelector(selector, {}, false))
end
return p