Модуль:Infoboxes/Patch

Материал из ARK Wiki
Перейти к навигации Перейти к поиску

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

local Arkitecture = require( 'Module:Arkitecture' )
local ColumnTypes = Arkitecture.Cargo.ColumnTypes
local ParameterTypes = Arkitecture.ParameterTypes
local ParameterConstraint = Arkitecture.ParameterConstraints

local Text = require( 'Module:Infoboxes/Patch/strings' )


-- TODO: publish via Module:Version
local function parseVersion( str, defaultGame, defaultPlatform )
    if str == nil then
        return nil
    end
    local game, platform, major, minor = str:match( '([%w :]+)/Патч/(%w+) (%d+)%.(%d+)' )
    if major == nil then
        game, major, minor = str:match( '([%w :]+)/Патч/(%d+)%.(%d+)' )
    end
    if major == nil then
        platform, major, minor = str:match( '(%w+) (%d+)%.(%d+)' )
    end
    if major == nil then
        major, minor = str:match( '(%d+)%.(%d+)' )
    end
    if major == nil or minor == nil then
        return nil
    end
    return game or defaultGame, platform or defaultPlatform or 'PC', major, minor
end


return Arkitecture.makeInfoboxRenderer{
    RequiredLibraries = {
        'Module:Arkitecture/Common library',
    },

    PrivateComponents = {
        GameBar = Arkitecture.Component{
            render = function ( self, ctx )
                return Arkitecture.Html.Element{
                    tag = 'div',
                    classes = 'arkitect-game-bar',
                    Arkitecture.File{
                        name = ctx.instance.Game .. '.png',
                        width = 16,
                        link = ctx.instance.Game,
                    },
                    Arkitecture.Html.NonBreakingSpace,
                    Arkitecture.Link( ctx.instance.Game ),
                }
            end
        },

        TargetCell = Arkitecture.Component{
            render = function ( self, ctx, instance )
                if instance.Date == nil then
                    return ctx:expandComponent{
                        Component = 'Checkmark',
                        Value = false,
                    }
                end

                return {
                    ctx:expandComponent{
                        Component = 'Checkmark',
                        CheckedLabel = instance.UseNoUnofficialWording
                        	and Text.SECTION_AVAILABILITY_ROW_SERVER_OFFICIALS_ONLY
                        	or nil,
                        Value = true,
                    },
                    Arkitecture.Html.NewLine,
                    Arkitecture.Date( instance.Date ),
                }
            end
        },
    },

    CargoSetup = {
        Patch = {
            Unprefixed = true,
            { 'Game',
            	ColumnTypes.STRING,
            	Values = ParameterTypes.GAME.AllowedValues,
            },
            { 'Major', ColumnTypes.INTEGER, },
            { 'Minor', ColumnTypes.INTEGER, },
            { 'ClientReleaseDate',
                ColumnTypes.DATE,
                Optional = true,
            },
            { 'ServerReleaseDate',
                ColumnTypes.DATE,
                Optional = true,
            },
            { 'Platform',
                ColumnTypes.STRING,
                Values = {
                    'PC',
                    'Xbox',
                    'PlayStation',
                    'Switch',
                },
            },
        },
    },

    Parameters = {
        game = ParameterTypes.GAME,
        platform = ParameterTypes.STRING,
        major = ParameterTypes.INTEGER,
        minor = ParameterTypes.INTEGER,
        type = {
            ParameterTypes.STRING,
            AllowedValues = { 'major', 'minor', 'initial' },
        },
        changelist = {
            ParameterTypes.INTEGER,
            Optional = true,
        },
        date = {
            ParameterTypes.DATE,
            Optional = true,
        },
        client = {
            ParameterTypes.DATE,
            Optional = true,
        },
        server = {
            ParameterTypes.DATE,
            Optional = true,
        },
        unofficials = {
        	ParameterTypes.BOOL,
        	Default = true,
        },
        clientNumber = {
            ParameterTypes.STRING,
            Optional = true,
        },
        nintendoVersion = {
            ParameterTypes.STRING,
            Optional = true,
        },
        previous = ParameterTypes.GAME_VERSION,
        next = ParameterTypes.GAME_VERSION,
    },

    _makeVersionStringFromRecord = function ( self, record )
        if record.Platform == 'PC' then
            return string.format( '%s/Патч/%d.%d', record.Game, record.Major, record.Minor )
        end
        return string.format( '%s/Патч/%s %d.%d', record.Game, record.Platform, record.Major, record.Minor )
    end,

    _makeShortVersionStringFromRecord = function ( self, record )
        return string.format( '%d.%d', record.Major, record.Minor )
    end,
    
    _LinkVersion = function ( self, ctx, record )
    	if record then
    		if type( record ) == 'string' then
    			local game, platform, major, minor = parseVersion( record, ctx:getParameter( 'game' ), ctx:getParameter( 'platform' ) )
    			record = {
    				Game = game,
    				Platform = platform,
    				Major = major,
    				Minor = minor,
    			}
    		end
    		return string.format( '[[%s|%d.%d]]', self:_makeVersionStringFromRecord( record ), record.Major, record.Minor )
    	end
    	return nil
    end,

    injectParameters = function ( self, ctx )
        local title = mw.title.getCurrentTitle().text
        local game, platform, major, minor = parseVersion( title )

        if major == nil or minor == nil then
            error( 'Titles of patch articles should follow either of these formats: "[.../]major.minor", '
                .. '"[.../]platform major.minor".' )
        end

        local out = {
        	game = game,
            platform = platform,
            major = major,
            minor = minor,
        }

        if ctx:getParameter( 'date' ) ~= nil then
            out.client = ctx:getParameter( 'date' )
            out.server = ctx:getParameter( 'date' )
        end

        -- Fetch previous and next version from Cargo
        -- TODO: forced chronology needs to be exported
        if not ctx:hasParameterValueUnchecked( 'previous' ) then
            local results = mw.ext.cargo.query( ctx:getCargoTablePrefix() .. 'Patch', 'Game, Platform, Major, Minor', {
                where = string.format(
                    'Game = \'%s\' AND Platform = \'%s\' AND ( ( Major < %d ) OR ( Major = %d AND Minor < %d ) )',
                    game, platform, major, major, minor
                ),
                orderBy = 'GREATEST( COALESCE( ClientReleaseDate, 0 ), COALESCE( ServerReleaseDate, 0 ) ) DESC, '
                    .. 'Major DESC, Minor DESC',
                limit = 1
            } )
            if #results ~= 0 then
                out.previous = self:_makeVersionStringFromRecord( results[1] )
            end
        end
        if not ctx:hasParameterValueUnchecked( 'next' ) then
            local results = mw.ext.cargo.query( ctx:getCargoTablePrefix() .. 'Patch', 'Game, Platform, Major, Minor', {
                where = string.format(
                    'Game = \'%s\' AND Platform = \'%s\' AND ( ( Major > %d ) OR ( Major = %d AND Minor > %d ) )',
                    game, platform, major, major, minor
                ),
                orderBy = 'GREATEST( COALESCE( ClientReleaseDate, 0 ), COALESCE( ServerReleaseDate, 0 ) ) ASC, '
                    .. 'Major ASC, Minor ASC',
                limit = 1
            } )
            if #results ~= 0 then
                out.next = self:_makeShortVersionStringFromRecord( results[1] )
            end
        end

        out.__NEXT = ( function ()
            local out = {}
            -- If not specified already, determine type
            if not ctx:hasParameterValueUnchecked( 'type' ) then
                local _, _, prevMajor, prevMinor = parseVersion( ctx:getParameter( 'previous' ), game, platform )
                out.type = prevMajor == nil and 'initial'
                    or prevMajor == major and 'minor'
                    or 'major'
            end
            return out
        end )

        return out
    end,

    ---
    --- @class PatchInfo
    --- @field platform string
    --- |"'PC'"
    --- |"'Xbox'"
    --- |"'PS'"
    --- |"'Switch'"
    --- @field major integer
    --- @field minor integer
    ---

    ---
    --- Packs all information identifying a patch into a single structure. Ideally, we'd retrieve it from the title
    --- directly rather than relying on wikitext-end to give it to us. But this is easier (... and faster) than parsing
    --- the title in Lua...
    --- @param ctx Arkitecture.RendererContext
    --- @return PatchInfo 
    _packPatchInfo = function ( self, ctx )
        return {
        	game = ctx:getParameter( 'game' ),
            platform = ctx:getParameter( 'platform' ),
            major = ctx:getParameter( 'major' ),
            minor = ctx:getParameter( 'minor' ),
        }
    end,

    PLATFORM_ICONS = {
        PS = 'PS.svg',
        Switch = 'Nintendo Switch.svg',
        TSOTF = 'TSotF_Logo.png'
    },

    _makePlatformIcons = function ( self, pInfo )
        local width = 22

        if self.PLATFORM_ICONS[pInfo.platform] then
            return Arkitecture.File{
                name = self.PLATFORM_ICONS[pInfo.platform],
                width = width,
                link = false
            }
        elseif pInfo.platform == 'Xbox' then
            return table.concat( {
                Arkitecture.File{
                    name = 'Microsoft Store.svg',
                    width = width,
                    link = false
                },
                Arkitecture.File{
                    name = 'Xbox One.svg',
                    width = width,
                    link = false
                }
            }, Arkitecture.Html.NonBreakingSpace )
        elseif pInfo.platform == 'PC' then
            -- Steam, always
            local icons = {
                Arkitecture.File{
                    name = 'Steam.svg',
                    width = width,
                    link = false
                }
            }
            -- EOS, at or after 311.74
            if pInfo.major > 311 or ( pInfo.major == 311 and pInfo.minor >= 74 ) then
                icons[#icons + 1] = Arkitecture.File{
                    name = 'Epic Games.svg',
                    width = width,
                    link = false
                }
            end
            -- Stadia, at 678 or after 337, but before 355.16 (January 18th, 2022)
            if pInfo.major == 678 or ( pInfo.major > 337 and not ( pInfo.major > 355 or pInfo.major == 355
                and pInfo.minor >= 16  ) ) then
                icons[#icons + 1] = Arkitecture.File{
                    name = 'Stadia.svg',
                    width = width,
                    link = false
                }
            end

            return table.concat( icons, Arkitecture.Html.NonBreakingSpace )
        end

        error( 'Platform not recognised in _makePlatformIcons, someone did not update it when adding a platform: '
            .. pInfo.platform )
    end,

    getSetup = function ( self, ctx )
        local pInfo = self:_packPatchInfo( ctx )
        local gameCode = ctx:getGameId()

        return {
            {
                {
                    Component = 'GameBar',
                    Game = ctx:getParameter( 'game' ),
                },
                {
                    Component = 'SegmentedHeader',
                    LeftValue = self:_makePlatformIcons( pInfo ),
                    Name = string.format( '%d.%d', pInfo.major, pInfo.minor ),
                },
                {
                    Component = 'NamedDataRow',
                    Dimensions = '25x75',
                    Name = Text.ROW_CLIENT_BUILD_NUMBER,
                    Value = ctx:getParameter( 'clientNumber' ),
                },
                {
                    Component = 'NamedDataRow',
                    Dimensions = '25x75',
                    Name = Text.ROW_P4_CHANGELIST,
                    Tooltip = Text.ROW_P4_CHANGELIST_TOOLTIP,
                    Value = ctx:getParameter( 'changelist' ),
                },
                {
                    Component = 'NamedDataRow',
                    Dimensions = '25x75',
                    Name = Text.ROW_TYPE,
                    Value = ( {
                        initial = Text.ROW_TYPE_INITIAL_RELEASE,
                        major = Text.ROW_TYPE_MAJOR,
                        minor = Text.ROW_TYPE_MINOR,
                    } )[ctx:getParameter( 'type' )],
                },
                {
                    Component = 'FloatingNote',
                    Value = ctx:getParameter( 'nintendoVersion' )
                        and string.format( Text.NOTE_NINTENDO_VERSION_F, ctx:getParameter( 'nintendoVersion' ) )
                        or nil,
                },
            },
            {
                Caption = Text.SECTION_AVAILABILITY,
                Component = 'NamedDataTable2x2',
                {
                    Name = Text.SECTION_AVAILABILITY_ROW_CLIENT,
                    Value = ctx:expandComponent{
                        Component = 'TargetCell',
                        Date = ctx:getParameter( 'client' )
                    },
                },
                {
                    Name = Text.SECTION_AVAILABILITY_ROW_SERVER,
                    Value = ctx:expandComponent{
                        Component = 'TargetCell',
                        UseNoUnofficialWording = not ctx:getParameter( 'unofficials' ),
                        Date = ctx:getParameter( 'server' )
                    },
                },
            },
            {
                Caption = Text.SECTION_CHRONOLOGY,
                gameCode == 'ASA' and {
                    Component = 'NamedDataRow',
                    Name = Text.SECTION_CHRONOLOGY_ROW_PREVIOUS,
                    Value = self:_LinkVersion( ctx, ctx:getParameter( 'previous' ) ),
                } or '',
                gameCode == 'ASA' and {
                    Component = 'NamedDataRow',
                    Name = Text.SECTION_CHRONOLOGY_ROW_NEXT,
                    Value = self:_LinkVersion( ctx, ctx:getParameter( 'next' ) ),
                } or '',
            },

            {
                SideEffect = true,
                Arkitecture.JoinCategory( Text['CATEGORY_' .. gameCode .. '_' .. ctx:getParameter( 'platform' ):upper()]
                    or Text['CATEGORY_' .. gameCode .. '_FALLBACK'] ),
            },
            {
                Component = 'NewCargoRow',
                Table = 'Patch',

				Game = ctx:getParameter( 'game' ),
                Platform = ctx:getParameter( 'platform' ),
                Major = ctx:getParameter( 'major' ),
                Minor = ctx:getParameter( 'minor' ),
                ClientReleaseDate = ctx:getParameter( 'client' ),
                ServerReleaseDate = ctx:getParameter( 'server' ),
            }
        }
    end
}