Modul:VehicleComponent

Aus Steel Beasts Wiki

Die Dokumentation für dieses Modul kann unter Modul:VehicleComponent/Doku erstellt werden

local VehicleComponent = {}

local metatable = {}
local methodtable = {}

metatable.__index = methodtable

-- Extensions
local common       = require( 'Module:Common' )
local api          = require( 'Module:Common/Api' )
local manufacturer = require( 'Module:Hersteller' )
local TNT          = require( 'Module:Translate' ):new()
local log          = require( 'Module:Log' )
local item         = require( 'Module:Item' )
local data         = mw.loadData( 'Module:VehicleComponent/Data' )


local function setModifiers( modifiers, setData )
    for _, modifier in pairs( modifiers ) do
    	if data.modifierMap[ modifier.display_name ] ~= nil then
    		local value
    		if modifier.name == 'size' then
    			value = modifier.value
			else
				value = string.gsub( modifier.value, '%%', '' ) / 100
			end

    		setData[ 'Modifikator ' .. data.modifierMap[ modifier.display_name ] ] = value
		end
    end
end


local function addSpecificationData( self, setData )
    -- Cooler
    if self.apiData.cooler ~= nil then
        setData[ 'Kühlrate' ] = common.formatNum( self.apiData.cooler.cooling_rate )
    end

    -- Power Plant
    if self.apiData.power_plant ~= nil then
        setData[ 'Ausgangsleistung' ] = common.formatNum( self.apiData.power_plant.power_output )
    end

    -- Quantum Drive
    if self.apiData.quantum_drive ~= nil then
        setData[ 'Kraftstoffverbrauch' ] = common.formatNum( self.apiData.quantum_drive.quantum_fuel_requirement )
        setData[ 'Quantenreichweite' ]   = common.formatNum( self.apiData.quantum_drive.jump_range )

        if self.apiData.quantum_drive.modes ~= nil and type( self.apiData.quantum_drive.modes ) == 'table' then
            for _, jump in pairs( self.apiData.quantum_drive.modes ) do
                mw.smw.subobject( {
                    [ 'Subtypname' ]             = jump.type,
                    [ 'Quantengeschwindigkeit' ] = common.formatNum( jump.drive_speed ),
                    [ 'Quantenabkühlzeit' ]      = common.formatNum( jump.cooldown_time ),
                    [ 'Quantenspulzeit' ]        = common.formatNum( jump.spool_up_time ),
                } )
            end
        end
    end

    -- Shield
    if self.apiData.shield ~= nil then
        local absorptionPhysical
        local absorptionEnergy
        local absorptionThermal
        local absorptionDistortion
        local absorptionBiochemical
        local absorptionStun

        if self.apiData.shield.absorption ~= nil and type( self.apiData.shield.absorption ) == 'table' then
            for _, absorption in pairs( self.apiData.shield.absorption ) do
                if type( absorption.physical ) == 'table' then
                    absorptionPhysical = {
                        common.formatNum( absorption.physical.min ),
                        common.formatNum( absorption.physical.max ),
                    }
                elseif type( absorption.energy ) == 'table' then
                    absorptionEnergy = {
                        common.formatNum( absorption.energy.min ),
                        common.formatNum( absorption.energy.max ),
                    }
                elseif type( absorption.distortion ) == 'table' then
                    absorptionDistortion = {
                        common.formatNum( absorption.distortion.min ),
                        common.formatNum( absorption.distortion.max ),
                    }
                elseif type( absorption.thermal ) == 'table' then
                    absorptionThermal = {
                        common.formatNum( absorption.thermal.min ),
                        common.formatNum( absorption.thermal.max ),
                    }
                elseif type( absorption.biochemical ) == 'table' then
                    absorptionBiochemical = {
                        common.formatNum( absorption.biochemical.min ),
                        common.formatNum( absorption.biochemical.max ),
                    }
                elseif type( absorption.stun ) == 'table' then
                    absorptionStun = {
                        common.formatNum( absorption.stun.min ),
                        common.formatNum( absorption.stun.max ),
                    }
                end
            end
        end

        setData[ 'Schildpunkte' ]             = common.formatNum( self.apiData.shield.max_shield_health )
        setData[ 'Schildpunktregeneration' ]  = common.formatNum( self.apiData.shield.max_shield_regen )
        setData[ 'Ausfallzeit' ]              = common.formatNum( self.apiData.shield.regen_delay.downed )
        setData[ 'Schadensverzögerung' ]      = common.formatNum( self.apiData.shield.regen_delay.damage )
        setData[ 'Physikalische Absorption' ] = absorptionPhysical or nil
        setData[ 'Energie Absorption' ]       = absorptionEnergy or nil
        setData[ 'Distortion Absorption' ]    = absorptionDistortion or nil
        setData[ 'Thermische Absorption' ]    = absorptionThermal or nil
        setData[ 'Biochemische Absorption' ]  = absorptionBiochemical or nil
        setData[ 'Stun Absorption' ]          = absorptionStun or nil
    end

    -- Missile Launcher
    if self.apiData.max_missiles ~= nil then
        setData[ 'Anzahl Raketen' ] = self.apiData.max_missiles
        setData[ 'Raketengröße' ] = self.apiData.min_size
    end

    -- Mining Laser
    if self.apiData.mining_laser ~= nil then
        if self.apiData.mining_laser.modifiers ~= nil and type( self.apiData.mining_laser.modifiers ) == 'table' then
            setModifiers( self.apiData.mining_laser.modifiers, setData )
        end

        setData[ 'Energierate' ]                         = common.formatNum( self.apiData.mining_laser.power_transfer or nil, nil )
        setData[ 'Effektive Reichweite' ]                = common.formatNum( self.apiData.mining_laser.optimal_range or nil, nil )
        setData[ 'Maximalreichweite' ]                   = common.formatNum( self.apiData.mining_laser.maximum_range or nil, nil )
        setData[ 'Entnahmedurchsatz' ]                   = common.formatNum( self.apiData.mining_laser.extraction_throughput or nil, nil )
        setData[ 'Bergbau-Laserleistung' ]               = self.apiData.mining_laser.mining_laser_power or nil
        setData[ 'Extraktion Laserleistung' ]            = self.apiData.mining_laser.extraction_laser_power or nil
        setData[ 'Anzahl Verbrauchsplätze' ]             = self.apiData.mining_laser.module_slots
    end

    -- Mining Module
    if self.apiData.mining_module ~= nil then
        if self.apiData.mining_module.modifiers ~= nil and type( self.apiData.mining_module.modifiers ) == 'table' then
            setModifiers( self.apiData.mining_module.modifiers, setData )
        end
        
        local duration = self.apiData.mining_module.duration
        if duration ~= nil then
        	duration = string.gsub( duration, 'seconds', '' )
    	end

        setData[ 'Anwendungen' ] = self.apiData.mining_module.uses or nil
        setData[ 'Dauer' ]       = duration
        setData[ 'Subtyp' ]      = {
            ( data.mappings[ self.apiData.mining_module.type or '' ] or self.apiData.mining_module.type ) .. '@de',
            ( self.apiData.type ) .. '@en'
        }
    end

    -- Turret
    if self.apiData.max_mounts ~= nil then
        setData[ 'Anzahl Aufhängungen' ] = self.apiData.max_mounts
        setData[ 'Minimalgröße' ]        = self.apiData.min_size
        setData[ 'Maximalgröße' ]        = self.apiData.max_size
    end

    return setData
end


--- Request Api Data
--- Using current subpage name
--- @return table
function methodtable.getApiDataForCurrentPage( self )
    local name = self.frameArgs[ 'name' ] or self.frameArgs[ 'UUID' ] or mw.title.getCurrentTitle().text
    name = common.removeTypeSuffix( name, {
	    'Kühler',
	    'Generator',
	    'Quantenantrieb',
	    'Schildgenerator',
	    'Raketenwerfer',
	    'Bergbaulaser',
	    'Waffenturm',
	} )

    local json = mw.text.jsonDecode( mw.ext.Apiunto.get_raw( 'v2/items/' .. name, {
        include = {
            'shops.items'
        }
    } ) )

    api.checkResponseStructure( json, false, true )

    self.apiData = json[ 'data' ]

    return self.apiData
end


--- Sets the main categories for this object
function methodtable.setCategories( self, smwData )
	if smwData.manufacturer == nil then
		table.insert( self.categories, '[[Category:Seiten mit Skriptfehlern]]' )
		return
	end

	if self.lang == 'de' then
	    table.insert( self.categories, '[[Category:' .. smwData.manufacturer ..'|' .. smwData.name .. ' ]]' )
	
	    if smwData.type ~= nil then
	        table.insert( self.categories, '[[Category:' .. smwData.type_de .. '|' .. smwData.name .. ' ]]' )
	    end
	
	    if smwData.subtype ~= nil then
	        table.insert( self.categories, '[[Category:' .. smwData.subtype_de .. '|' .. smwData.name .. ' ]]' )
	    end
	
	    if smwData.class ~= nil then
	        table.insert( self.categories, '[[Category:' .. smwData.class_de .. ' Komponente' .. '|' .. smwData.name .. ' ]]' )
	    end
	
	    if smwData.grade ~= nil then
	        table.insert( self.categories, '[[Category:Grad ' .. smwData.grade .. ' Komponente' .. '|' .. smwData.name .. ' ]]' )
	    end
	
	    if smwData.size ~= nil then
	        table.insert( self.categories, '[[Category:S' .. smwData.size .. ' Komponente' .. '|' .. smwData.name .. ' ]]' )
	    end

	    table.insert( self.categories, '[[Category:' .. self.objectType .. '|' .. self.pageName .. ']]' )
    else
    	table.insert( self.categories, '[[Category:' .. smwData.name ..'|' .. smwData.name .. ' ]]' )
    end
end


--- Queries the SMW Store for the current object
--- @param identifier string|number Starmap Code / Name / ID of the object
--- @return table SMW Data
function methodtable.getSmwData( self, identifier )
    -- Cache multiple calls
    if self.itemData ~= nil then
        return self.itemData
    end

    -- We'll try to get the code by pagename
    if identifier == nil then
        identifier  = self.frameArgs[ 'name' ] or require('Module:Localized').getMainTitle()
	    identifier = common.removeTypeSuffix( identifier, {
		    'Kühler',
		    'Generator',
		    'Quantenantrieb',
		    'Schildgenerator',
		    'Raketenwerfer',
		    'Bergbaulaser',
		    'Waffenturm',
		} )
    end

    local askData = {
        '[[Name::' .. identifier .. ']][[Spielversion::+]][[Hersteller::+]]',

        '?#-=page',

        -- Base Item Data
        '?UUID#-=uuid',
        '?Name#-=name',
        '?Grad=grade',
        '?Größe=size',
        '?Typ=type', '+lang=' .. common.getLocaleForPage(),
        '?Typ=type_de', '+lang=de',
        '?Subtyp=subtype', '+lang=' .. common.getLocaleForPage(),
        '?Subtyp=subtype_de', '+lang=de',
        '?Klasse=class', '+lang=' .. common.getLocaleForPage(),
        '?Klasse=class_de', '+lang=de',
        '?Hersteller#-=manufacturer',
        '?Schadenspunkte#-=health',
        '?Spielversion#-=version',

        '?Grundverbrauch#-=power_base',
        '?Leistungsaufnahme#-=power_draw',
        '?Kühllast#-=cooling_consumption',

        '?Infrarotstrahlung#-=ir',
        '?Elektromagnetische Strahlung#-=em',

        --- TODO: Thermal Properties and cooling rate

        -- Cooler
        '?Kühlrate=cooling_rate',

        -- Power Plant
        '?Ausgangsleistung#-=power_output',

        -- Shield
        '?Schildpunkte=shield_points',
        '?Schildpunktregeneration=shield_regeneration',
        '?Ausfallzeit#-=downed_delay',
        '?Schadensverzögerung#-=damage_delay',
        '?Physikalische Absorption#-=physical_absorption',
        '?Energie Absorption#-=energy_absorption',
        '?Distortion Absorption#-=distortion_absorption',
        '?Thermische Absorption#-=thermal_absorption',
        '?Biochemische Absorption#-=biochemical_absorption',
        '?Stun Absorption#-=stun_absorption',

        -- Quantum Drive
        '?Kraftstoffverbrauch#-p10=fuel_rate',
        '?Quantenreichweite#-=jump_range',

        -- Raketenwerfer
        '?Anzahl Raketen#-=rocket_count',
        '?Raketengröße#-=rocket_size',

        -- Mining Laser
        '?Energierate#-=energy_rate',
        '?Entnahmedurchsatz#-=extraction_throughput',
        '?Effektive Reichweite#-=full_damage_range',
        '?Maximalreichweite#-=zero_damage_range',
        '?Hitze pro Sekunde#-=heat_per_second',
        '?Schaden pro Sekunde#-=damage_per_second',
        '?Anzahl Verbrauchsplätze#-=consumable_slots',

        '?Bergbau-Laserleistung#-=mining_power',
        '?Extraktion-Laserleistung#-=extraction_power',

        '?Modifikator Resistenz#-=resistance',
        '?Modifikator Instabilität#-=instability',
        '?Modifikator Ladungsraten#-=all_charge_rates',
        '?Modifikator Splitterschaden#-=shatter_damage',
        '?Modifikator Drosselgeschwindigkeit#-=throttle_speed',
        '?Modifikator Optimales Ladungsfenster#-=optimal_charge_window',
        '?Modifikator Katastrophische Laderate#-=catastrophic_rate',
        '?Modifikator Laser-Instabilität#-=laser_instability',
        '?Modifikator Optimale Ladefensterrate#-=optimal_charge_window_rate',
        '?Modifikator Inerte Materialien#-=inert_materials',
        '?Modifikator Optimale Laderate#-=optimal_charge_rate',


        -- Modules
        '?Dauer#-=duration',
        '?Anwendungen#-=uses',

        -- Turrets
        '?Anzahl Aufhängungen#-=max_mounts',
        '?Minimalgröße#-=min_size',
        '?Maximalgröße#-=max_size',
    }

    askData.mainlabel = '-'
    askData.limit = 1

    local data = mw.smw.ask( askData )

    if data == nil or data[ 1 ] == nil then
        return TNT.formatInLanguage( self.lang, 'Item', 'msg_smw_loading' )
    end

    self.itemData = data[ 1 ]

    return self.itemData
end


--- Base Properties that are shared across all Vehicles
--- @return table SMW Result
function methodtable.setSemanticProperties( self )
    -- Api Error, don't set anything
    if self.apiData == nil then
        return
    end

    local item = item:new( self.pageName, self.apiData )

    local grade = self.apiData.grade or nil
    if grade ~= nil and grade == 'Bespoke' then
        grade = 'Sonderanfertigung'
    end

    local setData = item:makeSmwObject()

    setData = addSpecificationData( self, setData )

    local result = mw.smw.set( setData )

    local commodity = require( 'Module:Commodity' ):new()
    commodity:addShopData( self.apiData )

    return result
end


--- Save Api Data to SMW store
function methodtable.saveApiData( self )
    if self.currentFrame == nil then
        error( 'No frame set. Call "setFrame" first.', 0 )
    end

    local data = self:getApiDataForCurrentPage()

	self:setSemanticProperties()

    return data
end


--- Creates the base infobox for a celestial object
--- TODO Clean
--- @return string Infobox
function methodtable.getInfoBox( self )
    local data = self:getSmwData()

    if nil == data.manufacturer then
        return log.info( 'SMW Daten noch nicht geladen, dies dauert wenige Minuten.' )
    end

    if data.manufacturer == 'Unknown Manufacturer' then
        data.manufacturer = 'Unbekannter Hersteller'
    end

    -- Set Title
    common.setDisplayTitle( self.currentFrame, data.name )

    local box = require( 'Module:InfoboxNeue' ):new( {
        removeEmpty = true,
        emptyString = '-'
    } )

	local nameNormalized, _ = mw.ustring.gsub( data.name, "[^%w-]", ' ' )
	nameNormalized, _ = mw.ustring.gsub( nameNormalized, "%s+", ' ' )

    box:renderImage( common.getImage( {
        self.frameArgs[ 'image' ],
        mw.text.trim( nameNormalized ) .. '.jpg',
    } ) )

    box:renderHeader( {
    	title = data.name,
    	subtitle = '[[' .. data.manufacturer .. ']]' .. api.getVersionInfoRef( data.version, self.lang )
    } )

    local itemType = data.type
    if itemType ~= nil then
        itemType = string.format( '[[:Kategorie:%s|%s]]', data.type_de, itemType )
    end

    local itemClass = data.class
    if itemClass ~= nil then
        itemClass = string.format( '[[:Kategorie:%s Komponente|%s]]', data.class_de, itemClass )

        if data.grade ~= nil then
            itemClass = itemClass .. string.format(' ([[:Kategorie:Grad %s Komponente|%s]])', data.grade, data.grade )
        end
    end

    local size = data.size
    if size ~= nil and tonumber( size ) ~= nil then
        size = '[[:Kategorie:S' .. size ..' Komponente|S' ..size .. ']]'
    end

	if type( data.em ) == 'table' then
		data.em = table.concat( data.em, ' - ' )
	end

	if type( data.cooling_consumption ) == 'table' then
		data.cooling_consumption = table.concat( data.cooling_consumption, ' - ' )
	end
	
	box:renderSection( {
		content = {
    		box:renderItem( TNT.formatInLanguage( self.lang,  'Item', 'lbl_type' ), itemType or '-' ),
    		box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_size' ), size or '-' ),
    		box:renderItem( TNT.formatInLanguage( self.lang,  'Item', 'lbl_class' ), itemClass or '-' ),
		},
		col = 3
	} )
	
	box:renderSection( {
		title = TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_power_data' ),
		content = {
    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_basic_consumption' ), data.power_base or '-' ),
    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_power_consumption' ), data.power_draw or '-' ),
    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_ir' ), data.ir or '-' ),
    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_em' ), data.em or '-' ),
    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_cooling_consumption' ), data.cooling_consumption or '-' ),
		},
		col = 2
	} )

	local specification = {}

    -- Cooler
    if data.cooling_rate ~= nil then
        table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_cooling_rate' ), data.cooling_rate or '-' ) )
    end

    -- Power Plant
    if data.power_output ~= nil then
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_output_power' ), data.power_output or '-' ) )
    end

    -- Quantum Drive
    if data.fuel_rate ~= nil then
    	local info = mw.smw.info( 'Aktuell besitzen alle Quantenantriebe dieselbe Reichweite. Die Größe des Quantentreibstofftanks bestimmt, wie weit man mit einem Antrieb springen kann.', 'info' )

    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_fuel_consumption' ), data.fuel_rate or '-' ) )
		table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_quantum_range' ),  (data.jump_range or '-') .. info ) )
    end

    -- Shield
    if data.shield_points ~= nil then
        local downedDelay = data.downed_delay
        if downedDelay ~= nil then
            downedDelay = downedDelay .. 's'
        end

        local damageDelay = data.damage_delay
        if damageDelay ~= nil then
            damageDelay = damageDelay .. 's'
        end

        local shieldRegeneration = data.shield_regeneration
        if shieldRegeneration ~= nil then
            shieldRegeneration = shieldRegeneration .. ' Punkte/s'
        end

    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_shield_points' ), data.shield_points or '-' ) )
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_shield_point_regeneration' ), shieldRegeneration or '-' ) )
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_downtime' ), downedDelay or '-' ) )
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_damage_delay' ), damageDelay or '-' ) )
    end

    -- Missile Launcher
    if data.rocket_count ~= nil then
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_missile_count' ), data.rocket_count or '-' ) )
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_missile_size' ), string.format( 'S%s', data.rocket_size ) or '-' ) )
    end

    -- Mining Laser
    if data.consumable_slots ~= nil then
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_extraction_throughput' ), data.extraction_throughput or '-' ) )
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_consumables_count' ), data.consumable_slots or '-' ) )
    	table.insert( specification, box:renderItem( 'Energierate', data.energy_rate or '-' ) )
    	table.insert( specification, box:renderItem( 'Bergbau-Leistung', data.mining_power or '-' ) )
    	table.insert( specification, box:renderItem( 'Extraktion-Leistung', data.extraction_power or '-' ) )
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_effective_range' ), data.full_damage_range or '-' ) )
    	table.insert( specification, box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent','lbl_maximum_range' ), data.zero_damage_range or '-' ) )
    end
    
    -- Mining Modules
    if data.uses ~= nil then
		box:renderSection( {
			title = TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_specification' ),
			content = {
	    		box:renderItem( 'Typ', data.type or '-' ),
	    		box:renderItem( 'Anwendungen', data.uses or '-' ),
	    		box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_duration' ), data.uses or '-' )
			},
			col = 3
		} )
	else
		box:renderSection( {
			title = TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_specification' ),
			content = specification,
			col = 2
		} )
	end

	if data.consumable_slots ~= nil or data.uses ~= nil then
		box:renderSection( {
			title = TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_modifiers' ),
			content = {
	    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_resistance' ), ( ( data.resistance or 0 ) * 100 ) .. ' %' ),
	    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_resistance' ), ( ( data.instability or 0 ) * 100 ) .. ' %' ),
	    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_instability' ), ( ( data.all_charge_rates or 0 ) * 100 ) .. ' %' ),
	    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_all_charge_rates' ), ( ( data.shatter_damage or 0 ) * 100 ) .. ' %' ),
	    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_shatter_damage' ), ( ( data.throttle_speed or 0 ) * 100 ) .. ' %' ),
	    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_throttle_speed' ), ( ( data.throttle_speed or 0 ) * 100 ) .. ' %' ),
	    		box:renderItem( TNT.formatInLanguage( self.lang, 'VehicleComponent', 'lbl_optimal_charge_window' ), ( ( data.optimal_charge_window or 0 ) * 100 ) .. ' %' ),
			},
			col = 2
		} )
	end

    -- Turrets
    if data.max_mounts ~= nil then
		box:renderSection( {
			title = TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_attachments' ),
			content = {
	    		box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_attachments_min_size' ), string.format( 'S%s', data.min_size or 1 ) ),
	    		box:renderItem( TNT.formatInLanguage( self.lang,  'VehicleComponent', 'lbl_attachments_max_size' ), string.format( 'S%s', data.max_size or 1 ) ),
			},
			col = 2
		} )
    end

    return tostring( box ) .. tostring( common.generateInterWikiLinks( nameNormalized ) )
end


--- Entrypoint for {{#seo:}}
function methodtable.setSeoData(self)
    if self.currentFrame == nil then
        error( 'No frame set. Call "setFrame" first.', 0 )
    end

    local data = self:getSmwData()

    if nil == data.manufacturer then
        -- Faulty SMW data, don't call #seo
        return
    end

    require( 'Module:SEO' ).set(
        TNT.formatInLanguage( self.lang,  'VehicleComponent', 'seo_section' ),
        data.page,
        table.concat({
            data.name,
            data.type or '',
            self.currentFrame:preprocess( '{{SITENAME}}' )
        }, ' - '),
        'replace',
        {
            data.name or '',
            data.type or '',
            data.manufacturer or '',
            manufacturer.getCodeFromName( data.manufacturer ) or '',
            'Star Citizen',
            TNT.formatInLanguage( self.lang,  'VehicleComponent', 'seo_section' ),
            --'Klasse ' .. ( data.class or 'Unbekannt' )
        },
        nil,
        self.frameArgs[ 'image' ],
        data.page
    )
end


--- WIP: Create a manual ship item
function methodtable.addManual( self )
    if self.frameArgs[ 'Manuell' ] == nil then
        return
    end

    error("Not implemented - yet")
end


--- Load data and output the infobox
--- @param frame table The current frame
--- @return string Infobox and categories
function methodtable.run( self, frame )
    self.currentFrame = frame
    self.frameArgs = require( 'Module:Arguments' ).getArgs( frame )

    if type( self.frameArgs[ 'Bild' ] ) == 'string' then
    	self.frameArgs[ 'image' ] = self.frameArgs[ 'Bild' ]
	end

    if type( self.frameArgs[ 'Name' ] ) == 'string' then
    	self.frameArgs[ 'name' ] = self.frameArgs[ 'Name' ]
	end

    if self.frameArgs[ 1 ] ~= nil then
        self.objectType = self.frameArgs[ 1 ]
    end

	if not mw.title.getCurrentTitle().isSubpage then
        if self.frameArgs[ 'Manuell' ] ~= nil then
	        self:addManual()
	    else
	        self:saveApiData()
	    end	
	end

    local data = self:getSmwData()

    if type( data ) == 'string' then
    	return log.info( data )
	end

    self:setCategories( data )

    self:setSeoData()

    return tostring( self:getInfoBox() ) .. tostring( table.concat( self.categories ) )
end


--- New Instance
--- @return table VehicleComponent
function VehicleComponent.new( self, objectType )
    local instance = {
	    objectType = '',
	
	    currentFrame = {},
	    frameArgs = {},
	
	    apiData = nil,
	    itemData = nil,
	
	    pageName = {},
	    categories = {},
	}

    setmetatable( instance, metatable )

    if objectType == nil then
        error( 'Required argument "objectType" missing.' )
    end

    instance.objectType = objectType
    local title = mw.title.getCurrentTitle()
    instance.pageName = title.subpageText
    instance.lang = require( 'Module:Template translation' )._getLanguageSubpage( title )

    return instance
end


--- For direct invocation
function VehicleComponent.newFromFrame( frame )
    local instance = VehicleComponent:new( frame.args[ 'type' ], frame.args[ 'method' ] )

    return instance:run( frame )
end

return VehicleComponent