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

Материал из Space Station 14 Вики
Нет описания правки
Нет описания правки
 
(не показано 9 промежуточных версий этого же участника)
Строка 2: Строка 2:
local JsonPaths = require('Module:JsonPaths')
local JsonPaths = require('Module:JsonPaths')


local fileExistsCache = {}
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 = {}
 
if entry.sprite then
table.insert(parts, "sprite=" .. normalizeSpritePath(entry.sprite))
end
 
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
layerParts[#layerParts+1] = k .. "=" .. tostring(v)
end
 
table.sort(layerParts)
table.insert(layers, table.concat(layerParts, ","))
end
 
table.sort(layers)
table.insert(parts, "layers=" .. table.concat(layers, "|"))
end
 
return table.concat(parts, ";")
end


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


Строка 11: Строка 48:
local result = {}
local result = {}


if entry.layers and entry.sprite then
if entry.layers and type(entry.layers) == "table" then
for _, layer in ipairs(entry.layers) do
for _, layer in ipairs(entry.layers) do
if layer.visible ~= false then
if layer.visible ~= false then
table.insert(result, {
table.insert(result, {
state = tostring(layer.state or entry.state or ""),
state = tostring(layer.state or entry.state or ""),
sprite = entry.sprite
sprite = normalizeSpritePath(layer.sprite or entry.sprite)
})
})
end
end
Строка 23: Строка 60:
table.insert(result, {
table.insert(result, {
state = tostring(entry.state or ""),
state = tostring(entry.state or ""),
sprite = entry.sprite
sprite = normalizeSpritePath(entry.sprite)
})
})
end
end
Строка 37: Строка 74:
end
end


local function batchFileExists(names)
local function splitCsv(value)
local result = {}
local result = {}
local batch = {}
local seen = {}


local function flush()
if value == nil then
if #batch == 0 then
return nil
return
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
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 titles = mw.title.newBatch(batch, 6):lookupExistence():getTitles()
local function hasAnyParent(parents, set)
for i, title in ipairs(titles) do
if not parents or not set then
local name = batch[i]
return false
fileExistsCache[name] = title and title.exists or false
end
 
for _, parent in ipairs(parents) do
if set[parent] then
return true
end
end
end


batch = {}
return false
seen = {}
end
end


for _, name in ipairs(names) do
local function shouldIncludeEntry(entry, whitelistSet, blacklistSet)
if fileExistsCache[name] == nil and not seen[name] then
local parents = getParents(entry)
seen[name] = true
table.insert(batch, name)


if #batch == 25 then
if whitelistSet then
flush()
if not hasAnyParent(parents, whitelistSet) then
end
return false
end
end
end
end


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


for _, name in ipairs(names) do
return true
result[name] = fileExistsCache[name] or false
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
end


Строка 81: Строка 174:


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


for _, group in pairs(spriteGroups) do
spriteGroups[key] = spriteGroups[key] or {}
local g = group[1]
table.insert(spriteGroups[key], { id = id, entry = entry })
 
if entry.sprite == g.entry.sprite and entry.state == g.entry.state then
table.insert(group, { id = id, entry = entry })
found = true
break
end
end
 
if not found then
table.insert(spriteGroups, {
{ id = id, entry = entry }
})
end
end
end


Строка 105: Строка 185:
if #group > 1 then
if #group > 1 then
local idLinks = {}
local idLinks = {}
 
for _, obj in pairs(group) do
for _, obj in ipairs(group) do
local id = obj.id
local id = obj.id
local prefix = getPrefix(id, project)
local prefix = getPrefix(id, project)
 
table.insert(idLinks, "[[:Файл:" .. prefix .. id .. ".png]]")
table.insert(idLinks, "[[:Файл:" .. prefix .. id .. ".png]]")
end
end
 
local firstId = group[1].id
local prefix = getPrefix(firstId, project)
table.insert(result, mw.getCurrentFrame():preprocess(
table.insert(result, mw.getCurrentFrame():preprocess(
"{{Entity Sprite/Repeat|" ..
"{{Entity Sprite/Repeat|спрайты=" .. table.concat(idLinks, " ") ..
table.concat(idLinks, " ") ..
"|перенаправление=" .. prefix .. firstId ..
"|" .. group[1].id ..
"|id=" .. firstId ..
"}}"
"}}"
))
))
Строка 139: Строка 222:
local links = {}
local links = {}
for _, item in ipairs(states) do
for _, item in ipairs(states) do
local url = baseUrl .. item.sprite .. "/" .. item.state .. ".png"
if item.sprite and item.state then
table.insert(links, "[" .. url .. " " .. item.state .. "]")
local url = baseUrl .. item.sprite .. "/" .. item.state .. ".png"
table.insert(links, "[" .. url .. " " .. item.state .. "]")
end
end
end
stateStr = table.concat(links, ", ")
stateStr = table.concat(links, ", ")
Строка 157: Строка 242:
local action = frame.args[1]
local action = frame.args[1]
local json = frame.args.json or "sprite_entity.json"
local json = frame.args.json or "sprite_entity.json"
local checkFile = frame.args.checkFile or ""
if checkFile == "" then checkFile = false end


local project = JsonPaths.project()
local project = JsonPaths.project()
local baseUrl = JsonPaths.git() .. "/Resources/Textures/"
local baseUrl = JsonPaths.git() .. "/Resources/Textures/"
local dataPage = JsonPaths.get(json)
local dataPage = JsonPaths.get(json)
local prototypesPage = JsonPaths.get("entity prototypes.json")
local spriteData = mw.loadData(dataPage)
local spriteData = mw.loadData(dataPage)
local prototypeData = mw.loadData(prototypesPage)


if not spriteData or type(spriteData) ~= "table" then
if not spriteData or type(spriteData) ~= "table" then
Строка 169: Строка 256:
end
end


if action == "repeat" then
if not prototypeData or type(prototypeData) ~= "table" then
return generateRepeatTemplate(spriteData, project)
return "Ошибка загрузки JSON: " .. prototypesPage
 
end
elseif action == "image" then
local items = {}
local fileNames = {}


for id, entry in pairs(spriteData) do
local whitelistSet = buildSet(frame.args.whitelistParent)
local prefix = getPrefix(id, project)
local blacklistSet = buildSet(frame.args.blacklistParent)
local fileName = prefix .. id .. ".png"


table.insert(items, {
local filteredData = filterSpriteData(spriteData, prototypeData, whitelistSet, blacklistSet)
id = id,
entry = entry,
fileName = fileName
})


if checkFile then
if action == "repeat" then
table.insert(fileNames, fileName)
return generateRepeatTemplate(filteredData, project)
end
end
 
local existsMap = nil
if checkFile then
existsMap = batchFileExists(fileNames)
end


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


for _, item in ipairs(items) do
for id, entry in pairs(filteredData) do
local skip = false
local t = generateTemplate(id, entry, baseUrl, project)
 
if t then
if checkFile and existsMap[item.fileName] then
table.insert(result, t)
skip = true
end
 
if not skip then
local t = generateTemplate(item.id, item.entry, baseUrl, project)
if t then
table.insert(result, t)
end
end
end
end
end

Текущая версия от 03:10, 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 = {}

	if entry.sprite then
		table.insert(parts, "sprite=" .. normalizeSpritePath(entry.sprite))
	end

	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
				layerParts[#layerParts+1] = k .. "=" .. tostring(v)
			end

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

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

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

local function getSpritePath(entry)
	return normalizeSpritePath(entry.sprite)
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 then
				table.insert(result, {
					state = tostring(layer.state or entry.state or ""),
					sprite = normalizeSpritePath(layer.sprite or entry.sprite)
				})
			end
		end
	elseif entry.state and entry.sprite then
		table.insert(result, {
			state = tostring(entry.state or ""),
			sprite = normalizeSpritePath(entry.sprite)
		})
	end

	return (#result > 0) and result or nil
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 generateRepeatTemplate(data, project)
	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 prefix = getPrefix(id, project)
	
				table.insert(idLinks, "[[:Файл:" .. prefix .. id .. ".png]]")
			end
	
			local firstId = group[1].id
			local prefix = getPrefix(firstId, project)
	
			table.insert(result, mw.getCurrentFrame():preprocess(
				"{{Entity Sprite/Repeat|спрайты=" .. table.concat(idLinks, " ") ..
				"|перенаправление=" .. 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