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

Материал из Space Station 14 Вики
мНет описания правки
мНет описания правки
 
(не показано 18 промежуточных версий этого же участника)
Строка 1: Строка 1:
local p = {}
local p = {}


-- Загрузка данных
local function loadData(filePath)
    local page = mw.title.new(filePath)
    local content = page and page:getContent()
    return content and mw.text.jsonDecode(content) or nil
end
-- Проверка равенства двух таблиц
local function deepEqual(t1, t2)
local function deepEqual(t1, t2)
     if t1 == t2 then return true end
     if t1 == t2 then return true end
Строка 47: Строка 39:
end
end


-- Вспомогательная функция для получения поля без учёта регистра
local function findFieldInsensitive(tbl, fieldName)
local function getInsensitiveKey(t, target)
     for key, value in pairs(tbl) do
    target = target:lower()
         if type(key) == "string" and mw.ustring.lower(key) == mw.ustring.lower(fieldName) then
     for k, v in pairs(t) do
             return value
         if type(k) == "string" and k:lower() == target then
             return v
         end
         end
     end
     end
Строка 58: Строка 48:
end
end


-- Получение пути спрайта
local function getSpritePath(entry)
local function getSpritePath(entry)
     local spriteField = getInsensitiveKey(entry, "Sprite")
     local iconField = findFieldInsensitive(entry, "Icon")
     local iconField = getInsensitiveKey(entry, "Icon")
     local spriteField = findFieldInsensitive(entry, "Sprite")
 
   
     if spriteField and spriteField.sprite then
     if iconField and iconField.sprite then
        return iconField.sprite
    elseif spriteField and spriteField.sprite then
         return spriteField.sprite
         return spriteField.sprite
    elseif iconField and iconField.sprite then
        return iconField.sprite
     elseif spriteField and spriteField.layers then
     elseif spriteField and spriteField.layers then
         for _, layer in pairs(spriteField.layers) do
         for _, layer in pairs(spriteField.layers) do
Строка 77: Строка 66:
end
end


-- Генерация шаблона repeat
local function isStateFirstKey(layer)
    for k, _ in pairs(layer) do
        return k == "state"
    end
    return false
end
 
local function getSpriteStates(entry)
    local result = {}
 
    local function addState(state, sprite)
        table.insert(result, { state = state, sprite = sprite })
    end
 
    local spritePath = getSpritePath(entry)
 
    local iconBlock = findFieldInsensitive(entry, "Icon")
    local spriteBlock = findFieldInsensitive(entry, "Sprite")
 
    if iconBlock and iconBlock.state then
        addState(iconBlock.state, iconBlock.sprite or spritePath)
    else
        if spriteBlock and spriteBlock.layers then
            for _, layer in ipairs(spriteBlock.layers) do
                if layer.visible ~= false then
                    local stateName = layer.state or (iconBlock and iconBlock.state) or "icon"
                    local s = layer.sprite or (iconBlock and iconBlock.sprite) or spritePath
                    addState(stateName, s)
                    break
                end
            end
        elseif spriteBlock and spriteBlock.state then
            addState(spriteBlock.state, spriteBlock.sprite or spritePath)
        end
    end
 
    if spriteBlock and spriteBlock.layers then
        for _, layer in ipairs(spriteBlock.layers) do
            local alreadyAdded = false
            for _, r in ipairs(result) do
                local layerState = layer.state or "icon"
                if r.state == layerState then
                    alreadyAdded = true
                    break
                end
            end
 
            if not alreadyAdded and layer.visible ~= false then
                local stateName = layer.state or "icon"
                local s = layer.sprite or (iconBlock and iconBlock.sprite) or spritePath
 
                if s then
                    addState(stateName, s)
                end
            end
        end
    end
 
    if #result == 0 and (iconBlock and (iconBlock.sprite or iconBlock.state) or spritePath) then
        local s = (iconBlock and iconBlock.sprite) or spritePath
        addState("icon", s)
    end
 
    return (#result > 0) and result or nil
end
 
local function generateRepeatTemplate(data)
local function generateRepeatTemplate(data)
     local spriteGroups = {}
     local spriteGroups = {}
Строка 84: Строка 138:
         local found = false
         local found = false
         for _, group in pairs(spriteGroups) do
         for _, group in pairs(spriteGroups) do
             if deepEqual(getInsensitiveKey(entry, "Sprite"), getInsensitiveKey(group[1], "Sprite")) and
             if deepEqual(findFieldInsensitive(entry, "Sprite"), findFieldInsensitive(group[1], "Sprite")) and
               deepEqual(entry.EntityStorageVisuals, group[1].EntityStorageVisuals) and
               deepEqual(entry.EntityStorageVisuals, group[1].EntityStorageVisuals) and
               deepEqual(getInsensitiveKey(entry, "Icon"), getInsensitiveKey(group[1], "Icon")) then
               deepEqual(findFieldInsensitive(entry, "Icon"), findFieldInsensitive(group[1], "Icon")) then
                 table.insert(group, entry)
                 table.insert(group, entry)
                 found = true
                 found = true
Строка 105: Строка 159:
                 table.insert(idLinks, "[[:Файл:" .. entry.id .. ".png]]")
                 table.insert(idLinks, "[[:Файл:" .. entry.id .. ".png]]")
             end
             end
             table.insert(result, mw.getCurrentFrame():preprocess("{{Entity Sprite/Repeat|" .. table.concat(idLinks, " ") .. "|" .. group[1].id .. "}}"))
             table.insert(result, mw.getCurrentFrame():preprocess(
                "{{Entity Sprite/Repeat|" .. table.concat(idLinks, " ") .. "|" .. group[1].id .. "}}"
            ))
         end
         end
     end
     end
Строка 112: Строка 168:
end
end


-- Функция генерации шаблона по записи
local function generateTemplate(entry, param)
local function generateTemplate(entry, param, data)
     local spritePath = getSpritePath(entry)
     local spritePath = getSpritePath(entry)
     if not entry.id or not spritePath then
     if not entry.id or not spritePath then
         return nil
         return nil
    end
    local spriteField = getInsensitiveKey(entry, "Sprite")
    -- Определяем state
    local state = nil
    if spriteField and spriteField.state then
        state = spriteField.state
    elseif spriteField and spriteField.layers then
        for _, layer in pairs(spriteField.layers) do
            if layer.state then
                state = layer.state
                break
            end
        end
     end
     end


     if param == "image" then
     if param == "image" then
        local states = getSpriteStates(entry)
         local stateStr = ""
         local stateStr = ""
         if state then
         if states then
             stateStr = " (state: " .. state .. ")"
             local baseUrl = "https://github.com/space-syndicate/space-station-14/blob/master/Resources/Textures/"
            local links = {}
            for _, item in ipairs(states) do
                local spritePath = item.sprite
                local stateName = item.state
                local url = baseUrl .. spritePath .. "/" .. stateName .. ".png"
                table.insert(links, "[" .. url .. " " .. stateName .. "]")
            end
            stateStr = table.concat(links, ", ")
         end
         end
         return mw.getCurrentFrame():preprocess("{{Entity Sprite/Image|" .. entry.id .. "|https://github.com/space-syndicate/space-station-14/blob/master/Resources/Textures/" .. spritePath .. stateStr .. "}}")
         return mw.getCurrentFrame():preprocess(
            "{{Entity Sprite/Image|" .. entry.id ..
            "|https://github.com/space-syndicate/space-station-14/blob/master/Resources/Textures/" .. spritePath ..
            "|" .. stateStr .. "}}"
        )
     end
     end


Строка 146: Строка 199:


function p.main(frame)
function p.main(frame)
     local param = frame.args[1]
     local action = frame.args[1]
    local mode = frame.args[2]
 
    local dataPage
    if mode == "item" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/item.json/data"
    elseif mode == "clothing" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/clothing.json/data"
    elseif mode == "structure" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/structure.json/data"
    elseif mode == "mob" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/mob.json/data"
    elseif mode == "other" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/other.json/data"
    else
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite.json/data"
    end


     local data = loadData('User:IanComradeBot/prototypes/entity sprite.json')
     local spriteData = mw.loadData(dataPage)
     if not data or type(data) ~= 'table' then
     if not spriteData or type(spriteData) ~= "table" then
         return 'Ошибка: Невозможно загрузить данные из JSON.'
         return "Ошибка: Невозможно загрузить данные из JSON (" .. dataPage .. ")."
     end
     end


     if param == "repeat" then
     if action == "repeat" then
         return generateRepeatTemplate(data)
         return generateRepeatTemplate(spriteData)
     elseif param == "image" or param == "path" then
     elseif action == "image" then
         local result = {}
         local result = {}
         for _, entry in pairs(data) do
         for _, entry in pairs(spriteData) do
             local template = generateTemplate(entry, param, data)
             local template = generateTemplate(entry, action)
             if template then
             if template then
                 table.insert(result, template)
                 table.insert(result, template)

Текущая версия от 19:56, 3 ноября 2025

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

local p = {}

local function deepEqual(t1, t2)
    if t1 == t2 then return true end
    if type(t1) ~= "table" or type(t2) ~= "table" then return false end

    local function isArray(t)
        local count = 0
        for k in pairs(t) do
            if type(k) ~= "number" then return false end
            count = count + 1
        end
        return count == #t
    end

    if isArray(t1) and isArray(t2) then
        if #t1 ~= #t2 then return false end
        for i = 1, #t1 do
            if not deepEqual(t1[i], t2[i]) then
                return false
            end
        end
        return true
    end

    for k, v in pairs(t1) do
        if t2[k] == nil or not deepEqual(v, t2[k]) then
            return false
        end
    end

    for k, v in pairs(t2) do
        if t1[k] == nil or not deepEqual(v, t1[k]) then
            return false
        end
    end

    return true
end

local function findFieldInsensitive(tbl, fieldName)
    for key, value in pairs(tbl) do
        if type(key) == "string" and mw.ustring.lower(key) == mw.ustring.lower(fieldName) then
            return value
        end
    end
    return nil
end

local function getSpritePath(entry)
    local iconField = findFieldInsensitive(entry, "Icon")
    local spriteField = findFieldInsensitive(entry, "Sprite")
    
    if iconField and iconField.sprite then
        return iconField.sprite
    elseif spriteField and spriteField.sprite then
        return spriteField.sprite
    elseif spriteField and spriteField.layers then
        for _, layer in pairs(spriteField.layers) do
            if layer.sprite then
                return layer.sprite
            end
        end
    end
    return nil
end

local function isStateFirstKey(layer)
    for k, _ in pairs(layer) do
        return k == "state"
    end
    return false
end

local function getSpriteStates(entry)
    local result = {}

    local function addState(state, sprite)
        table.insert(result, { state = state, sprite = sprite })
    end

    local spritePath = getSpritePath(entry)

    local iconBlock = findFieldInsensitive(entry, "Icon")
    local spriteBlock = findFieldInsensitive(entry, "Sprite")

    if iconBlock and iconBlock.state then
        addState(iconBlock.state, iconBlock.sprite or spritePath)
    else
        if spriteBlock and spriteBlock.layers then
            for _, layer in ipairs(spriteBlock.layers) do
                if layer.visible ~= false then
                    local stateName = layer.state or (iconBlock and iconBlock.state) or "icon" 
                    local s = layer.sprite or (iconBlock and iconBlock.sprite) or spritePath 
                    addState(stateName, s)
                    break
                end
            end
        elseif spriteBlock and spriteBlock.state then
            addState(spriteBlock.state, spriteBlock.sprite or spritePath)
        end
    end

    if spriteBlock and spriteBlock.layers then
        for _, layer in ipairs(spriteBlock.layers) do
            local alreadyAdded = false
            for _, r in ipairs(result) do
                local layerState = layer.state or "icon"
                if r.state == layerState then
                    alreadyAdded = true
                    break
                end
            end

            if not alreadyAdded and layer.visible ~= false then
                local stateName = layer.state or "icon"
                local s = layer.sprite or (iconBlock and iconBlock.sprite) or spritePath 

                if s then
                    addState(stateName, s)
                end
            end
        end
    end

    if #result == 0 and (iconBlock and (iconBlock.sprite or iconBlock.state) or spritePath) then
        local s = (iconBlock and iconBlock.sprite) or spritePath
        addState("icon", s)
    end

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

local function generateRepeatTemplate(data)
    local spriteGroups = {}

    for _, entry in pairs(data) do
        local found = false
        for _, group in pairs(spriteGroups) do
            if deepEqual(findFieldInsensitive(entry, "Sprite"), findFieldInsensitive(group[1], "Sprite")) and
               deepEqual(entry.EntityStorageVisuals, group[1].EntityStorageVisuals) and
               deepEqual(findFieldInsensitive(entry, "Icon"), findFieldInsensitive(group[1], "Icon")) then
                table.insert(group, entry)
                found = true
                break
            end
        end

        if not found then
            table.insert(spriteGroups, {entry})
        end
    end

    local result = {}
    for _, group in pairs(spriteGroups) do
        if #group > 1 then
            local idLinks = {}
            for _, entry in pairs(group) do
                table.insert(idLinks, "[[:Файл:" .. entry.id .. ".png]]")
            end
            table.insert(result, mw.getCurrentFrame():preprocess(
                "{{Entity Sprite/Repeat|" .. table.concat(idLinks, " ") .. "|" .. group[1].id .. "}}"
            ))
        end
    end

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

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

    if param == "image" then
        local states = getSpriteStates(entry)
        local stateStr = ""
        if states then
            local baseUrl = "https://github.com/space-syndicate/space-station-14/blob/master/Resources/Textures/"
            local links = {}
            for _, item in ipairs(states) do
                local spritePath = item.sprite
                local stateName = item.state
                local url = baseUrl .. spritePath .. "/" .. stateName .. ".png"
                table.insert(links, "[" .. url .. " " .. stateName .. "]")
            end
            stateStr = table.concat(links, ", ")
        end
        return mw.getCurrentFrame():preprocess(
            "{{Entity Sprite/Image|" .. entry.id ..
            "|https://github.com/space-syndicate/space-station-14/blob/master/Resources/Textures/" .. spritePath ..
            "|" .. stateStr .. "}}"
        )
    end

    return nil
end

function p.main(frame)
    local action = frame.args[1]
    local mode = frame.args[2]

    local dataPage
    if mode == "item" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/item.json/data"
    elseif mode == "clothing" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/clothing.json/data"
    elseif mode == "structure" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/structure.json/data"
    elseif mode == "mob" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/mob.json/data"
    elseif mode == "other" then
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite/other.json/data"
    else
        dataPage = "Модуль:IanComradeBot/prototypes/entity sprite.json/data"
    end

    local spriteData = mw.loadData(dataPage)
    if not spriteData or type(spriteData) ~= "table" then
        return "Ошибка: Невозможно загрузить данные из JSON (" .. dataPage .. ")."
    end

    if action == "repeat" then
        return generateRepeatTemplate(spriteData)
    elseif action == "image" then
        local result = {}
        for _, entry in pairs(spriteData) do
            local template = generateTemplate(entry, action)
            if template then
                table.insert(result, template)
            end
        end
        return table.concat(result, "\n")
    else
        return nil
    end
end

return p