跳转到内容

模組:PlatformMap

本页使用了标题或全文手工转换
维基百科,自由的百科全书

local p = {}

---@overload fun(x: any): boolean?
local yesno = require('Module:Yesno')

---@type { [string]: { set: string, subset: string } }
local alias_map = {
    ['SS']  = { set = 'lr\\lr', subset = 'a\\\\\\e' },
    ['SIS'] = { set = 'lr\\\\lr', subset = 'a\\\\ae\\\\e' },
    ['I']   = { set = 'lr\\\\\\lr', subset = 'e\\a' }
}

---@param frame frame
---@return string
function p.main(frame)
    local args = frame:getParent().args
    ---@module 'PlatformMapShared'
    local util = require('Module:PlatformMap/Shared')
    ---@module "PlatformMapStrings"
    local strs = require('Module:PlatformMap/Strings')

    -- Import set inst
    local set = args[1]
    local subset = args[2]
    if not set then
        return '~~ ~~' .. strs.err_nil_arg_set
    end
    if alias_map[set] then
        subset = alias_map[set].subset
        set = alias_map[set].set
    end
    ---@type { subsets: { [string]: string[][] }, rows: { tracks: integer[], [integer]: { continue: ('left'|'right')?, to: ('left'|'right')?, trimmable: boolean? } }, switches: { both: { [string]: string[][] }, left: { [string]: string[][] }, right: { [string]: string[][] } } }
    local meta = require('Module:PlatformMap/' .. set)

    -- Init
    local inst
    if meta.subsets[subset] then
        inst = meta.subsets[subset]
    else
        local rows = mw.text.split(subset, ' ')
        if #rows > 1 then
            inst = {}
            for r, row in ipairs(rows) do
                inst[r] = mw.text.split(row, '\\')
            end
        else
            return '~~ ~~' .. mw.ustring.format(strs.err_unknown_subset, set, subset)
        end
    end
    local stats = {
        custom_subset = false,   -- TODO
        custom_switches = false, -- TODO
        unknown_switches = {}
    }

    -- Override track connections
    for t, r in ipairs(meta.rows.tracks) do
        if args['continue' .. t] then
            meta.rows[r].continue = args['continue' .. t]
        end
    end

    local i
    -- Left switches
    i = 1
    while (true) do
        local name = args['switchL' .. i]
        if not name then
            break
        end

        local switch = meta.switches.both[name] or meta.switches.left[name]
        if switch then
            util.matrix.prepend(inst, switch)
        else
            local rows = mw.text.split(name, ' ')
            if #rows == #inst then -- direct input
                switch = {}
                for r, row in ipairs(rows) do
                    switch[r] = mw.text.split(row:gsub('^MASK$', ''), '\\')
                end
                util.matrix.prepend(inst, switch)
            elseif mw.ustring.sub(name, 1, 3) == 'STR' and mw.ustring.sub(name, -1, -1) == '#' then
                local pad = tonumber(mw.ustring.sub(name, 4, -2))
                for r = 1, #inst do
                    local info = meta.rows[r]
                    if info and info.to and info.continue ~= 'right' then
                        for _ = 1, pad do
                            table.insert(inst[r], 1, 'uSTRq')
                        end
                    else
                        for _ = 1, pad do
                            table.insert(inst[r], 1, '')
                        end
                    end
                end
            else
                table.insert(stats.unknown_switches, name)
            end
        end

        i = i + 1
    end

    -- Right switches
    i = 1
    while (true) do
        local name = args['switchR' .. i]
        if not name then
            break
        end

        local switch = meta.switches.both[name] or meta.switches.right[name]
        if switch then
            util.matrix.append(inst, switch)
        else
            local rows = mw.text.split(name, ' ')
            if #rows == #inst then -- direct input
                switch = {}
                for r, row in ipairs(rows) do
                    switch[r] = mw.text.split(row:gsub('^MASK$', ''), '\\')
                end
                util.matrix.append(inst, switch)
            elseif mw.ustring.sub(name, 1, 3) == 'STR' and mw.ustring.sub(name, -1, -1) == '#' then
                local pad = tonumber(mw.ustring.sub(name, 4, -2))
                for r = 1, #inst do
                    local info = meta.rows[r]
                    if info and info.to and info.continue ~= 'left' then
                        for _ = 1, pad do
                            table.insert(inst[r], 'uSTRq')
                        end
                    else
                        for _ = 1, pad do
                            table.insert(inst[r], '')
                        end
                    end
                end
            else
                table.insert(stats.unknown_switches, name)
            end
        end

        i = i + 1
    end

    ---@alias end_mode 'none' | 'push' | 'pad'
    ---@type end_mode
    local end_l = 'push'
    ---@type end_mode
    local end_r = 'push'

    -- Tracks (round I)
    for t, r in ipairs(meta.rows.tracks) do
        -- override orientions
        if args['to' .. t] then meta.rows[r].to = args['to' .. t] end
        -- track end detection
        local row = inst[r]
        local info = meta.rows[r]
        if info.continue ~= 'right' then
            local bound = args['bound' .. t .. 'L'] or args['boundL']
            if info.to == 'right' or (bound ~= nil and bound ~= '') then
                if not util.icon.map[row[1]] then
                    end_l = 'none'
                end
            else
                if string.find(row[1], 'BS') then
                    end_l = 'pad'
                end
            end
        end
        if info.continue ~= 'left' then
            local bound = args['bound' .. t .. 'R'] or args['boundR']
            if info.to == 'left' or (bound ~= nil and bound ~= '') then
                if not util.icon.map[row[#row]] then
                    end_r = 'none'
                end
            else
                if string.find(row[#row], 'BS') then
                    end_r = 'pad'
                end
            end
        end
        -- outbound text (overrided by manual)
        local nxt = args['next' .. t]
        if nxt then
            local system = args['system' .. t] or args.system
            local line = args['line' .. t] or args.line
            local typ = args['type' .. t] or args.type
            local inverse = yesno(args['inverse' .. t]) or false
            if meta.rows[r].to == 'left' then
                row.textL = util.text.adjacent
                    { system = system, line = line, type = typ, next = nxt, left = true, inverse = inverse } .. '~~'
            else
                row.textR = '~~' ..
                    util.text.adjacent
                    { system = system, line = line, type = typ, next = nxt, left = false, inverse = inverse }
            end
        end
    end

    -- Tracks (round II)
    for t, r in ipairs(meta.rows.tracks) do
        -- override orientions
        if args['to' .. t] then meta.rows[r].to = args['to' .. t] end
        -- track end detection
        local row = inst[r]
        local info = meta.rows[r]
        if info.continue ~= 'right' then
            local bound = args['bound' .. t .. 'L'] or args['boundL']
            if end_l == 'none' then
                if bound == 'soft' or args['switchL1'] == nil or args['switchL1'] == '' then
                    end_l = 'pad'
                end
            end
        end
        if info.continue ~= 'left' then
            local bound = args['bound' .. t .. 'R'] or args['boundR']
            if end_r == 'none' then
                if bound == 'soft' or args['switchR1'] == nil or args['switchR1'] == '' then
                    end_r = 'pad'
                end
            end
        end
    end

    -- Manual texts
    for r, row in ipairs(inst) do
        -- Manual text
        if args['textL' .. r] then
            row.textL = args['textL' .. r]
        end
        if args['textR' .. r] then
            row.textR = args['textR' .. r]
        end
    end

    -- Tracks (round III)
    for t, r in ipairs(meta.rows.tracks) do
        -- track ends
        local row = inst[r]
        local info = meta.rows[r]
        -- left
        if info.continue ~= 'right' then
            local bound = args['bound' .. t .. 'L'] or args['boundL']
            if bound == 'hard' then
                if end_l == 'push' then
                    row[1] = util.icon.map[row[1]].hard.on_left
                    table.insert(row, 1, '')
                else
                    table.insert(row, 1, util.icon.map['uSTRq'].hard.on_left)
                    if end_l == 'pad' then
                        table.insert(row, 1, '')
                    end
                end
            elseif bound == 'soft' then
                if end_l == 'push' then
                    row[1] = util.icon.map[row[1]].soft.on_left
                elseif end_l == 'pad' then
                    table.insert(row, 1, util.icon.map['uSTRq'].soft.on_left)
                else
                    error('Invalid `end_l`')
                end
                table.insert(row, 1, util.icon.map['uexCONTgq'][info.to])
            else
                if end_l == 'push' then
                    if util.icon.map[row[1]] then
                        row[1] = util.icon.map[row[1]].open.on_left
                    end
                elseif end_l == 'pad' then
                    table.insert(row, 1, util.icon.map['uSTRq'].open.on_left)
                end
                table.insert(row, 1, util.icon.map['uCONTgq'][info.to])
            end
        else
            table.insert(row, 1, '')
            if end_l == 'pad' then
                table.insert(row, 1, '')
            end
        end
        -- right
        if info.continue ~= 'left' then
            local bound = args['bound' .. t .. 'R'] or args['boundR']
            if bound == 'hard' then
                if end_r == 'push' then
                    row[#row] = util.icon.map[row[#row]].hard.on_right
                    table.insert(row, '')
                else
                    table.insert(row, util.icon.map['uSTRq'].hard.on_right)
                    if end_r == 'pad' then
                        table.insert(row, '')
                    end
                end
            elseif bound == 'soft' then
                if end_r == 'push' then
                    row[#row] = util.icon.map[row[#row]].soft.on_right
                elseif end_r == 'pad' then
                    table.insert(row, util.icon.map['uSTRq'].soft.on_right)
                else
                    error('Invalid `end_r`')
                end
                table.insert(row, util.icon.map['uexCONTfq'][info.to])
            else
                if end_r == 'push' then
                    if util.icon.map[row[#row]] then
                        row[#row] = util.icon.map[row[#row]].open.on_right
                    end
                elseif end_r == 'pad' then
                    table.insert(row, util.icon.map['uSTRq'].open.on_right)
                end
                table.insert(row, util.icon.map['uCONTfq'][info.to])
            end
        else
            table.insert(row, '')
            if end_r == 'pad' then
                table.insert(row, '')
            end
        end
    end

    -- Fix align
    local nontrack_pad = 0
    if end_r == 'pad' then
        nontrack_pad = nontrack_pad + 1
    end
    if end_l == 'pad' then
        nontrack_pad = nontrack_pad - 1
    end
    if nontrack_pad ~= 0 then
        for r, row in ipairs(inst) do
            local info = meta.rows[r]
            if not info or not info.to then
                if nontrack_pad == -2 then
                    table.insert(row, 1, '')
                    table.insert(row, 1, '')
                elseif nontrack_pad == -1 then
                    table.insert(row, 1, '')
                elseif nontrack_pad == 1 then
                    table.insert(row, '')
                elseif nontrack_pad == 2 then
                    table.insert(row, '')
                    table.insert(row, '')
                end
            end
        end
    end

    -- Trim unused rows
    for r = #inst, 1, -1 do
        local row = inst[r]
        local info = meta.rows[r]
        if info and info.trimmable then
            local trim = true
            for c = 1, #row do
                if row[c] ~= '' then
                    trim = false
                end
            end
            if trim then table.remove(inst, r) end
        end
    end

    -- Padding
    if args.pad then
        local pad = tonumber(args.pad)
        if pad < 0 then
            for r = 1, #inst do
                for _ = 1, -pad do
                    table.insert(inst[r], 1, '')
                end
            end
        elseif pad > 0 then
            for r = 1, #inst do
                for _ = 1, pad do
                    table.insert(inst[r], '')
                end
            end
        end
    end

    -- Error
    if #stats.unknown_switches > 0 then
        local row = inst[#inst]
        row.textR = table.concat {
            mw.ustring.format(strs.err_unknown_switches, table.concat(stats.unknown_switches, ', ')),
            row.textR
        }
    end

    -- Output
    local lines = {}

    for r, row in ipairs(inst) do
        lines[r] = table.concat(row, '\\')
        ---@diagnostic disable-next-line: undefined-field
        if row.textL then lines[r] = mw.ustring.format('%s! !%s', row.textL, lines[r]) end
        ---@diagnostic disable-next-line: undefined-field
        if row.textR then lines[r] = mw.ustring.format('%s~~%s', lines[r], row.textR) end
    end

    return table.concat(lines, '\n')
end

---@param frame frame
---@return string
function p.switch(frame)
    local args = frame.args
    local set = args[1]
    set = require('Module:PlatformMap/' .. set)
    local switch = args[2]
    local side = args[3] or 'both'
    switch = set.switches[side][switch]
    local lines = {}
    for l = 1, #switch do
        lines[l] = table.concat(switch[l], '\\') .. '~~' .. l .. '~~'
    end
    return table.concat(lines, '\n')
end

return p