模块:VehicleHardpoint

来自星际公民中文百科
Module documentation[view][edit][history][purge]
This documentation is transcluded from 模块:VehicleHardpoint/doc. Changes can be proposed in the talk page.

This module saves and displays the hardpoints found on a vehicle. Implements {{Vehicle hardpoints}}.


local VehicleHardPoint = {}

local metatable = {}
local methodtable = {}

local common = require( 'Module:Common' )
local data = mw.loadData( 'Module:VehicleHardpoint/Data' )
local hatnote = require( 'Module:Hatnote' )._hatnote

metatable.__index = methodtable

-- Local functions

--- Checks if an entry contains a 'child' key with further entries
---
--- @return boolean
local function hasChildren( row )
    return row.children ~= nil and type( row.children ) == 'table' and #row.children > 0
end

--- Creates a key to be used in 'setHardPointObjects'
--- This allows to sum the total count of each similar object
---
--- @param row table - API Data
--- @param hardpointData table - Data from getHardpointData
--- @param parent table|nil - Parent hardpoint
--- @param root string|nil - Root hardpoint
--- @return string Key
local function makeKey( row, hardpointData, parent, root )
    local key

    if type( row.item ) == 'table' then
        if row.type == 'ManneuverThruster' or
           row.type == 'MainThruster' or
           row.type == 'WeaponDefensive' or
           row.type == 'WeaponLocker' or
           row.type == 'ArmorLocker' or
           row.type == 'Bed' or
           row.type == 'CargoGrid'
        then
            key = row.type .. row.sub_type
        else
            key = row.type .. row.sub_type .. row.item.uuid
        end
    else
        key = hardpointData.class .. hardpointData.type
    end

    if row.type ~= 'WeaponDefensive' then
        if parent ~= nil then
            key = key .. parent[ 'Hardpoint' ]
        end

        if root ~= nil and not string.match( key, root ) and ( hardpointData.class == 'Weapons' ) then
            key = key .. root
        end
    end

    if hardpointData.class == 'Weapons' and row.name ~= nil and row.type == 'MissileLauncher' then
        key = key .. row.name
    end

    --mw.log(string.format('Key: %s', key))
    return key
end


--- Tries to fix hardpoints that have no item, but everything set on the 'child' key
---
--- @param row table - API Data
--- @return table - Fixed entry
local function fixChild( row )
    if row.item == nil and hasChildren( row ) and #row.children == 1 then
        local item = row.children[ 1 ]

        local children = {}

        if hasChildren( item ) then
            children = item.children
            if item.item ~= nil and item.item.children ~= nil then
                item.item.children = {}
            end
        end

        row.name = item.name
        row.type = item.type
        row.sub_type = item.sub_type
        row.item = item.item
        if #children > 1 then
            row.children = children
        else
        	row.children = { data = {} }
        end
    end

    return row
end

--- Get pre-defined hardpoint data for a given hardpoint type or name
---
--- @param hardpointType string
--- @return table|nil
function methodtable.getHardpointData( self, hardpointType )
    if type( data.hardPointMappings[ hardpointType ] ) == 'table' then
        return data.hardPointMappings[ hardpointType ]
    end

    for hType, mappingData in pairs( data.hardPointMappings ) do
        if hardpointType == hType then
            return mappingData
        elseif type( mappingData.matches ) == 'table' then
            for _, matcher in pairs( mappingData.matches ) do
                if string.match( hardpointType, matcher ) ~= nil then
                    return mappingData
                end
            end
        end
    end

    return nil
end

--- Creates a settable SMW Subobject
---
--- @param row table - API Data
--- @param hardpointData table - Data from getHardpointData
--- @param parent table|nil - Parent hardpoint
--- @param root string|nil - Root hardpoint
--- @return table
function methodtable.makeObject( self, row, hardpointData, parent, root )
    local object = {}

    if hardpointData == nil then
        hardpointData = self:getHardpointData( row.type or row.name )
    end

    if hardpointData == nil then
        return nil
    end

    object[ 'Hardpoint' ] = row.name
    --object[ 'From game data' ] = true
    object[ 'Hardpoint minimum size' ] = row.min_size
    object[ 'Hardpoint maximum size' ] = row.max_size
    object[ 'Vehicle hardpoints template group' ] = hardpointData.class

    if data.hardPointNames[ row.type ] ~= nil then
        object[ 'Hardpoint type' ] = data.hardPointNames[ row.type ]
    else
        object[ 'Hardpoint type' ] = hardpointData.type
    end

    if data.hardPointNames[ row.sub_type ] ~= nil then
        object[ 'Hardpoint subtype' ] = data.hardPointNames[ row.sub_type ]
    else
        object[ 'Hardpoint subtype' ] = hardpointData.type
    end

    if hardpointData.item ~= nil then
    	if type( hardpointData.item.name ) == 'string' then object[ 'Name' ] = hardpointData.item.name end
    end

    if type( row.item ) == 'table' then
        local itemObj = row.item
        if itemObj.name ~= '<= PLACEHOLDER =>' then
            local match = string.match( row.class_name or '', 'Destruct_(%d+s)')
            if row.type == 'SelfDestruct' and match ~= nil then
                object[ 'Name' ] = 'Self destruct (' .. match .. ')'
            else
                object[ 'Name' ] = row.item.name
            end
        end

        if itemObj.type == 'WeaponDefensive' and type( itemObj.counter_measure ) == 'table' then
            object[ 'Magazine capacity' ] = itemObj.counter_measure.capacity
        end

        if ( itemObj.type == 'Cargo' or itemObj.type == 'SeatAccess' or itemObj.type == 'CargoGrid' or itemObj.type == 'Container' )
        	and type( itemObj.inventory ) == 'table' then
        	object[ 'Inventory' ] = common.formatNum( (itemObj.inventory.scu or nil), nil )
    	end

        if object[ 'Hardpoint minimum size' ] == nil then
            object[ 'Hardpoint minimum size' ] = itemObj.size
            object[ 'Hardpoint maximum size' ] = itemObj.size
        end

        object[ 'UUID' ] = row.item.uuid
    end

    if parent ~= nil then
        object[ 'Parent hardpoint UUID' ] = parent[ 'UUID' ]
        object[ 'Parent hardpoint' ] = parent[ 'Hardpoint' ]
    end

    if root ~= nil and root ~= row.name then
        object[ 'Root hardpoint' ] = root
    end

	-- Remove SeatAccess Hardpoints without storage
	if row.item ~= nil and row.item.type == 'SeatAccess' and object[ 'Inventory' ] == nil then
		object = nil
	end

    return object;
end

--- Sets all available hardpoints as sub-objects
--- This is the main method called by others
---
--- @param hardpoints table API Hardpoint data
function methodtable.setHardPointObjects( self, hardpoints )
    if type( hardpoints ) ~= 'table' then
        error( 'Hardpoints need to be a table' )
    end

    local out = {}

    local function addToOut( object, key )
        if object == nil then
            return
        end

        if type( out[ key ] ) ~= 'table' then
            if object ~= nil then
                out[ key ] = object
                out[ key ][ 'Item quantity' ] = 1
            end
        else
            out[ key ][ 'Item quantity' ] = out[ key ][ 'Item quantity' ] + 1

            if type( out[ key ][ 'Magazine capacity' ] ) == 'number' then
                out[ key ][ 'Magazine capacity' ] = out[ key ][ 'Magazine capacity' ] + object[ 'Magazine capacity' ]
            end

			if object[ 'Hardpoint type' ] == 'Cargo grid' then
				out[ key ][ 'Item quantity' ] = 1
				if out[ key ][ 'Inventory' ] ~= nil and object[ 'Inventory' ] ~= nil then
            		out[ key ][ 'Inventory' ] = tonumber(out[ key ][ 'Inventory' ]) + tonumber(object[ 'Inventory' ])
        		end
			end
        end
    end

    local depth = 1

    local function addHardpoints( hardpoints, parent, root )
        for _, hardpoint in pairs( hardpoints ) do
            hardpoint.name = string.lower( hardpoint.name )
            hardpoint = fixChild( hardpoint )

            if depth == 1 then
                root = hardpoint.name
                --mw.log(string.format('Root: %s', root))
            end

            hardpoint = VehicleHardPoint.fixTypes( hardpoint )

            local hardpointData = self:getHardpointData( hardpoint.type or hardpoint.name )

            if hardpointData ~= nil then
                local key = makeKey( hardpoint, hardpointData, parent, root )

                local obj = self:makeObject( hardpoint, hardpointData, parent, root )

                addToOut( obj, key )

                if hasChildren( hardpoint ) then
                    depth = depth + 1
                    addHardpoints( hardpoint.children, obj, root )
                end
            end
        end

        depth = depth - 1

        if depth < 1 then
            depth = 1
            root = nil
        end
    end

    addHardpoints( hardpoints )

    --mw.logObject(out)

    for _, subobject in pairs( out ) do
        mw.smw.subobject( subobject )
    end
end

--- Queries the SMW store for all available hardpoint subobjects for a given page
---
--- @param page string - The page to query
--- @return table hardpoints
function methodtable.querySmwStore( self, page )
    -- Cache multiple calls
    if self.smwData ~= nil then
        return self.smwData
    end

	local smwData = mw.smw.ask( {
        '[[-Has subobject::' .. page .. ']][[Hardpoint type::+]][[Vehicle hardpoints template group::+]]',
        '?Item quantity#-=count',
        '?Hardpoint minimum size#-=min_size',
        '?Hardpoint maximum size#-=max_size',
        '?Vehicle hardpoints template group=class',
        '?Hardpoint type=type',
        '?Hardpoint subtype=sub_type',
        '?Name#-=name',
        '?Inventory#-n=scu',
        '?UUID#-=uuid',
        '?Hardpoint#-=hardpoint',
        '?Magazine capacity#-=magazine_size',
        '?Parent hardpoint#-=parent_hardpoint',
        '?Root hardpoint#-=root_hardpoint',
        '?Parent UUID#-=parent_uuid',
        '?Name.Grade#-=item_grade',
        '?Name.Class#-=item_class',
        '?Name.Size#-=item_size',
        '?Name.Manufacturer#-=manufacturer',
        'sort=Vehicle hardpoints template group,Hardpoint type,Hardpoint maximum size,Item quantity',
        'order=asc,asc,asc,asc',
        'limit=1000'
    } )

    if smwData == nil or smwData[ 1 ] == nil then
        return nil
    end

    mw.logObject( smwData )

    self.smwData = smwData

    return self.smwData
end

--- Group Hardpoints by Class and type
---
--- @param smwData table SMW data - Requires a 'class' key on each row
--- @return table
function methodtable.group( self, smwData )
    local grouped = {}

    if type( smwData ) ~= 'table' then
        return {}
    end

    for _, row in self.spairs( smwData ) do
        if not row.isChild and row.class ~= nil and row.type ~= nil then
            if type( grouped[ row.class ] ) ~= 'table' then
                grouped[ row.class ] = {}
            end

            if type( grouped[ row.class ][ row.type ] ) ~= 'table' then
                grouped[ row.class ][ row.type ] = {}
            end

            table.insert( grouped[ row.class ][ row.type ], row )
        end
    end

    --mw.logObject( grouped )

    return grouped
end

--- Adds children to the according parents
---
--- @param smwData table All available Hardpoint objects for this page
--- @return table The stratified table
function methodtable.createDataStructure( self, smwData )
    -- Maps object id to key in array
    local idMapping = {}

    for key, object in pairs( smwData ) do
        if object.hardpoint ~= nil then
            local keyMap = ( object.root_hardpoint or object.hardpoint ) .. object.hardpoint

            idMapping[ keyMap ] = key
        end
    end

    local function stratify( toStratify )
        for _, object in pairs( toStratify ) do
            if object.parent_hardpoint ~= nil then
                local parentEl = toStratify[ idMapping[ (object.root_hardpoint or '') .. object.parent_hardpoint ] ]

                if parentEl ~= nil then
                    if parentEl.children == nil then
                        parentEl.children = {}
                    end

                    object.isChild = true

                    table.insert( parentEl.children, object )
                end
            end
        end
    end

    stratify( smwData )

    return smwData
end

--- Generate the output
---
--- @param groupedData table Grouped SMW data
--- @return table
function methodtable.makeOutput( self, groupedData )
    local classOutput = {}

	-- An item with potential children
    local function makeEntry( item, depth )
    	-- Info if data stems from ship-matrix or game files
        if classOutput.info == nil then
            local text
            ---if item.from_gamedata == true then	
            text = 'Data extracted from game data.'
            ---else
            ---    text = 'Data extracted from ship matrix.'
            ---end

            classOutput.info = hatnote( text, { icon = 'WikimediaUI-Robot.svg' } )
        end

        depth = depth or 1

		local row = mw.html.create( 'div' )
            :addClass( 'template-component' )
            :addClass( string.format( 'template-component--level-%d', depth) )
			:tag('div')
				:addClass('template-component__connectors')
					:tag('div'):addClass('template-component__connectorX'):done()
					:tag('div'):addClass('template-component__connectorY'):done()
			:done()

        if item.magazine_size ~= nil then
            item.count = item.magazine_size
        end

        local size = 'N/A'
        local prefix = 'S'

        ---if item.from_gamedata == true or item.class == 'Weapons' then
        ---    prefix = 'S'
        ---end

        if item.item_size ~= nil then
            size = string.format( '%s%s', prefix, item.item_size )
        else
            size = string.format( '%s%s', prefix, item.max_size )
        end

        local nodeSizeCount = mw.html.create( 'div' )
        	:addClass('template-component__port')
            	:tag( 'div' )
                	:addClass( 'template-component__count' )
            		:wikitext( string.format( '%dx', item.count ) )
                :done()

        if item.class ~= 'Cargo grid' then
	        nodeSizeCount
	            :tag( 'div' )
	                :addClass( 'template-component__size' )
	                    :wikitext( size )
	                :done()
        end

        nodeSizeCount = nodeSizeCount:allDone()

        local title = item.sub_type or item.type
        if item.name ~= nil then
            if data.nameFixes[ item.name ] ~= nil then
                title = string.format( '[[%s|%s]]', data.nameFixes[ item.name ], item.name )
            else
                title = string.format( '[[%s]]', item.name )
            end
        end

		local subtitle = item.manufacturer or 'N/A'
        if item.manufacturer ~= nil and item.manufacturer ~= 'N/A' then
            subtitle = string.format( '[[%s]]', item.manufacturer )
        end

		-- Show SCU in subtitle
		if item.scu ~= nil then
	        if item.type == 'Cargo grid' then
	        	subtitle = item.scu .. ' SCU' or 'N/A'
	        elseif item.type == 'Personal storage' then
	    		subtitle = item.scu * 100 .. 'K µSCU'  or 'N/A'
	    	end
    	end

        local nodeItemManufacturer = mw.html.create( 'div' )
        	:addClass( 'template-component__item' )
            	:tag( 'div' )
            	:addClass( 'template-component__title' )
            	:wikitext( title )
            :done()
            :tag( 'div' )
                :addClass( 'template-component__subtitle' )
                :wikitext( subtitle )
                :done()
                :allDone()

        row:tag('div')
			:addClass('template-component__card')
			:node( nodeSizeCount )
			:node( nodeItemManufacturer )
        :done()

        row = tostring( row )

        if type( item.children ) == 'table' then
            depth = depth + 1
            for _, child in self.spairs( item.children ) do
                row = row .. makeEntry( child, depth )
            end
        end

        return row
    end


	-- Items of a given class e.g. avionics
    local function makeSection( types )
        local out = ''

        for classType, items in self.spairs( types ) do
        	local label = classType

			-- Label override
        	if data.sectionLabelFixes[ classType ] ~= nil then
                label = data.sectionLabelFixes[ classType ]
            end

            local icon = string.format( '[[File:Hardpoints icon %s.svg|20px|link=]]', string.lower( label ) )
            -- Disable label missing icons for now
            for _, labelMissingIcon in pairs( data.labelsMissingIcon ) do
            	if label == labelMissingIcon then icon = '' end
            end

            local section = mw.html.create( 'div' )
                :addClass( 'template-components__section')
                :tag( 'div' )
                    :addClass( 'template-components__label' )
                    :wikitext( string.format(
                        '%s %s',
                        icon,
                        label
                    ) )
                :done()
                :tag( 'div' ):addClass( 'template-components__group' )

            local str = ''

            for _, item in self.spairs( items ) do
                if not item.isChild then
            		local subGroup = mw.html.create('div')
			            :addClass( 'template-components__subgroup' )
			            :node( makeEntry( item ) )
			            :allDone()
                	str = str .. tostring( subGroup )
                end
            end

            out = out .. tostring( section:node( str ):allDone() )
        end

        return out
    end


    for class, types in self.spairs( groupedData ) do
        classOutput[ class ] = makeSection( types )
    end

    ---mw.logObject(classOutput)

    return classOutput
end

--- Generates tabber output
function methodtable.out( self )
    local smwData = self:querySmwStore( self.page )

    if smwData == nil then
        return hatnote( 'SMW data not found on [[' .. self.page .. ']].', { icon = 'WikimediaUI-Error.svg' } )
    end

    smwData = self:createDataStructure( smwData )
    smwData = self:group( smwData )

    local output = self:makeOutput( smwData )

	--- Class corresponds to Module:VehicleHardpoint/Data
    local avSys = ( tostring( output[ 'Avionics' ] or '' ) ) .. ( tostring( output[ 'Systems' ] or '' ) )
    local prThr = ( tostring( output[ 'Propulsion' ] or '' ) ) .. ( tostring( output[ 'Thrusters' ] or '' ) )
    local weUti = ( tostring( output[ 'Weapons' ] or '' ) ) .. ( tostring( output[ 'Utility' ] or '' ) )
    local caFac = ( tostring( output[ 'Cargo' ] or '' ) ) .. ( tostring( output[ 'Facilities' ] or '' ) )

    if #avSys > 0 then
        avSys = avSys .. ( output.info or '' )
    else
        avSys = hatnote( 'No avionics or systems available' )
    end

    if #prThr > 0 then
        prThr = prThr .. ( output.info or '' )
    else
        prThr = hatnote( 'No propulsion or thrusters available.' )
    end

    if #weUti > 0 then
        weUti = weUti .. ( output.info or '' )
    else
        weUti = hatnote( 'No weaponry or utility items present.' )
    end

    if #caFac > 0 then
        caFac = caFac .. ( output.info or '' )
    else
        caFac = hatnote( 'No cargo or facilities information found.' )
    end

    local format = string.format([[
%s=%s
|-|
%s=%s
|-|
%s=%s
|-|
%s=%s
]],
			'Avionics & Systems',
            avSys,
            'Propulsion & Thrusters',
            prThr,
            'Weaponry & Utility',
            weUti,
            'Cargo & Facilities',
            caFac
    )

    return mw.getCurrentFrame():extensionTag{
        name = 'tabber', content = format
    } .. mw.getCurrentFrame():extensionTag{
		name = 'templatestyles', args = { src = 'Template:Vehicle hardpoints/styles.css' }
	}
end

--- Manually fix some (sub_)types by checking the hardpoint name
---
--- @param hardpoint table Entry from the api
--- @return table The fixed entry
function VehicleHardPoint.fixTypes( hardpoint )
    if hardpoint.type == 'ManneuverThruster' or hardpoint.type == 'MainThruster' then
        if ( hardpoint.sub_type == 'FixedThruster' or hardpoint.sub_type == 'UNDEFINED' ) and
                string.match( string.lower( hardpoint.name ), 'vtol' ) ~= nil then
            hardpoint.sub_type = 'VtolThruster'
        end

        if ( hardpoint.sub_type == 'FixedThruster' or hardpoint.sub_type == 'UNDEFINED' ) and
                string.match( string.lower( hardpoint.name ), 'retro' ) ~= nil then
            hardpoint.sub_type = 'RetroThruster'
        end

        if ( hardpoint.sub_type == 'FixedThruster' or hardpoint.sub_type == 'UNDEFINED' ) and
                string.match( string.lower( hardpoint.name ), 'retro' ) ~= nil then
            hardpoint.sub_type = 'RetroThruster'
        end
    
    	if ( hardpoint.sub_type == 'JointThruster' or hardpoint.sub_type == 'UNDEFINED' ) and
                string.match( string.lower( hardpoint.name ), 'grav' ) ~= nil then
            hardpoint.sub_type = 'GravLev'
        end

        if hardpoint.type == 'MainThruster' then
            hardpoint.sub_type = 'Main' .. hardpoint.sub_type
        end
    end

    if hardpoint.type == 'WeaponDefensive' then
        if ( hardpoint.sub_type == 'CountermeasureLauncher' or hardpoint.sub_type == 'UNDEFINED' ) and
                ( string.match( string.lower( hardpoint.class_name ), 'decoy' ) ~= nil or
                  string.match( string.lower( hardpoint.class_name ), 'flare' ) ~= nil) then
            hardpoint.sub_type = 'DecoyLauncher'
        end

        if ( hardpoint.sub_type == 'CountermeasureLauncher' or hardpoint.sub_type == 'UNDEFINED' ) and
            ( string.match( string.lower( hardpoint.class_name ), 'chaff' ) ~= nil  or
              string.match( string.lower( hardpoint.class_name ), 'noise' ) ~= nil) then
            hardpoint.sub_type = 'NoiseLauncher'
        end

        if type( hardpoint.item ) == 'table' and hardpoint.item ~= nil then
            hardpoint.item.name = '<= PLACEHOLDER =>'
        end
    end

    if hardpoint.type == 'FuelTank' or hardpoint.type == 'QuantumFuelTank' then
        local prefix = ''
        if hardpoint.type == 'QuantumFuelTank' then
            prefix = 'Quantum'
        end

        --- Fuel refinery (e.g. Starfarer)
        if string.match( string.lower( hardpoint.class_name ), 'fuel_refinery' ) ~= nil then
            hardpoint.type = 'FuelRefinery'
        end

        if string.match( string.lower( hardpoint.class_name ), 'small' ) ~= nil then
            hardpoint.sub_type = prefix .. 'FuelTankSmall'
        end

        if string.match( string.lower( hardpoint.class_name ), 'large' ) ~= nil then
            hardpoint.sub_type = prefix .. 'FuelTankLarge'
        end
    end

    if hardpoint.type == 'Turret' then
    	--- Gimbal mount
        if hardpoint.sub_type == 'GunTurret' and string.match( string.lower( hardpoint.class_name ), 'mount_gimbal' ) ~= nil then
	        hardpoint.type = 'WeaponGun'
	        hardpoint.sub_type = 'GimbalMount'
	    -- Pilot controllable weapon (e.g. F7CM, Mustang Delta)
        elseif hardpoint.sub_type == 'BallTurret' or hardpoint.sub_type == 'CanardTurret' then
        	hardpoint.type = 'WeaponGun'
        -- Reclaimer remote salvage turret
        elseif hardpoint.sub_type == 'Utility' and string.match( string.lower( hardpoint.class_name ), 'salvage' ) ~= nil then
    		hardpoint.type = 'UtilityTurret'
    		hardpoint.sub_type = 'GunTurret'
    	-- Fix remote turret designation
        elseif hardpoint.sub_type == 'Turret' and string.match( string.lower( hardpoint.class_name ), 'remote' ) ~= nil then
			hardpoint.sub_type = 'RemoteTurret'
        end
    end
    
    if hardpoint.type == 'ToolArm' then
    	if hardpoint.sub_type == 'UNDEFINED' then
    		if string.match( string.lower( hardpoint.class_name ), 'mining' ) ~= nil then
            	hardpoint.sub_type = 'MiningArm'
    		elseif string.match( string.lower( hardpoint.class_name ), 'salvage' ) ~= nil then
    			hardpoint.sub_type = 'SalvageArm'
            end
    	end
    end

	-- Manual mapping defined in Module:VehicleHardpoint/Data
    if type( hardpoint.item ) == 'table' and hardpoint.item ~= nil then
        for _, mapping in pairs( data.hardPointTypeFixes ) do
	    	for _, matcher in pairs( data.hardPointMappings[mapping]['matches'] ) do
	    		if string.match( hardpoint.name, matcher ) ~= nil then
	    			hardpoint.type = mapping
	    			return hardpoint
				end
	    	end
    	end
    end

    return hardpoint
end

--- New Instance
---
--- @return table VehicleHardPoint
function VehicleHardPoint.new( self, page )
    local instance = {
        page = page or nil,
        spairs = require( 'Module:Common' ).spairs
    }

    setmetatable( instance, metatable )

    return instance
end

--- Parser call for generating the table
function VehicleHardPoint.outputTable( frame )
	local args = require( 'Module:Arguments' ).getArgs( frame )
    local page = args[ 1 ] or args[ 'Name' ] or mw.title.getCurrentTitle().rootText

    local instance = VehicleHardPoint:new( page )

	if args['debug'] ~= nil then
		local smwData = instance:querySmwStore(page)
		local struct = instance:createDataStructure( smwData )
        local group = instance:group( struct )
	    return mw.dumpObject(smwData) .. mw.dumpObject(struct) .. mw.dumpObject(group)
	end
    return instance:out()
end

return VehicleHardPoint

Debug data: