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