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( {
.. page .. '属性“-具有子对象”的应用范围受限,用户不能把它用作注释属性。++',
'?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', 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', 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