Модуль:Entity Sprite/all: различия между версиями

Материал из Space Station 14 Вики
Нет описания правки
Нет описания правки
Строка 179: Строка 179:


return result
return result
end
local function getFirstSpriteAndState(entry)
local spritePath = getSpritePath(entry)
local states = getSpriteStates(entry)
if not spritePath then
return nil, nil
end
if states and #states > 0 and states[1].state then
return spritePath, states[1].state
end
return spritePath, entry and entry.state or nil
end
end



Версия от 20:52, 22 марта 2026

Для документации этого модуля может быть создана страница Модуль:Entity Sprite/all/doc

local p = {}
local JsonPaths = require('Module:JsonPaths')

local function normalizeSpritePath(path)
	if path == nil then
		return nil
	end

	path = mw.text.trim(tostring(path))
	path = path:gsub("^/Textures/?", "")

	return path
end

local function buildEntryKey(entry)
	local parts = {}

	local function addField(name, value)
		if value ~= nil then
			table.insert(parts, name .. "=" .. tostring(value))
		end
	end

	addField("sprite", normalizeSpritePath(entry.sprite))
	addField("state", entry.state)
	addField("scale", entry.scale)
	addField("enableOverrideDir", entry.enableOverrideDir)
	addField("overrideDir", entry.overrideDir)

	if entry.layers and type(entry.layers) == "table" then
		local layers = {}

		for _, layer in ipairs(entry.layers) do
			local layerParts = {}

			for k, v in pairs(layer) do
				if type(v) ~= "table" then
					layerParts[#layerParts + 1] = k .. "=" .. tostring(v)
				end
			end

			table.sort(layerParts)
			table.insert(layers, table.concat(layerParts, ","))
		end

		table.sort(layers)
		table.insert(parts, "layers=" .. table.concat(layers, "|"))
	end

	table.sort(parts)
	return table.concat(parts, ";")
end

local function getSpriteStates(entry)
	local result = {}

	if entry.layers and type(entry.layers) == "table" then
		for _, layer in ipairs(entry.layers) do
			if layer.visible ~= false and layer.state then
				table.insert(result, {
					state = tostring(layer.state),
					sprite = normalizeSpritePath(layer.sprite or entry.sprite)
				})
			end
		end
	elseif entry.state and entry.sprite then
		table.insert(result, {
			state = tostring(entry.state),
			sprite = normalizeSpritePath(entry.sprite)
		})
	end

	return (#result > 0) and result or nil
end

local function getSpritePath(entry)
	return normalizeSpritePath(entry.sprite)
end

local function getPrefix(id, project)
	if project ~= "" and JsonPaths.has(id, project) then
		return project .. ":"
	end
	return ""
end

local function splitCsv(value)
	local result = {}

	if value == nil then
		return nil
	end

	value = mw.text.trim(tostring(value))
	if value == "" then
		return nil
	end

	for part in mw.text.gsplit(value, ",", true) do
		local item = mw.text.trim(part)
		if item ~= "" then
			table.insert(result, item)
		end
	end

	return (#result > 0) and result or nil
end

local function buildSet(value)
	local list = splitCsv(value)
	if not list then
		return nil
	end

	local set = {}
	for _, item in ipairs(list) do
		set[item] = true
	end

	return set
end

local function getParents(entry)
	if not entry then
		return nil
	end

	if type(entry.parents) == "table" then
		return entry.parents
	end

	if type(entry.parents) == "string" then
		return splitCsv(entry.parents)
	end

	return nil
end

local function hasAnyParent(parents, set)
	if not parents or not set then
		return false
	end

	for _, parent in ipairs(parents) do
		if set[parent] then
			return true
		end
	end

	return false
end

local function shouldIncludeEntry(entry, whitelistSet, blacklistSet)
	local parents = getParents(entry)

	if whitelistSet then
		if not hasAnyParent(parents, whitelistSet) then
			return false
		end
	end

	if blacklistSet and hasAnyParent(parents, blacklistSet) then
		return false
	end

	return true
end

local function filterSpriteData(spriteData, prototypeData, whitelistSet, blacklistSet)
	local result = {}

	for id, entry in pairs(spriteData) do
		local protoEntry = prototypeData and prototypeData[id] or entry

		if shouldIncludeEntry(protoEntry, whitelistSet, blacklistSet) then
			result[id] = entry
		end
	end

	return result
end

local function getFirstSpriteAndState(entry)
	local spritePath = getSpritePath(entry)
	local states = getSpriteStates(entry)

	if not spritePath then
		return nil, nil
	end

	if states and #states > 0 and states[1].state then
		return spritePath, states[1].state
	end

	return spritePath, entry and entry.state or nil
end

local function getRepeatSpriteUrl(entry, baseUrl)
	local spritePath = getSpritePath(entry)
	local states = getSpriteStates(entry)

	if not spritePath then
		return nil
	end

	if states and #states > 0 and states[1].state then
		return baseUrl .. spritePath .. "/" .. states[1].state .. ".png"
	end

	if entry and entry.state then
		return baseUrl .. spritePath .. "/" .. tostring(entry.state) .. ".png"
	end

	return baseUrl .. spritePath
end

local function generateRepeatTemplate(data, project, baseUrl)
	local spriteGroups = {}

	for id, entry in pairs(data) do
		local key = buildEntryKey(entry)

		spriteGroups[key] = spriteGroups[key] or {}
		table.insert(spriteGroups[key], { id = id, entry = entry })
	end

	local result = {}

	for _, group in pairs(spriteGroups) do
		if #group > 1 then
			local idLinks = {}

			for _, obj in ipairs(group) do
				local id = obj.id
				local entry = obj.entry
				local prefix = getPrefix(id, project)
				local spriteUrl = getRepeatSpriteUrl(entry, baseUrl)

				if spriteUrl then
					table.insert(idLinks, "[" .. spriteUrl .. " " .. prefix .. id .. "]")
				end
			end

			local firstId = group[1].id
			local firstEntry = group[1].entry
			local prefix = getPrefix(firstId, project)

			local spritePath, stateName = getFirstSpriteAndState(firstEntry)
			local spriteValue = spritePath or ""

			if stateName and stateName ~= "" then
				spriteValue = spriteValue .. " (" .. stateName .. ")"
			end

			table.insert(result, mw.getCurrentFrame():preprocess(
				"{{Entity Sprite/Repeat|спрайты=" .. table.concat(idLinks, " ") ..
				"|спрайт=" .. spriteValue ..
				"|перенаправление=" .. prefix .. firstId ..
				"|id=" .. firstId ..
				"}}"
			))
		end
	end

	return table.concat(result, "\n")
end

local function generateTemplate(id, entry, baseUrl, project)
	local spritePath = getSpritePath(entry)
	if not id or not spritePath then
		return nil
	end

	local prefix = getPrefix(id, project)

	local states = getSpriteStates(entry)
	local stateStr = ""

	if states then
		local links = {}
		for _, item in ipairs(states) do
			if item.sprite and item.state then
				local url = baseUrl .. item.sprite .. "/" .. item.state .. ".png"
				table.insert(links, "[" .. url .. " " .. item.state .. "]")
			end
		end
		stateStr = table.concat(links, ", ")
	end

	return mw.getCurrentFrame():preprocess(
		"{{Entity Sprite/Image|файл=" .. prefix .. id ..
		"|id=" .. id ..
		"|путь=" .. baseUrl .. spritePath ..
		"|state=" .. stateStr ..
		"}}"
	)
end

function p.main(frame)
	local action = frame.args[1]
	local json = frame.args.json or "sprite_entity.json"

	local project = JsonPaths.project()
	local baseUrl = JsonPaths.git() .. "/Resources/Textures/"

	local dataPage = JsonPaths.get(json)
	local prototypesPage = JsonPaths.get("entity prototypes.json")

	local spriteData = mw.loadData(dataPage)
	local prototypeData = mw.loadData(prototypesPage)

	if not spriteData or type(spriteData) ~= "table" then
		return "Ошибка загрузки JSON: " .. dataPage
	end

	if not prototypeData or type(prototypeData) ~= "table" then
		return "Ошибка загрузки JSON: " .. prototypesPage
	end

	local whitelistSet = buildSet(frame.args.whitelistParent)
	local blacklistSet = buildSet(frame.args.blacklistParent)

	local filteredData = filterSpriteData(spriteData, prototypeData, whitelistSet, blacklistSet)

	if action == "repeat" then
		return generateRepeatTemplate(filteredData, project)

	elseif action == "image" then
		local result = {}

		for id, entry in pairs(filteredData) do
			local t = generateTemplate(id, entry, baseUrl, project)
			if t then
				table.insert(result, t)
			end
		end

		return table.concat(result, "\n")
	end

	return nil
end

return p