Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Please sign up or log in to edit the wiki.

Module:Item: Difference between revisions

From the Star Citizen Wiki, the fidelity™ encyclopedia
Content deleted Content added
No edit summary
No edit summary
Tag: Reverted
Line 1: Line 1:
require( 'strict' )
require( 'strict' )


local Item = {}
local p = {}


local metatable = {}
local MODULE_NAME = 'VehicleItem'
local methodtable = {}

metatable.__index = methodtable


local TNT = require( 'Module:Translate' ):new()
local TNT = require( 'Module:Translate' ):new()
local common = require( 'Module:Common' )
local smwCommon = require( 'Module:Common/SMW' )
local manufacturer = require( 'Module:Manufacturer' )._manufacturer
local data = mw.loadJsonData( 'Module:Item/' .. MODULE_NAME .. '/data.json' )
local hatnote = require( 'Module:Hatnote' )._hatnote
local data = mw.loadJsonData( 'Module:Item/data.json' )
local config = mw.loadJsonData( 'Module:Item/config.json' )
local config = mw.loadJsonData( 'Module:Item/config.json' )


local common = require( 'Module:Common' )
local lang

if config.module_lang then

lang = mw.getLanguage( config.module_lang )
--- Merge two tables where they share the same key-value pairs
else
--- TODO: Move to Common
lang = mw.getContentLanguage()
---
---@param t1 table
---@param t2 table
---@return table
local function mergeTables( t1, t2 )
-- Returns the union of the values of two tables, as a sequence.
local vals = {}
for k, v in pairs( t1 ) do
vals[ v ] = true
end
for k, v in pairs( t2 ) do
vals[ v ] = true
end
local ret = {}
for k, v in pairs( vals ) do
table.insert( ret, k )
end
return ret
end
end

local moduleCache = {}




Line 31: Line 42:
--- @return string If the key was not found in the .tab page, the key is returned
--- @return string If the key was not found in the .tab page, the key is returned
local function translate( key, addSuffix, ... )
local function translate( key, addSuffix, ... )
return TNT:translate( 'Module:Item/i18n.json', config, key, addSuffix, {...} ) or key
return TNT:translate( 'Module:Item/' .. MODULE_NAME .. '/i18n.json', config, key, addSuffix, { ... } )
end
end




--- Adds the properties valid for this item to the SMW Set object
--- Invokes a method on the required module, if the modules type matches targetType
--- Utilizes the moduleCache to only load modules once
---
---
--- @param smwSetObject table
--- @param targetType string|boolean The type to check against extension_modules.type or 'true' to run against all modules
function p.addSmwProperties( apiData, frameArgs, smwSetObject )
--- @param methodName string The method to invoke
smwCommon.addSmwProperties(
--- @param args table Arguments passed to the method
apiData,
local function runModuleFN( targetType, methodName, args, returnsData )
frameArgs,
returnsData = returnsData or false
smwSetObject,
for _, module in ipairs( data.extension_modules ) do
translate,
if targetType == true or ( module.type ~= nil and type( module.type ) == 'table' ) then
config,
for _, type in ipairs( module.type ) do
data,
if module ~= nil and ( targetType == true or targetType == type ) then
'Item/' .. MODULE_NAME
if moduleCache[ module.name ] == nil then
local success, mod = pcall( require, module.name )
if success then
moduleCache[ module.name ] = mod
end
end
module = moduleCache[ module.name ]

if module ~= nil then
local result = module[ methodName ]( unpack( args ) )
if returnsData then
return result
end
end
end
end
end
end
end


--- Creates the object that is used to query the SMW store
---
--- @param page string the item page containing data
--- @return table
local function makeSmwQueryObject( page )
local query = {
string.format( '[[%s]]', page ),
'?Page image#-=image'
}

require( 'Module:Common/SMW' ).addSmwAskProperties(
query,
translate,
config,
data
)

runModuleFN( true, 'addSmwAskProperties', { query } )

table.insert( query, 'limit=1' )

return query
end


--- Request Api Data
--- Using current subpage name without item type suffix
--- @return table|nil
function methodtable.getApiDataForCurrentPage( self )
local api = require( 'Module:Common/Api' )

local query = self.frameArgs[ translate( 'ARG_UUID' ) ] or self.frameArgs[ translate( 'ARG_Name' ) ] or common.removeTypeSuffix(
mw.title.getCurrentTitle().rootText,
config.name_suffixes
)
)


local setData = {}
local success, json = pcall( mw.text.jsonDecode, mw.ext.Apiunto.get_raw( 'v2/items/' .. query, {
include = data.includes,
locale = config.api_locale
} ) )


local formatConfig = {
if not success or api.checkResponseStructure( json, true, false ) == false then return end
type = 'number'
}


-- TODO: Modifiers and Damages are generic enough that maybe we should search for it by default on Module:Item?
self.apiData = json[ 'data' ]
smwCommon.setFromTable( setData, apiData:get( 'mining_laser.modifiers' ), 'display_name', 'value', 'Modifier',
self.apiData = api.makeAccessSafe( self.apiData )
translate, formatConfig )
smwCommon.setFromTable( setData, apiData:get( 'mining_module.modifiers' ), 'display_name', 'value', 'Modifier',
translate, formatConfig )
smwCommon.setFromTable( setData, apiData:get( 'bomb.damages' ), 'name', 'damage', 'Damage', translate, formatConfig )
smwCommon.setFromTable( setData, apiData:get( 'missile.damages' ), 'name', 'damage', 'Damage', translate,
formatConfig )


-- TODO: Implement this for bombs and missiles
return self.apiData
if apiData.vehicle_weapon then
end
-- Save damages as subobjects, we did not do it through data.json because we need to build the key
-- for the damage SMW properties such as SMW_DamageEnergy
-- TODO: This is generic enough that we should consider making it shared for bombs, missile, personal weapon, and vehicle weapons
if apiData.vehicle_weapon.damages then
local ucfirst = require( 'Module:String2' ).ucfirst
local damages = apiData.vehicle_weapon.damages


local typeKey = translate( 'SMW_DamageType' )


local subobjects = {}
--- Base Properties that are shared across all items
for _, damage in ipairs( damages ) do
--- @return table SMW Result
-- FIXME: Wikipedia modules like Module:String2 does not have a proper Lua entry point
function methodtable.setSemanticProperties( self )
-- Perhaps we should look into it some day
local setData = {}
local ucfirstArgs = { args = { damage.name } }
local dmgKey = translate( 'SMW_Damage' .. ucfirst( ucfirstArgs ) )


-- If there are no existing tables, set one first
local smwCommon = require( 'Module:Common/SMW' )
if not next( subobjects ) then
table.insert( subobjects, {
[ typeKey ] = damage.type,
[ dmgKey ] = damage.damage
} )
-- If damage type is the same, add the damage value of the under the same table
elseif subobjects[ #subobjects ] and subobjects[ #subobjects ][ dmgKey ] and subobjects[ #subobjects ][ dmgKey ] == damage.type then
subobjects[ #subobjects ][ dmgKey ] = damage.damage
else
table.insert( subobjects, {
[ typeKey ] = damage.type,
[ dmgKey ] = damage.damage
} )
end
end


mw.logObject( damages, 'Dmg subobjects' )
smwCommon.addSmwProperties(
mw.logObject( subobjects, 'Dmg subobjects' )
self.apiData,
self.frameArgs,
setData,
translate,
config,
data,
'Item'
)


for _, subobject in pairs( subobjects ) do
setData[ translate( 'SMW_Name' ) ] = self.frameArgs[ translate( 'ARG_Name' ) ] or common.removeTypeSuffix(
mw.smw.subobject( subobject )
mw.title.getCurrentTitle().rootText,
end
config.name_suffixes
end
)

if type( setData[ translate( 'SMW_Manufacturer' ) ] ) == 'string' then
local man = manufacturer( setData[ translate( 'SMW_Manufacturer' ) ] )
if man ~= nil then man = man.name end

setData[ translate( 'SMW_Manufacturer' ) ] = man or setData[ translate( 'SMW_Manufacturer' ) ]
setData[ translate( 'SMW_Manufacturer' ) ] = mw.ustring.format( '[[%s]]', setData[ translate( 'SMW_Manufacturer' ) ] )
end

-- Set properties with API data
if self.apiData ~= nil and self.apiData.uuid ~= nil then
--- Commodity
local commodity = require( 'Module:Commodity' ):new()
commodity:addShopData( self.apiData )

if self.apiData.type ~= nil and self.apiData.sub_type ~= nil then
-- Merge subtype into type, like how the game handles it
if self.apiData.sub_type ~= 'UNDEFINED' then
-- SMW_Type is already set prior if self.apiData.type exists
setData[ translate( 'SMW_Type' ) ] = mw.ustring.format( '%s.%s', setData[ translate( 'SMW_Type' ) ], self.apiData.sub_type )
end

local descData = self.apiData.description_data
if descData ~= nil then
for _, descObj in ipairs( descData ) do
-- Check if there are item type localization
if descObj.name == 'Item Type' or descObj.name == 'Type' then
local descType = descObj.type
-- FIXME: This only works for English, need some way to get only the English text for comparison since descType is always in English
local itemType = translate( mw.ustring.format( 'type_%s', mw.ustring.lower( setData[ translate( 'SMW_Type' ) ] ) ) )

-- If the type in item description is different than what we compose out of type and subtype data, record it in subtype
-- TODO: We should make a common function to sanitize strings for comaprison (e.g. lowercase, remove all the space)
if mw.ustring.lower( descType ) ~= mw.ustring.lower( itemType ) then
setData[ translate( 'SMW_Subtype' ) ] = descType
end
end
end
end
end
end

runModuleFN( setData[ translate( 'SMW_Type' ) ], 'addSmwProperties', { self.apiData, self.frameArgs, setData } )

mw.logObject( setData, 'SET' )

self.setData = setData

return mw.smw.set( setData )
end


--- Queries the SMW Store
--- @return table
function methodtable.getSmwData( self )
-- Use cached data if possible, SMW queries are expensive
if self.smwData ~= nil and self.smwData[ translate( 'SMW_Name' ) ] ~= nil then
return self.smwData
end
end


mw.smw.set( setData )
local queryName = self.frameArgs[ translate( 'ARG_SmwQueryName' ) ] or
mw.title.getCurrentTitle().fullText

local smwData = mw.smw.ask( makeSmwQueryObject( queryName ) )

if smwData == nil or smwData[ 1 ] == nil then
return hatnote( mw.ustring.format(
'%s[[%s]]',
translate( 'error_no_data_text' ),
translate( 'error_category_script_error' )
),
{ icon = 'WikimediaUI-Error.svg' }
)
end

self.smwData = smwData[ 1 ]

return self.smwData
end
end


--- Adds all SMW parameters set by this Module to the ASK object

---
--- Creates the infobox
--- @param smwAskObject table
function methodtable.getInfobox( self )
--- @return nil
local smwData = self:getSmwData()
function p.addSmwAskProperties( smwAskObject )

smwCommon.addSmwAskProperties(
local infobox = require( 'Module:InfoboxNeue' ):new( {
smwAskObject,
placeholderImage = config.placeholder_image
translate,
} )
config,
local tabber = require( 'Module:Tabber' ).renderTabber
data

)
--- SMW Data load error
--- Infobox data should always have Name property
if type( smwData ) ~= 'table' then
return infobox:renderInfobox( infobox:renderMessage( {
title = translate( 'error_no_infobox_data_title' ),
desc = translate( 'error_no_data_text' ),
} ) )
end

local function getManufacturer()
if smwData[ translate( 'SMW_Manufacturer' ) ] == nil then return end

local mfu = manufacturer( smwData[ translate( 'SMW_Manufacturer' ) ] )
if mfu == nil then return '[[' .. smwData[ translate( 'SMW_Manufacturer' ) ] .. ']]' end

return infobox.showDescIfDiff(
table.concat( { '[[', smwData[ translate( 'SMW_Manufacturer' ) ], '|', mfu.name , ']]' } ),
mfu.code
)
end

local function getType()
if smwData[ translate( 'SMW_Type' ) ] == nil then return end

local itemType = translate( mw.ustring.format( 'type_%s', mw.ustring.lower( smwData[ translate( 'SMW_Type' ) ] ) ) )

if mw.ustring.find( itemType, 'type_' ) then
itemType = smwData[ translate( 'SMW_Type' ) ]
end

return mw.ustring.format( '[[%s]]', itemType )
end

local function getSize()
if smwData[ translate( 'SMW_Size' ) ] == nil then return end
return 'S' .. smwData[ translate( 'SMW_Size' ) ]
end

local function getClass()
if smwData[ translate( 'SMW_Class' ) ] == nil then return end

local classKey = mw.ustring.lower( smwData[ translate( 'SMW_Class' ) ] )
local class = translate( mw.ustring.format( 'class_%s', classKey ) )

if smwData[ translate( 'SMW_Grade' ) ] ~= nil then
class = class .. ' (' .. smwData[ translate( 'SMW_Grade' ) ] .. ')'
end

return class
end

--- Other sites
local function getOfficialSites()
local links = {}

for _, site in ipairs( data.official_sites ) do
local query = smwData[ translate( site.attribute ) ]

if query ~= nil then
table.insert( links, infobox:renderLinkButton( {
label = translate( site.label ),
link = query
} ) )
end
end

return links
end

local function getCommunitySites()
local links = {}

for _, site in ipairs( data.community_sites ) do
local query = smwData[ translate( site.data ) ]

if query ~= nil then
if site.data == 'SMW_ClassName' or site.data == 'SMW_UUID' then
query = mw.ustring.lower( query )
elseif site.data == 'SMW_ShipMatrixName' then
query = mw.uri.encode( query, 'PATH' )
end

if site.label == 'FleetYards' then
query = mw.ustring.lower( mw.ustring.gsub( query, '%%20', '-' ) )
end

table.insert( links, infobox:renderLinkButton( {
label = site.label,
link = mw.ustring.format( site.format, query )
} ) )
end
end

return links
end


local image = self.frameArgs[ translate( 'ARG_Image' ) ] or self.frameArgs[ 'image' ] or smwData[ 'image' ]
infobox:renderImage( image )

infobox:renderHeader( {
title = smwData[ translate( 'SMW_Name' ) ],
--- e.g. Aegis Dynamics (AEGS)
subtitle = getManufacturer(),
badge = getSize()
} )


--- Type, Size, Class
infobox:renderSection( {
content = {
infobox:renderItem( {
label = translate( 'LBL_Type' ),
data = getType(),
} ),
infobox:renderItem( {
label = translate( 'LBL_Subtype' ),
data = smwData[ translate( 'SMW_Subtype' ) ],
} ),
infobox:renderItem( {
label = translate( 'LBL_Class' ),
data = getClass(),
} ),
infobox:renderItem( {
label = translate( 'LBL_Occupancy' ),
data = smwData[ translate( 'SMW_Occupancy' ) ],
} ),
infobox:renderItem( {
label = translate( 'LBL_Inventory' ),
data = smwData[ translate( 'SMW_Inventory' ) ],
} )
},
col = 2
} )

local pageIdentifier = self.frameArgs[ translate( 'ARG_SmwQueryName' ) ] or mw.title.getCurrentTitle().fullText
runModuleFN( smwData[ translate( 'SMW_Type' ) ], 'addInfoboxData', { infobox, smwData, pageIdentifier } )

--- Dimensions
infobox:renderSection( {
title = translate( 'LBL_Dimensions' ),
content = {
infobox:renderItem( {
label = translate( 'LBL_Length' ),
data = smwData[ translate( 'SMW_EntityLength' ) ],
} ),
infobox:renderItem( {
label = translate( 'LBL_Width' ),
data = smwData[ translate( 'SMW_EntityWidth' ) ],
} ),
infobox:renderItem( {
label = translate( 'LBL_Height' ),
data = smwData[ translate( 'SMW_EntityHeight' ) ],
} ),
infobox:renderItem( {
label = translate( 'LBL_Mass' ),
data = smwData[ translate( 'SMW_Mass' ) ],
} )
},
col = 3
} )

--- Metadata section
infobox:renderSection( {
class = 'infobox__section--metadata infobox__section--hasBackground',
content = {
infobox:renderItem( {
label = translate( 'SMW_UUID' ),
data = smwData[ translate( 'SMW_UUID' ) ],
row = true,
spacebetween = true
} ),
infobox:renderItem( {
label = translate( 'SMW_ClassName' ),
data = smwData[ translate( 'SMW_ClassName' ) ],
row = true,
spacebetween = true
} ),
infobox:renderItem( {
label = translate( 'SMW_GameBuild' ),
data = smwData[ translate( 'SMW_GameBuild' ) ],
row = true,
spacebetween = true
} )
}
} )

--- Actions section
if smwData[ translate( 'SMW_UUID' ) ] then
infobox:renderSection( {
class = 'infobox__section--actions infobox__section--hasBackground',
content = {
infobox:renderItem( {
icon = 'WikimediaUI-Search.svg',
data = translate( 'actions_find_item_title' ),
desc = translate( 'actions_find_item_text' ),
-- FIXME: Make this configurable?
link = 'https://finder.cstone.space/search/' .. smwData[ translate( 'SMW_UUID' ) ]
} )
}
} )
end

--- Footer
infobox:renderFooter( {
button = {
icon = 'WikimediaUI-Globe.svg',
label = translate( 'LBL_OtherSites' ),
type = 'popup',
content = infobox:renderSection( {
content = {
infobox:renderItem( {
label = translate( 'LBL_OfficialSites' ),
data = table.concat( getOfficialSites(), '' )
} ),
infobox:renderItem( {
label = translate( 'LBL_CommunitySites' ),
data = table.concat( getCommunitySites(), '' )
} ),
},
class = 'infobox__section--linkButtons',
}, true )
}
} )

return infobox:renderInfobox( nil, smwData[ translate( 'SMW_Name' ) ] )
end
end


--- Adds entries to the infobox
---
--- @param infobox table The Module:InfoboxNeue instance
--- @param smwData table Data from Semantic MediaWiki
--- @return nil
function p.addInfoboxData( infobox, smwData, itemPageIdentifier )
local tabber = require( 'Module:Tabber' ).renderTabber
local tabberData = {}
local section


local function getDamagesSection()
--- Creates the description
local damageTypes = {
function methodtable.getDescription( self )
'Physical',
local smwData = self:getSmwData()
'Energy',
'Distortion',
'Thermal',
'Biochemical',
'Stun'
}


local smwProps = { 'SMW_DamageType' }
--- Error: No SMW Data
for _, damageType in ipairs( damageTypes ) do
if type( smwData ) ~= 'table' then
table.insert( smwProps, 'SMW_Damage' .. damageType )
return require( 'Module:Mbox' )._mbox(
end
translate( 'error_no_description_title' ),
translate( 'error_no_data_text' ),
{ icon = 'WikimediaUI-Error.svg' }
)
end


local subobjects = smwCommon.loadSubobjects(
--- Error: No description SMW property
itemPageIdentifier,
if smwData[ translate( 'SMW_Description' ) ] == nil then
'SMW_DamageType',
return require( 'Module:Mbox' )._mbox(
smwProps,
translate( 'error_no_description_title' ),
translate
translate( 'error_no_description_text' ),
)
{ icon = 'WikimediaUI-Error.svg' }
)
end


if type( subobjects ) == 'table' then
return '<blockquote>' .. smwData[ translate( 'SMW_Description' ) ] .. '</blockquote>'
local damagesTabberData = {}
end
local damagesTabCount = 1


for _, damage in ipairs( subobjects ) do
section = {}
damagesTabberData[ 'label' .. damagesTabCount ] = translate( 'damagetype_' ..
damage[ translate( 'SMW_DamageType' ) ] )
for _, damageType in ipairs( damageTypes ) do
table.insert( section,
infobox:renderItem( {
label = translate( 'LBL_Damage' .. damageType ),
tooltip = translate( 'SMW_Damage' .. damageType ),
data = damage[ translate( 'SMW_Damage' .. damageType ) ]
} )
)
end
damagesTabberData[ 'content' .. damagesTabCount ] = infobox:renderSection( { content = section, col = 3 },
true )
damagesTabCount = damagesTabCount + 1
end


return infobox:renderSection( {
--- Set the frame and load args
title = translate( 'LBL_Damages' ),
--- @param frame table
class = 'infobox__section--tabber',
function methodtable.setFrame( self, frame )
content = tabber( damagesTabberData ),
self.currentFrame = frame
border = false
self.frameArgs = require( 'Module:Arguments' ).getArgs( frame )
}, true )
end
end
end


-- Bomb
if smwData[ translate( 'SMW_Type' ) ] == 'Bomb' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_DamagePhysical' ), smwData[ translate( 'SMW_DamagePhysical' ) ] ),
infobox:renderItem( translate( 'LBL_DamageEnergy' ), smwData[ translate( 'SMW_DamageEnergy' ) ] ),
infobox:renderItem( translate( 'LBL_ExplosionRadius' ),
infobox.addUnitIfExists(
infobox.formatRange( smwData[ translate( 'SMW_MinimumExplosionRadius' ) ],
smwData[ translate( 'SMW_MaximumExplosionRadius' ) ], true ), 'm' ) ),
infobox:renderItem( translate( 'LBL_ArmTime' ), smwData[ translate( 'SMW_ArmTime' ) ] ),
infobox:renderItem( translate( 'LBL_IgniteTime' ), smwData[ translate( 'SMW_IgniteTime' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- Cooler
elseif smwData[ translate( 'SMW_Type' ) ] == 'Cooler' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_CoolingRate' ), smwData[ translate( 'SMW_CoolingRate' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- EMP Generator
elseif smwData[ translate( 'SMW_Type' ) ] == 'EMP' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_EMPRadius' ), smwData[ translate( 'SMW_EMPRadius' ) ] ),
infobox:renderItem( translate( 'LBL_ChargeTime' ), smwData[ translate( 'SMW_ChargeTime' ) ] ),
infobox:renderItem( translate( 'LBL_CooldownTime' ), smwData[ translate( 'SMW_CooldownTime' ) ] ),
infobox:renderItem( translate( 'LBL_Duration' ), smwData[ translate( 'SMW_Duration' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- Fuel Pod
elseif smwData[ translate( 'SMW_Type' ) ] == 'ExternalFuelTank' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_FuelCapacity' ), smwData[ translate( 'SMW_FuelCapacity' ) ] ),
infobox:renderItem( translate( 'LBL_FuelFillRate' ), smwData[ translate( 'SMW_FuelFillRate' ) ] ),
infobox:renderItem( translate( 'LBL_FuelDrainRate' ), smwData[ translate( 'SMW_FuelDrainRate' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- Gun / Rocket Pod
elseif smwData[ translate( 'SMW_Type' ) ] == 'WeaponGun.Gun' or smwData[ translate( 'SMW_Type' ) ] == 'WeaponGun.Rocket' then
local function getFiringModesSection()
local modes = smwCommon.loadSubobjects(
itemPageIdentifier,
'SMW_FiringMode',
{
'SMW_FiringMode',
'SMW_FiringRate',
'SMW_AmmoPerShot',
'SMW_ProjectilePerShot',
'SMW_DamagePerSecond'
},
translate
)


if type( modes ) == 'table' then
--- Sets the main categories for this object
local modeTabberData = {}
function methodtable.setCategories( self )
local modeCount = 1
if config.set_categories == false then
return
end


for _, mode in ipairs( modes ) do
local function addSubcategory( s1, s2 )
modeTabberData[ 'label' .. modeCount ] = translate( mode[ translate( 'SMW_FiringMode' ) ] )
table.insert( self.categories, mw.ustring.format( '%s (%s)', s1, s2 ) )
section = {
end
infobox:renderItem( translate( 'LBL_DamagePerSecond' ),
mode[ translate( 'SMW_DamagePerSecond' ) ] ),
infobox:renderItem( translate( 'LBL_FiringRate' ), mode[ translate( 'SMW_FiringRate' ) ] ),
infobox:renderItem( translate( 'LBL_ProjectilePerShot' ),
mode[ translate( 'SMW_ProjectilePerShot' ) ] ),
infobox:renderItem( translate( 'LBL_AmmoPerShot' ), mode[ translate( 'SMW_AmmoPerShot' ) ] )
}
modeTabberData[ 'content' .. modeCount ] = infobox:renderSection( { content = section, col = 2 },
true )
modeCount = modeCount + 1
end


return infobox:renderSection( {
--- Only set category if category_type value exists
title = translate( 'LBL_Modes' ),
if self.smwData[ translate( 'SMW_Type' ) ] ~= nil then
class = 'infobox__section--tabber',
local typeCategory = translate( 'category_' .. mw.ustring.lower( self.smwData[ translate( 'SMW_Type' ) ] ) )
content = tabber( modeTabberData ),
border = false
}, true )
end
end


-- Overview
if typeCategory ~= nil and typeCategory ~= 'category_' .. mw.ustring.lower( self.smwData[ translate( 'SMW_Type' ) ] ) then
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
table.insert( self.categories, typeCategory )
section = {
infobox:renderItem( translate( 'LBL_Damage' ), smwData[ translate( 'SMW_Damage' ) ] ),
infobox:renderItem( translate( 'LBL_AmmoSpeed' ), smwData[ translate( 'SMW_AmmoSpeed' ) ] ),
infobox:renderItem( translate( 'LBL_MaximumRange' ), smwData[ translate( 'SMW_MaximumRange' ) ] ),
infobox:renderItem( translate( 'LBL_Ammo' ), smwData[ translate( 'SMW_Ammo' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true ) ..
getDamagesSection() .. getFiringModesSection()
-- Missile
elseif smwData[ translate( 'SMW_Type' ) ] == 'Missile.Missile' or smwData[ translate( 'SMW_Type' ) ] == 'Missile.Torpedo' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_DamagePhysical' ), smwData[ translate( 'SMW_DamagePhysical' ) ] ),
infobox:renderItem( translate( 'LBL_DamageEnergy' ), smwData[ translate( 'SMW_DamageEnergy' ) ] ),
infobox:renderItem( translate( 'LBL_SignalType' ), smwData[ translate( 'SMW_SignalType' ) ] ),
infobox:renderItem( translate( 'LBL_LockTime' ), smwData[ translate( 'SMW_LockTime' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- Missile launcher / Weapon mount
-- FIXME: Maybe refactor the type check to a local function?
elseif smwData[ translate( 'SMW_Type' ) ] == 'MissileLauncher.MissileRack' or smwData[ translate( 'SMW_Type' ) ] == 'Turret.GunTurret' or smwData[ translate( 'SMW_Type' ) ] == 'Turret.BallTurret' or smwData[ translate( 'SMW_Type' ) ] == 'Turret.CanardTurret' or smwData[ translate( 'SMW_Type' ) ] == 'Turret.NoseMounted' then
--- NOTE: Should we just set the size SMW property to type:quantity, then prefix the S as a unit?
local function getPortSize()
if smwData[ translate( 'SMW_PortSize' ) ] == nil then return end
return 'S' .. smwData[ translate( 'SMW_PortSize' ) ]
end


-- Overview
if self.smwData[ translate( 'SMW_Size' ) ] ~= nil then
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
addSubcategory( typeCategory, translate( 'SMW_Size' ) .. ' ' .. self.smwData[ translate( 'SMW_Size' ) ] )
section = {
end
infobox:renderItem( translate( 'LBL_PortCount' ), smwData[ translate( 'SMW_PortCount' ) ] ),
infobox:renderItem( translate( 'LBL_PortSize' ), getPortSize() )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- Mining Laser
elseif smwData[ translate( 'SMW_Type' ) ] == 'WeaponMining.Gun' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_MiningLaserPower' ), smwData[ translate( 'SMW_MiningLaserPower' ) ] ),
infobox:renderItem( translate( 'LBL_ExtractionLaserPower' ),
smwData[ translate( 'SMW_ExtractionLaserPower' ) ] ),
infobox:renderItem( translate( 'LBL_OptimalRange' ), smwData[ translate( 'SMW_OptimalRange' ) ] ),
infobox:renderItem( translate( 'LBL_MaximumRange' ), smwData[ translate( 'SMW_MaximumRange' ) ] ),
infobox:renderItem( translate( 'LBL_ExtractionThroughput' ),
smwData[ translate( 'SMW_ExtractionThroughput' ) ] ),
infobox:renderItem( translate( 'LBL_ModuleSlots' ), smwData[ translate( 'SMW_ModuleSlots' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )


tabberData[ 'label2' ] = translate( 'LBL_Modifiers' )
if self.smwData[ translate( 'SMW_Grade' ) ] ~= nil then
section = {
addSubcategory( typeCategory, translate( 'SMW_Grade' ) .. ' ' .. self.smwData[ translate( 'SMW_Grade' ) ] )
infobox:renderItem( translate( 'LBL_ModifierCatastrophicChargeRate' ),
end
smwData[ translate( 'SMW_ModifierCatastrophicChargeRate' ) ] ),
--infobox:renderItem( translate( 'LBL_ModifierExtractionLaserPower' ), smwData[ translate( 'SMW_ModifierExtractionLaserPower' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierLaserInstability' ),
smwData[ translate( 'SMW_ModifierLaserInstability' ) ] ),
--infobox:renderItem( translate( 'LBL_ModifierMiningLaserPower' ), smwData[ translate( 'SMW_ModifierMiningLaserPower' ) ] ),
--infobox:renderItem( translate( 'LBL_ModifierOptimalChargeWindowSize' ), smwData[ translate( 'SMW_ModifierOptimalChargeWindowSize' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierInertMaterials' ),
smwData[ translate( 'SMW_ModifierInertMaterials' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierOptimalChargeRate' ),
smwData[ translate( 'SMW_ModifierOptimalChargeRate' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierResistance' ), smwData[ translate( 'SMW_ModifierResistance' ) ] ),
--infobox:renderItem( translate( 'LBL_ModifierShatterDamage' ), smwData[ translate( 'SMW_ModifierShatterDamage' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierSize' ), smwData[ translate( 'SMW_ModifierSize' ) ] )
}
tabberData[ 'content2' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- Mining Module
elseif smwData[ translate( 'SMW_Type' ) ] == 'MiningModifier.Gun' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_Uses' ), smwData[ translate( 'SMW_Uses' ) ] ),
infobox:renderItem( translate( 'LBL_Duration' ), smwData[ translate( 'SMW_Duration' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierCatastrophicChargeRate' ),
smwData[ translate( 'SMW_ModifierCatastrophicChargeRate' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierExtractionLaserPower' ),
smwData[ translate( 'SMW_ModifierExtractionLaserPower' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierLaserInstability' ),
smwData[ translate( 'SMW_ModifierLaserInstability' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierMiningLaserPower' ),
smwData[ translate( 'SMW_ModifierMiningLaserPower' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierOptimalChargeWindowSize' ),
smwData[ translate( 'SMW_ModifierOptimalChargeWindowSize' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierInertMaterials' ),
smwData[ translate( 'SMW_ModifierInertMaterials' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierOptimalChargeRate' ),
smwData[ translate( 'SMW_ModifierOptimalChargeRate' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierResistance' ), smwData[ translate( 'SMW_ModifierResistance' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierShatterDamage' ),
smwData[ translate( 'SMW_ModifierShatterDamage' ) ] ),
infobox:renderItem( translate( 'LBL_ModifierSize' ), smwData[ translate( 'SMW_ModifierSize' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- Power Plant
elseif smwData[ translate( 'SMW_Type' ) ] == 'PowerPlant.Power' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_PowerOutput' ), smwData[ translate( 'SMW_PowerOutput' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- Quantum Drive
elseif smwData[ translate( 'SMW_Type' ) ] == 'QuantumDrive' then
local function getQuantumDriveModesSection()
local modes = smwCommon.loadSubobjects(
itemPageIdentifier,
'SMW_QuantumTravelType',
{
'SMW_QuantumTravelType',
'SMW_QuantumTravelSpeed',
'SMW_CooldownTime',
'SMW_ChargeTime'
},
translate
)


if type( modes ) == 'table' then
if self.smwData[ translate( 'SMW_Subtype' ) ] ~= nil then
local modeTabberData = {}
addSubcategory( typeCategory, self.smwData[ translate( 'SMW_Subtype' ) ] )
local modeCount = 1
end


for _, mode in ipairs( modes ) do
if self.smwData[ translate( 'SMW_Class' ) ] ~= nil then
modeTabberData[ 'label' .. modeCount ] = translate( mode[ translate( 'SMW_QuantumTravelType' ) ] )
addSubcategory( typeCategory, self.smwData[ translate( 'SMW_Class' ) ] )
section = {
end
infobox:renderItem( translate( 'LBL_QuantumTravelSpeed' ),
end
mode[ translate( 'SMW_QuantumTravelSpeed' ) ] ),
end
infobox:renderItem( translate( 'LBL_CooldownTime' ), mode[ translate( 'SMW_CooldownTime' ) ] ),
infobox:renderItem( translate( 'LBL_ChargeTime' ), mode[ translate( 'SMW_ChargeTime' ) ] )
}
modeTabberData[ 'content' .. modeCount ] = infobox:renderSection( { content = section, col = 3 },
true )
modeCount = modeCount + 1
end


return infobox:renderSection( {
if self.smwData[ translate( 'SMW_Manufacturer' ) ] ~= nil then
title = translate( 'LBL_Modes' ),
local manufacturer = mw.ustring.gsub( self.smwData[ translate( 'SMW_Manufacturer' ) ], '%[+', '' )
class = 'infobox__section--tabber',
manufacturer = mw.ustring.gsub( manufacturer, '%]+', '' )
content = tabber( modeTabberData ),
border = false
}, true )
end
end


-- Overview
table.insert( self.categories, manufacturer )
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
else
section = {
table.insert( self.categories, translate( 'error_category_item_missing_manufacturer' ) )
infobox:renderItem( translate( 'LBL_QuantumFuelRequirement' ),
end
smwData[ translate( 'SMW_QuantumFuelRequirement' ) ] ),
infobox:renderItem( translate( 'LBL_QuantumTravelDisconnectRange' ),
smwData[ translate( 'SMW_QuantumTravelDisconnectRange' ) ] )
-- Does range matter currently? The range seems to be limited by the QF fuel tank of the vehicle anyways
--infobox:renderItem( translate( 'LBL_QuantumTravelRange' ), smwData[ translate( 'SMW_QuantumTravelRange' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true ) ..
getQuantumDriveModesSection()
-- Quantum Enforcement Device
elseif smwData[ translate( 'SMW_Type' ) ] == 'QuantumInterdictionGenerator' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_JammerRange' ), smwData[ translate( 'SMW_JammerRange' ) ] ),
infobox:renderItem( translate( 'LBL_InterdictionRange' ), smwData[ translate( 'SMW_InterdictionRange' ) ] ),
infobox:renderItem( translate( 'LBL_Duration' ), smwData[ translate( 'SMW_Duration' ) ] ),
infobox:renderItem( translate( 'LBL_ChargeTime' ), smwData[ translate( 'SMW_ChargeTime' ) ] ),
infobox:renderItem( translate( 'LBL_CooldownTime' ), smwData[ translate( 'SMW_CooldownTime' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
-- Scraper Module
elseif smwData[ translate( 'SMW_Type' ) ] == 'SalvageModifier' or smwData[ translate( 'SMW_Type' ) ] == 'SalvageModifier.SalvageModifier_TractorBeam' then
-- Modifier
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_ModifierSalvageSpeed' ),
infobox.addUnitIfExists( smwData[ translate( 'SMW_ModifierSalvageSpeed' ) ], 'x' ) ),
infobox:renderItem( translate( 'LBL_ModifierRadius' ),
infobox.addUnitIfExists( smwData[ translate( 'SMW_ModifierRadius' ) ], 'x' ) ),
infobox:renderItem( translate( 'LBL_ModifierExtractionEfficiency' ),
infobox.addUnitIfExists( smwData[ translate( 'SMW_ModifierExtractionEfficiency' ) ], 'x' ) )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 3 }, true )
-- Shield
elseif smwData[ translate( 'SMW_Type' ) ] == 'Shield' then
-- We need raw number from SMW to calculate shield regen, so we add the unit back
local function getShieldPoint()
if smwData[ translate( 'SMW_ShieldHealthPoint' ) ] == nil then return end
return common.formatNum( math.ceil( smwData[ translate( 'SMW_ShieldHealthPoint' ) ] ) ) .. ' 🛡️'
end


local function getShieldRegen()
if self.smwData[ translate( 'SMW_UUID' ) ] == nil then
if smwData[ translate( 'SMW_ShieldPointRegeneration' ) ] == nil then return end
table.insert( self.categories, translate( 'error_category_item_missing_uuid' ) )
if smwData[ translate( 'SMW_ShieldHealthPoint' ) ] == nil then
end
return smwData
[ translate( 'SMW_ShieldPointRegeneration' ) ]
end


local fullChargeTime = math.ceil( smwData[ translate( 'SMW_ShieldHealthPoint' ) ] /
runModuleFN( self.smwData[ translate( 'SMW_Type' ) ], 'addCategories', { self.categories, self.frameArgs, self.smwData } )
smwData[ translate( 'SMW_ShieldPointRegeneration' ) ] )
end


return infobox.showDescIfDiff(
common.formatNum( math.ceil( smwData[ translate( 'SMW_ShieldPointRegeneration' ) ] ) ) .. ' 🛡️/s',
translate( 'unit_secondtillfull', false, fullChargeTime )
)
end


local function getShieldRegenDelay()
--- Sets the short description for this object
if smwData[ translate( 'SMW_ShieldDownTime' ) ] == nil or smwData[ translate( 'SMW_ShieldDamageDelay' ) ] == nil then return end
function methodtable.setShortDescription( self )
return infobox.showDescIfDiff(
local shortdesc = ''
local itemType = translate( 'type_item' )
smwData[ translate( 'SMW_ShieldDamageDelay' ) ],
translate( 'unit_whendown', false, smwData[ translate( 'SMW_ShieldDownTime' ) ] )
)
end


-- Overview
if self.smwData[ translate( 'SMW_Type' ) ] ~= nil then
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
if self.smwData[ translate( 'SMW_Subtype' ) ] ~= nil then
section = {
-- TODO: Localize subtype
infobox:renderItem( translate( 'LBL_ShieldHealthPoint' ), getShieldPoint() ),
itemType = self.smwData[ translate( 'SMW_Subtype' ) ]
infobox:renderItem( translate( 'LBL_ShieldPointRegeneration' ), getShieldRegen() ),
else
infobox:renderItem( translate( 'LBL_ShieldRegenDelay' ), getShieldRegenDelay() )
local itemTypeKey = 'type_' .. mw.ustring.lower( self.smwData[ translate( 'SMW_Type' ) ] )
}
if translate( itemTypeKey ) ~= nil and translate( itemTypeKey ) ~= itemTypeKey then
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
itemType = translate( itemTypeKey )
end
end
itemType = mw.ustring.lower( itemType )
end


-- TODO: Add on API
shortdesc = itemType
--infobox:renderSection( {
-- title = translate( 'LBL_Resistances' ),
-- col = 3,
-- content = {
-- infobox:renderItem( translate( 'LBL_ShieldPhysicalResistance' ), smwData[ translate( 'SMW_ShieldPhysicalResistance' ) ] ),
-- infobox:renderItem( translate( 'LBL_ShieldEnergyResistance' ), smwData[ translate( 'SMW_ShieldEnergyResistance' ) ] ),
-- infobox:renderItem( translate( 'LBL_ShieldDistortionResistance' ), smwData[ translate( 'SMW_ShieldDistortionResistance' ) ] ),
--
-- infobox:renderItem( translate( 'LBL_ShieldThermalResistance' ), smwData[ translate( 'SMW_ShieldThermalResistance' ) ] ),
-- infobox:renderItem( translate( 'LBL_ShieldBiochemicalResistance' ), smwData[ translate( 'SMW_ShieldBiochemicalResistance' ) ] ),
-- infobox:renderItem( translate( 'LBL_ShieldStunResistance' ), smwData[ translate( 'SMW_ShieldStunResistance' ) ] ),
-- }
--} )
-- Tractor beam
-- TODO: Maybe we should use SMW_Type for all the stuff above
elseif smwData[ translate( 'SMW_Type' ) ] == 'TractorBeam' or smwData[ translate( 'SMW_Type' ) ] == 'TowingBeam' then
-- Overview
tabberData[ 'label1' ] = translate( 'LBL_Overview' )
section = {
infobox:renderItem( translate( 'LBL_MaximumForce' ), smwData[ translate( 'SMW_MaximumForce' ) ] ),
infobox:renderItem( translate( 'LBL_OptimalRange' ), smwData[ translate( 'SMW_OptimalRange' ) ] ),
infobox:renderItem( translate( 'LBL_MaximumRange' ), smwData[ translate( 'SMW_MaximumRange' ) ] ),
infobox:renderItem( translate( 'LBL_MaximumAngle' ), smwData[ translate( 'SMW_MaximumAngle' ) ] ),
infobox:renderItem( translate( 'LBL_MaximumVolume' ), smwData[ translate( 'SMW_MaximumVolume' ) ] )
}
tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
end


-- Get the index of the last tab
if self.smwData[ translate( 'SMW_Class' ) ] ~= nil then
local tabCount = 0
shortdesc = mw.ustring.format( '%s %s',
for _, __ in pairs( tabberData ) do
string.lower( self.smwData[ translate( 'SMW_Class' ) ] ),
tabCount = tabCount + 1
shortdesc
end
)
tabCount = tabCount / 2
end


-- Engineering
if self.smwData[ translate( 'SMW_Grade' ) ] ~= nil then
-- TODO: Make temperatures into a graph?
shortdesc = translate( 'shortdesc_grade', false, self.smwData[ translate( 'SMW_Grade' ) ], shortdesc )
-- FIXME: Instead of hardcoding the unit, can we use SMW query to get the unit?
end
tabCount = tabCount + 1
tabberData[ 'label' .. tabCount ] = translate( 'LBL_Engineering' )
section = {
infobox:renderItem( translate( 'LBL_PowerDraw' ),
infobox.addUnitIfExists(
infobox.formatRange( smwData[ translate( 'SMW_MinimumPowerDraw' ) ],
smwData[ translate( 'SMW_MaximumPowerDraw' ) ], true ), '🔌/s' ) ),
infobox:renderItem( translate( 'LBL_ThermalEnergyOutput' ),
infobox.addUnitIfExists(
infobox.formatRange( smwData[ translate( 'SMW_MinimumThermalEnergyOutput' ) ],
smwData[ translate( 'SMW_MaximumThermalEnergyOutput' ) ], true ), '🌡️/s' ) ),
infobox:renderItem( translate( 'LBL_MaximumCoolingRate' ), smwData[ translate( 'SMW_MaximumCoolingRate' ) ] ),
infobox:renderItem( translate( 'LBL_StartCoolingTemperature' ),
smwData[ translate( 'SMW_StartCoolingTemperature' ) ] ),
infobox:renderItem( translate( 'LBL_Temperature' ),
infobox.addUnitIfExists(
infobox.formatRange( smwData[ translate( 'SMW_MinimumTemperature' ) ],
smwData[ translate( 'SMW_MaximumTemperature' ) ], true ), '°C' ) ),
infobox:renderItem( translate( 'LBL_MisfireTemperature' ),
infobox.addUnitIfExists(
infobox.formatRange( smwData[ translate( 'SMW_MinimumMisfireTemperature' ) ],
smwData[ translate( 'SMW_MaximumMisfireTemperature' ) ], true ), '°C' ) ),
infobox:renderItem( translate( 'LBL_OverheatTemperature' ), smwData[ translate( 'SMW_OverheatTemperature' ) ] )
}
tabberData[ 'content' .. tabCount ] = infobox:renderSection( { content = section, col = 2 }, true )


-- Emission
if self.smwData[ translate( 'SMW_Size' ) ] ~= nil then
local function getMaxIR()
shortdesc = mw.ustring.format( 'S%d %s',
if
self.smwData[ translate( 'SMW_Size' ) ],
(type( smwData[ translate( 'SMW_IRTemperatureThreshold' ) ] ) ~= 'number' or type( smwData[ translate( 'SMW_TemperatureToIR' ) ] ~= 'number' ))
shortdesc
and type( smwData[ translate( 'SMW_MinimumIR' ) ] ) ~= 'number'
)
then
end
return
end


return smwData[ translate( 'SMW_IRTemperatureThreshold' ) ] * smwData[ translate( 'SMW_TemperatureToIR' ) ] +
--- Manufacturer
if self.smwData[ translate( 'SMW_Manufacturer' ) ] ~= nil then
smwData[ translate( 'SMW_MinimumIR' ) ]
end
local mfuname = self.smwData[ translate( 'SMW_Manufacturer' ) ]
local man = manufacturer( mfuname )
--- Use short name if possible
if man ~= nil and man.shortname ~= nil then mfuname = man.shortname end

shortdesc = translate( 'shortdesc_manufactured_by', false, shortdesc, mfuname )
end

--- Submodule override
shortdesc = runModuleFN(
self.smwData[ translate( 'SMW_Type' ) ],
'getShortDescription',
{ self.frameArgs, self.smwData },
true
) or shortdesc

if type( shortdesc ) == 'string' and shortdesc ~= '' then
shortdesc = lang:ucfirst( shortdesc )
self.currentFrame:callParserFunction( 'SHORTDESC', shortdesc )
end
end


--- Save Api Data to SMW store
function methodtable.saveApiData( self )
self:getApiDataForCurrentPage()
self:setSemanticProperties()
end


--- Generates debug output
function methodtable.makeDebugOutput( self )
local debug = require( 'Module:Common/Debug' )

self.smwData = nil
local smwData = self:getSmwData()

local queryName = self.frameArgs[ translate( 'ARG_SmwQueryName' ) ] or
self.frameArgs[ translate( 'ARG_Name' ) ] or
mw.title.getCurrentTitle().fullText

return debug.collapsedDebugSections({
{
title = 'SMW Query',
content = debug.convertSmwQueryObject( makeSmwQueryObject( queryName ) ),
},
{
title = 'SMW Data',
content = smwData,
tag = 'pre',
},
{
title = 'Frame Args',
content = self.frameArgs,
tag = 'pre',
},
})
end


--- Get the wikitext valid categories for this item
function methodtable.getCategories( self )
local mapped = {}

for _, category in pairs( self.categories ) do
if mw.ustring.sub( category, 1, 2 ) ~= '[[' then
category = mw.ustring.format( '[[Category:%s]]', category )
end

table.insert( mapped, category )
end

return table.concat( mapped )
end


tabCount = tabCount + 1
tabberData[ 'label' .. tabCount ] = translate( 'LBL_Emission' )
section = {
infobox:renderItem( translate( 'LBL_EM' ),
infobox.formatRange( smwData[ translate( 'SMW_MinimumEM' ) ], smwData[ translate( 'SMW_MaximumEM' ) ], true ) ),
infobox:renderItem( translate( 'LBL_PowerToEM' ), smwData[ translate( 'SMW_PowerToEM' ) ] ),
infobox:renderItem( translate( 'LBL_EMDecayRate' ), smwData[ translate( 'SMW_EMDecayRate' ) ] ),
infobox:renderItem( translate( 'LBL_IR' ),
infobox.formatRange( smwData[ translate( 'SMW_MinimumIR' ) ], getMaxIR(), true ) ),
infobox:renderItem( translate( 'LBL_TemperatureToIR' ), smwData[ translate( 'SMW_TemperatureToIR' ) ] )
}
tabberData[ 'content' .. tabCount ] = infobox:renderSection( { content = section, col = 3 }, true )


-- Defense
--- New Instance
tabCount = tabCount + 1
function Item.new( self )
tabberData[ 'label' .. tabCount ] = translate( 'LBL_Defense' )
local instance = {
categories = {}
section = {
infobox:renderItem( translate( 'LBL_Health' ), smwData[ translate( 'SMW_HealthPoint' ) ] ),
infobox:renderItem( translate( 'LBL_DistortionHealthPoint' ), smwData
[ translate( 'SMW_DistortionHealthPoint' ) ] ),
infobox:renderItem( translate( 'LBL_DistortionDecayRate' ), smwData[ translate( 'SMW_DistortionDecayRate' ) ] ),
infobox:renderItem( translate( 'LBL_DistortionDecayDelay' ), smwData[ translate( 'SMW_DistortionDecayDelay' ) ] )
}
}
tabberData[ 'content' .. tabCount ] = infobox:renderSection( { content = section, col = 2 }, true )


-- Dimensions
setmetatable( instance, metatable )
--tabberData[ 'label' .. tabCount ] = translate( 'LBL_Dimensions' )
--section = {
-- infobox:renderItem( {
-- label = translate( 'LBL_Length' ),
-- data = smwData[ translate( 'SMW_EntityLength' ) ],
-- } ),
-- infobox:renderItem( {
-- label = translate( 'LBL_Width' ),
-- data = smwData[ translate( 'SMW_EntityWidth' ) ],
-- } ),
-- infobox:renderItem( {
-- label = translate( 'LBL_Height' ),
-- data = smwData[ translate( 'SMW_EntityHeight' ) ],
-- } ),
-- infobox:renderItem( {
-- label = translate( 'LBL_Mass' ),
-- data = smwData[ translate( 'SMW_Mass' ) ],
-- } )
--}
--tabberData[ 'content' .. tabCount ] = infobox:renderSection( { content = section, col = 3 }, true )


infobox:renderSection( {
return instance
class = 'infobox__section--tabber',
content = tabber( tabberData ),
border = false
} )
end
end


--- Add categories that are set on the page.

--- The categories table should only contain category names, no MW Links, i.e. 'Foo' instead of '[[Category:Foo]]'
--- Load data from api.star-citizen.wiki and save it to SMW
---
---
--- @param frame table Invocation frame
--- @param categories table The categories table
--- @param frameArgs table Frame arguments from Module:Arguments
--- @return string|nil
--- @param smwData table Data from Semantic MediaWiki
function Item.loadApiData( frame )
--- @return nil
local instance = Item:new()
function p.addCategories( categories, frameArgs, smwData )
instance:setFrame( frame )
instance:saveApiData()


local debugOutput
if instance.frameArgs[ 'debug' ] ~= nil then
local debug = require( 'Module:Common/Debug' )

debugOutput = debug.collapsedDebugSections({
{
title = 'SMW Set Data',
content = mw.getCurrentFrame():callParserFunction( '#tag', { 'nowiki', mw.dumpObject( instance.setData or {} ) } ),
},
})
end

return debugOutput
end
end


--- Set the short description for this object

--- Generates an infobox based on passed frame args and SMW data
---
---
--- @param frame table Invocation frame
--- @param shortdesc string Short description
--- @param frameArgs table Frame arguments from Module:Arguments
--- @return string
--- @param smwData table Data from Semantic MediaWiki
function Item.infobox( frame )
--- @return nil
local instance = Item:new()
function p.getShortDescription( shortdesc, frameArgs, smwData )
instance:setFrame( frame )


local debugOutput = ''
if instance.frameArgs[ 'debug' ] ~= nil then
debugOutput = instance:makeDebugOutput()
end

return tostring( instance:getInfobox() ) .. debugOutput
end
end


return p

--- Generates a description based on passed frame args and SMW data
---
--- @param frame table Invocation frame
--- @return string
function Item.description( frame )
local instance = Item:new()
instance:setFrame( frame )
return tostring( instance:getDescription() )
end


--- "Main" entry point for templates that saves the API Data and outputs the infobox
---
--- @param frame table Invocation frame
--- @return string
function Item.main( frame )
local instance = Item:new()
instance:setFrame( frame )
instance:saveApiData()

local debugOutput = ''
local interwikiLinks = ''

if instance.frameArgs[ 'debug' ] ~= nil then
debugOutput = instance:makeDebugOutput()
end

local infobox = tostring( instance:getInfobox() )

if instance.smwData ~= nil then
instance:setCategories()
instance:setShortDescription()
interwikiLinks = common.generateInterWikiLinks( mw.title.getCurrentTitle().rootText )
end

return infobox .. debugOutput .. instance:getCategories() .. interwikiLinks
end


---
function Item.test( page )
page = page or 'Cirrus'

local instance = Item:new()
instance.frameArgs = {}
instance.frameArgs[ translate( 'ARG_Name' ) ] = page

instance:saveApiData()
end


return Item

Revision as of 22:44, 6 April 2024

Module documentation[view][edit][history][purge]
This documentation is transcluded from Module:Item/doc. Changes can be proposed in the talk page.
Function list
L 21 — mergeTables
L 43 — translate
L 51 — p.addSmwProperties
L 128 — p.addSmwAskProperties
L 142 — p.addInfoboxData
L 147 — getDamagesSection
L 246 — getFiringModesSection
L 313 — getPortSize
L 396 — getQuantumDriveModesSection
L 475 — getShieldPoint
L 480 — getShieldRegen
L 496 — getShieldRegenDelay
L 579 — getMaxIR
L 652 — p.addCategories
L 662 — p.getShortDescription

Submodules:


require( 'strict' )

local p = {}

local MODULE_NAME = 'VehicleItem'

local TNT = require( 'Module:Translate' ):new()
local smwCommon = require( 'Module:Common/SMW' )
local data = mw.loadJsonData( 'Module:Item/' .. MODULE_NAME .. '/data.json' )
local config = mw.loadJsonData( 'Module:Item/config.json' )

local common = require( 'Module:Common' )


--- Merge two tables where they share the same key-value pairs
--- TODO: Move to Common
---
---@param t1 table
---@param t2 table
---@return table
local function mergeTables( t1, t2 )
    -- Returns the union of the values of two tables, as a sequence.
    local vals = {}
    for k, v in pairs( t1 ) do
        vals[ v ] = true
    end
    for k, v in pairs( t2 ) do
        vals[ v ] = true
    end
    local ret = {}
    for k, v in pairs( vals ) do
        table.insert( ret, k )
    end
    return ret
end


--- Wrapper function for Module:Translate.translate
---
--- @param key string The translation key
--- @param addSuffix boolean|nil Adds a language suffix if config.smw_multilingual_text is true
--- @return string If the key was not found in the .tab page, the key is returned
local function translate( key, addSuffix, ... )
    return TNT:translate( 'Module:Item/' .. MODULE_NAME .. '/i18n.json', config, key, addSuffix, { ... } )
end


--- Adds the properties valid for this item to the SMW Set object
---
--- @param smwSetObject table
function p.addSmwProperties( apiData, frameArgs, smwSetObject )
    smwCommon.addSmwProperties(
        apiData,
        frameArgs,
        smwSetObject,
        translate,
        config,
        data,
        'Item/' .. MODULE_NAME
    )

    local setData = {}

    local formatConfig = {
        type = 'number'
    }

    -- TODO: Modifiers and Damages are generic enough that maybe we should search for it by default on Module:Item?
    smwCommon.setFromTable( setData, apiData:get( 'mining_laser.modifiers' ), 'display_name', 'value', 'Modifier',
        translate, formatConfig )
    smwCommon.setFromTable( setData, apiData:get( 'mining_module.modifiers' ), 'display_name', 'value', 'Modifier',
        translate, formatConfig )
    smwCommon.setFromTable( setData, apiData:get( 'bomb.damages' ), 'name', 'damage', 'Damage', translate, formatConfig )
    smwCommon.setFromTable( setData, apiData:get( 'missile.damages' ), 'name', 'damage', 'Damage', translate,
        formatConfig )

    -- TODO: Implement this for bombs and missiles
    if apiData.vehicle_weapon then
        -- Save damages as subobjects, we did not do it through data.json because we need to build the key
        -- for the damage SMW properties such as SMW_DamageEnergy
        -- TODO: This is generic enough that we should consider making it shared for bombs, missile, personal weapon, and vehicle weapons
        if apiData.vehicle_weapon.damages then
            local ucfirst = require( 'Module:String2' ).ucfirst
            local damages = apiData.vehicle_weapon.damages

            local typeKey = translate( 'SMW_DamageType' )

            local subobjects = {}
            for _, damage in ipairs( damages ) do
                -- FIXME: Wikipedia modules like Module:String2 does not have a proper Lua entry point
                -- Perhaps we should look into it some day
                local ucfirstArgs = { args = { damage.name } }
                local dmgKey = translate( 'SMW_Damage' .. ucfirst( ucfirstArgs ) )

                -- If there are no existing tables, set one first
                if not next( subobjects ) then
                    table.insert( subobjects, {
                        [ typeKey ] = damage.type,
                        [ dmgKey ] = damage.damage
                    } )
                    -- If damage type is the same, add the damage value of the under the same table
                elseif subobjects[ #subobjects ] and subobjects[ #subobjects ][ dmgKey ] and subobjects[ #subobjects ][ dmgKey ] == damage.type then
                    subobjects[ #subobjects ][ dmgKey ] = damage.damage
                else
                    table.insert( subobjects, {
                        [ typeKey ] = damage.type,
                        [ dmgKey ] = damage.damage
                    } )
                end
            end

            mw.logObject( damages, 'Dmg subobjects' )
            mw.logObject( subobjects, 'Dmg subobjects' )

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

    mw.smw.set( setData )
end

--- Adds all SMW parameters set by this Module to the ASK object
---
--- @param smwAskObject table
--- @return nil
function p.addSmwAskProperties( smwAskObject )
    smwCommon.addSmwAskProperties(
        smwAskObject,
        translate,
        config,
        data
    )
end

--- Adds entries to the infobox
---
--- @param infobox table The Module:InfoboxNeue instance
--- @param smwData table Data from Semantic MediaWiki
--- @return nil
function p.addInfoboxData( infobox, smwData, itemPageIdentifier )
    local tabber = require( 'Module:Tabber' ).renderTabber
    local tabberData = {}
    local section

    local function getDamagesSection()
        local damageTypes = {
            'Physical',
            'Energy',
            'Distortion',
            'Thermal',
            'Biochemical',
            'Stun'
        }

        local smwProps = { 'SMW_DamageType' }
        for _, damageType in ipairs( damageTypes ) do
            table.insert( smwProps, 'SMW_Damage' .. damageType )
        end

        local subobjects = smwCommon.loadSubobjects(
            itemPageIdentifier,
            'SMW_DamageType',
            smwProps,
            translate
        )

        if type( subobjects ) == 'table' then
            local damagesTabberData = {}
            local damagesTabCount = 1

            for _, damage in ipairs( subobjects ) do
                section = {}
                damagesTabberData[ 'label' .. damagesTabCount ] = translate( 'damagetype_' ..
                    damage[ translate( 'SMW_DamageType' ) ] )
                for _, damageType in ipairs( damageTypes ) do
                    table.insert( section,
                        infobox:renderItem( {
                            label = translate( 'LBL_Damage' .. damageType ),
                            tooltip = translate( 'SMW_Damage' .. damageType ),
                            data = damage[ translate( 'SMW_Damage' .. damageType ) ]
                        } )
                    )
                end
                damagesTabberData[ 'content' .. damagesTabCount ] = infobox:renderSection( { content = section, col = 3 },
                    true )
                damagesTabCount = damagesTabCount + 1
            end

            return infobox:renderSection( {
                title = translate( 'LBL_Damages' ),
                class = 'infobox__section--tabber',
                content = tabber( damagesTabberData ),
                border = false
            }, true )
        end
    end

    -- Bomb
    if smwData[ translate( 'SMW_Type' ) ] == 'Bomb' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_DamagePhysical' ), smwData[ translate( 'SMW_DamagePhysical' ) ] ),
            infobox:renderItem( translate( 'LBL_DamageEnergy' ), smwData[ translate( 'SMW_DamageEnergy' ) ] ),
            infobox:renderItem( translate( 'LBL_ExplosionRadius' ),
                infobox.addUnitIfExists(
                    infobox.formatRange( smwData[ translate( 'SMW_MinimumExplosionRadius' ) ],
                        smwData[ translate( 'SMW_MaximumExplosionRadius' ) ], true ), 'm' ) ),
            infobox:renderItem( translate( 'LBL_ArmTime' ), smwData[ translate( 'SMW_ArmTime' ) ] ),
            infobox:renderItem( translate( 'LBL_IgniteTime' ), smwData[ translate( 'SMW_IgniteTime' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- Cooler
    elseif smwData[ translate( 'SMW_Type' ) ] == 'Cooler' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_CoolingRate' ), smwData[ translate( 'SMW_CoolingRate' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- EMP Generator
    elseif smwData[ translate( 'SMW_Type' ) ] == 'EMP' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_EMPRadius' ), smwData[ translate( 'SMW_EMPRadius' ) ] ),
            infobox:renderItem( translate( 'LBL_ChargeTime' ), smwData[ translate( 'SMW_ChargeTime' ) ] ),
            infobox:renderItem( translate( 'LBL_CooldownTime' ), smwData[ translate( 'SMW_CooldownTime' ) ] ),
            infobox:renderItem( translate( 'LBL_Duration' ), smwData[ translate( 'SMW_Duration' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- Fuel Pod
    elseif smwData[ translate( 'SMW_Type' ) ] == 'ExternalFuelTank' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_FuelCapacity' ), smwData[ translate( 'SMW_FuelCapacity' ) ] ),
            infobox:renderItem( translate( 'LBL_FuelFillRate' ), smwData[ translate( 'SMW_FuelFillRate' ) ] ),
            infobox:renderItem( translate( 'LBL_FuelDrainRate' ), smwData[ translate( 'SMW_FuelDrainRate' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- Gun / Rocket Pod
    elseif smwData[ translate( 'SMW_Type' ) ] == 'WeaponGun.Gun' or smwData[ translate( 'SMW_Type' ) ] == 'WeaponGun.Rocket' then
        local function getFiringModesSection()
            local modes = smwCommon.loadSubobjects(
                itemPageIdentifier,
                'SMW_FiringMode',
                {
                    'SMW_FiringMode',
                    'SMW_FiringRate',
                    'SMW_AmmoPerShot',
                    'SMW_ProjectilePerShot',
                    'SMW_DamagePerSecond'
                },
                translate
            )

            if type( modes ) == 'table' then
                local modeTabberData = {}
                local modeCount = 1

                for _, mode in ipairs( modes ) do
                    modeTabberData[ 'label' .. modeCount ] = translate( mode[ translate( 'SMW_FiringMode' ) ] )
                    section = {
                        infobox:renderItem( translate( 'LBL_DamagePerSecond' ),
                            mode[ translate( 'SMW_DamagePerSecond' ) ] ),
                        infobox:renderItem( translate( 'LBL_FiringRate' ), mode[ translate( 'SMW_FiringRate' ) ] ),
                        infobox:renderItem( translate( 'LBL_ProjectilePerShot' ),
                            mode[ translate( 'SMW_ProjectilePerShot' ) ] ),
                        infobox:renderItem( translate( 'LBL_AmmoPerShot' ), mode[ translate( 'SMW_AmmoPerShot' ) ] )
                    }
                    modeTabberData[ 'content' .. modeCount ] = infobox:renderSection( { content = section, col = 2 },
                        true )
                    modeCount = modeCount + 1
                end

                return infobox:renderSection( {
                    title = translate( 'LBL_Modes' ),
                    class = 'infobox__section--tabber',
                    content = tabber( modeTabberData ),
                    border = false
                }, true )
            end
        end

        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_Damage' ), smwData[ translate( 'SMW_Damage' ) ] ),
            infobox:renderItem( translate( 'LBL_AmmoSpeed' ), smwData[ translate( 'SMW_AmmoSpeed' ) ] ),
            infobox:renderItem( translate( 'LBL_MaximumRange' ), smwData[ translate( 'SMW_MaximumRange' ) ] ),
            infobox:renderItem( translate( 'LBL_Ammo' ), smwData[ translate( 'SMW_Ammo' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true ) ..
            getDamagesSection() .. getFiringModesSection()
        -- Missile
    elseif smwData[ translate( 'SMW_Type' ) ] == 'Missile.Missile' or smwData[ translate( 'SMW_Type' ) ] == 'Missile.Torpedo' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_DamagePhysical' ), smwData[ translate( 'SMW_DamagePhysical' ) ] ),
            infobox:renderItem( translate( 'LBL_DamageEnergy' ), smwData[ translate( 'SMW_DamageEnergy' ) ] ),
            infobox:renderItem( translate( 'LBL_SignalType' ), smwData[ translate( 'SMW_SignalType' ) ] ),
            infobox:renderItem( translate( 'LBL_LockTime' ), smwData[ translate( 'SMW_LockTime' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- Missile launcher / Weapon mount
        -- FIXME: Maybe refactor the type check to a local function?
    elseif smwData[ translate( 'SMW_Type' ) ] == 'MissileLauncher.MissileRack' or smwData[ translate( 'SMW_Type' ) ] == 'Turret.GunTurret' or smwData[ translate( 'SMW_Type' ) ] == 'Turret.BallTurret' or smwData[ translate( 'SMW_Type' ) ] == 'Turret.CanardTurret' or smwData[ translate( 'SMW_Type' ) ] == 'Turret.NoseMounted' then
        --- NOTE: Should we just set the size SMW property to type:quantity, then prefix the S as a unit?
        local function getPortSize()
            if smwData[ translate( 'SMW_PortSize' ) ] == nil then return end
            return 'S' .. smwData[ translate( 'SMW_PortSize' ) ]
        end

        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_PortCount' ), smwData[ translate( 'SMW_PortCount' ) ] ),
            infobox:renderItem( translate( 'LBL_PortSize' ), getPortSize() )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- Mining Laser
    elseif smwData[ translate( 'SMW_Type' ) ] == 'WeaponMining.Gun' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_MiningLaserPower' ), smwData[ translate( 'SMW_MiningLaserPower' ) ] ),
            infobox:renderItem( translate( 'LBL_ExtractionLaserPower' ),
                smwData[ translate( 'SMW_ExtractionLaserPower' ) ] ),
            infobox:renderItem( translate( 'LBL_OptimalRange' ), smwData[ translate( 'SMW_OptimalRange' ) ] ),
            infobox:renderItem( translate( 'LBL_MaximumRange' ), smwData[ translate( 'SMW_MaximumRange' ) ] ),
            infobox:renderItem( translate( 'LBL_ExtractionThroughput' ),
                smwData[ translate( 'SMW_ExtractionThroughput' ) ] ),
            infobox:renderItem( translate( 'LBL_ModuleSlots' ), smwData[ translate( 'SMW_ModuleSlots' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )

        tabberData[ 'label2' ] = translate( 'LBL_Modifiers' )
        section = {
            infobox:renderItem( translate( 'LBL_ModifierCatastrophicChargeRate' ),
                smwData[ translate( 'SMW_ModifierCatastrophicChargeRate' ) ] ),
            --infobox:renderItem( translate( 'LBL_ModifierExtractionLaserPower' ), smwData[ translate( 'SMW_ModifierExtractionLaserPower' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierLaserInstability' ),
                smwData[ translate( 'SMW_ModifierLaserInstability' ) ] ),
            --infobox:renderItem( translate( 'LBL_ModifierMiningLaserPower' ), smwData[ translate( 'SMW_ModifierMiningLaserPower' ) ] ),
            --infobox:renderItem( translate( 'LBL_ModifierOptimalChargeWindowSize' ), smwData[ translate( 'SMW_ModifierOptimalChargeWindowSize' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierInertMaterials' ),
                smwData[ translate( 'SMW_ModifierInertMaterials' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierOptimalChargeRate' ),
                smwData[ translate( 'SMW_ModifierOptimalChargeRate' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierResistance' ), smwData[ translate( 'SMW_ModifierResistance' ) ] ),
            --infobox:renderItem( translate( 'LBL_ModifierShatterDamage' ), smwData[ translate( 'SMW_ModifierShatterDamage' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierSize' ), smwData[ translate( 'SMW_ModifierSize' ) ] )
        }
        tabberData[ 'content2' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- Mining Module
    elseif smwData[ translate( 'SMW_Type' ) ] == 'MiningModifier.Gun' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_Uses' ), smwData[ translate( 'SMW_Uses' ) ] ),
            infobox:renderItem( translate( 'LBL_Duration' ), smwData[ translate( 'SMW_Duration' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierCatastrophicChargeRate' ),
                smwData[ translate( 'SMW_ModifierCatastrophicChargeRate' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierExtractionLaserPower' ),
                smwData[ translate( 'SMW_ModifierExtractionLaserPower' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierLaserInstability' ),
                smwData[ translate( 'SMW_ModifierLaserInstability' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierMiningLaserPower' ),
                smwData[ translate( 'SMW_ModifierMiningLaserPower' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierOptimalChargeWindowSize' ),
                smwData[ translate( 'SMW_ModifierOptimalChargeWindowSize' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierInertMaterials' ),
                smwData[ translate( 'SMW_ModifierInertMaterials' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierOptimalChargeRate' ),
                smwData[ translate( 'SMW_ModifierOptimalChargeRate' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierResistance' ), smwData[ translate( 'SMW_ModifierResistance' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierShatterDamage' ),
                smwData[ translate( 'SMW_ModifierShatterDamage' ) ] ),
            infobox:renderItem( translate( 'LBL_ModifierSize' ), smwData[ translate( 'SMW_ModifierSize' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- Power Plant
    elseif smwData[ translate( 'SMW_Type' ) ] == 'PowerPlant.Power' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_PowerOutput' ), smwData[ translate( 'SMW_PowerOutput' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- Quantum Drive
    elseif smwData[ translate( 'SMW_Type' ) ] == 'QuantumDrive' then
        local function getQuantumDriveModesSection()
            local modes = smwCommon.loadSubobjects(
                itemPageIdentifier,
                'SMW_QuantumTravelType',
                {
                    'SMW_QuantumTravelType',
                    'SMW_QuantumTravelSpeed',
                    'SMW_CooldownTime',
                    'SMW_ChargeTime'
                },
                translate
            )

            if type( modes ) == 'table' then
                local modeTabberData = {}
                local modeCount = 1

                for _, mode in ipairs( modes ) do
                    modeTabberData[ 'label' .. modeCount ] = translate( mode[ translate( 'SMW_QuantumTravelType' ) ] )
                    section = {
                        infobox:renderItem( translate( 'LBL_QuantumTravelSpeed' ),
                            mode[ translate( 'SMW_QuantumTravelSpeed' ) ] ),
                        infobox:renderItem( translate( 'LBL_CooldownTime' ), mode[ translate( 'SMW_CooldownTime' ) ] ),
                        infobox:renderItem( translate( 'LBL_ChargeTime' ), mode[ translate( 'SMW_ChargeTime' ) ] )
                    }
                    modeTabberData[ 'content' .. modeCount ] = infobox:renderSection( { content = section, col = 3 },
                        true )
                    modeCount = modeCount + 1
                end

                return infobox:renderSection( {
                    title = translate( 'LBL_Modes' ),
                    class = 'infobox__section--tabber',
                    content = tabber( modeTabberData ),
                    border = false
                }, true )
            end
        end

        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_QuantumFuelRequirement' ),
                smwData[ translate( 'SMW_QuantumFuelRequirement' ) ] ),
            infobox:renderItem( translate( 'LBL_QuantumTravelDisconnectRange' ),
                smwData[ translate( 'SMW_QuantumTravelDisconnectRange' ) ] )
            -- Does range matter currently? The range seems to be limited by the QF fuel tank of the vehicle anyways
            --infobox:renderItem( translate( 'LBL_QuantumTravelRange' ), smwData[ translate( 'SMW_QuantumTravelRange' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true ) ..
            getQuantumDriveModesSection()
        -- Quantum Enforcement Device
    elseif smwData[ translate( 'SMW_Type' ) ] == 'QuantumInterdictionGenerator' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_JammerRange' ), smwData[ translate( 'SMW_JammerRange' ) ] ),
            infobox:renderItem( translate( 'LBL_InterdictionRange' ), smwData[ translate( 'SMW_InterdictionRange' ) ] ),
            infobox:renderItem( translate( 'LBL_Duration' ), smwData[ translate( 'SMW_Duration' ) ] ),
            infobox:renderItem( translate( 'LBL_ChargeTime' ), smwData[ translate( 'SMW_ChargeTime' ) ] ),
            infobox:renderItem( translate( 'LBL_CooldownTime' ), smwData[ translate( 'SMW_CooldownTime' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
        -- Scraper Module
    elseif smwData[ translate( 'SMW_Type' ) ] == 'SalvageModifier' or smwData[ translate( 'SMW_Type' ) ] == 'SalvageModifier.SalvageModifier_TractorBeam' then
        -- Modifier
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_ModifierSalvageSpeed' ),
                infobox.addUnitIfExists( smwData[ translate( 'SMW_ModifierSalvageSpeed' ) ], 'x' ) ),
            infobox:renderItem( translate( 'LBL_ModifierRadius' ),
                infobox.addUnitIfExists( smwData[ translate( 'SMW_ModifierRadius' ) ], 'x' ) ),
            infobox:renderItem( translate( 'LBL_ModifierExtractionEfficiency' ),
                infobox.addUnitIfExists( smwData[ translate( 'SMW_ModifierExtractionEfficiency' ) ], 'x' ) )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 3 }, true )
        -- Shield
    elseif smwData[ translate( 'SMW_Type' ) ] == 'Shield' then
        -- We need raw number from SMW to calculate shield regen, so we add the unit back
        local function getShieldPoint()
            if smwData[ translate( 'SMW_ShieldHealthPoint' ) ] == nil then return end
            return common.formatNum( math.ceil( smwData[ translate( 'SMW_ShieldHealthPoint' ) ] ) ) .. ' 🛡️'
        end

        local function getShieldRegen()
            if smwData[ translate( 'SMW_ShieldPointRegeneration' ) ] == nil then return end
            if smwData[ translate( 'SMW_ShieldHealthPoint' ) ] == nil then
                return smwData
                    [ translate( 'SMW_ShieldPointRegeneration' ) ]
            end

            local fullChargeTime = math.ceil( smwData[ translate( 'SMW_ShieldHealthPoint' ) ] /
                smwData[ translate( 'SMW_ShieldPointRegeneration' ) ] )

            return infobox.showDescIfDiff(
                common.formatNum( math.ceil( smwData[ translate( 'SMW_ShieldPointRegeneration' ) ] ) ) .. ' 🛡️/s',
                translate( 'unit_secondtillfull', false, fullChargeTime )
            )
        end

        local function getShieldRegenDelay()
            if smwData[ translate( 'SMW_ShieldDownTime' ) ] == nil or smwData[ translate( 'SMW_ShieldDamageDelay' ) ] == nil then return end
            return infobox.showDescIfDiff(
                smwData[ translate( 'SMW_ShieldDamageDelay' ) ],
                translate( 'unit_whendown', false, smwData[ translate( 'SMW_ShieldDownTime' ) ] )
            )
        end

        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_ShieldHealthPoint' ), getShieldPoint() ),
            infobox:renderItem( translate( 'LBL_ShieldPointRegeneration' ), getShieldRegen() ),
            infobox:renderItem( translate( 'LBL_ShieldRegenDelay' ), getShieldRegenDelay() )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )

        -- TODO: Add on API
        --infobox:renderSection( {
        --    title = translate( 'LBL_Resistances' ),
        --    col = 3,
        --    content = {
        --        infobox:renderItem( translate( 'LBL_ShieldPhysicalResistance' ), smwData[ translate( 'SMW_ShieldPhysicalResistance' ) ] ),
        --        infobox:renderItem( translate( 'LBL_ShieldEnergyResistance' ), smwData[ translate( 'SMW_ShieldEnergyResistance' ) ] ),
        --        infobox:renderItem( translate( 'LBL_ShieldDistortionResistance' ), smwData[ translate( 'SMW_ShieldDistortionResistance' ) ] ),
        --
        --        infobox:renderItem( translate( 'LBL_ShieldThermalResistance' ), smwData[ translate( 'SMW_ShieldThermalResistance' ) ] ),
        --        infobox:renderItem( translate( 'LBL_ShieldBiochemicalResistance' ), smwData[ translate( 'SMW_ShieldBiochemicalResistance' ) ] ),
        --        infobox:renderItem( translate( 'LBL_ShieldStunResistance' ), smwData[ translate( 'SMW_ShieldStunResistance' ) ] ),
        --    }
        --} )
        -- Tractor beam
        -- TODO: Maybe we should use SMW_Type for all the stuff above
    elseif smwData[ translate( 'SMW_Type' ) ] == 'TractorBeam' or smwData[ translate( 'SMW_Type' ) ] == 'TowingBeam' then
        -- Overview
        tabberData[ 'label1' ] = translate( 'LBL_Overview' )
        section = {
            infobox:renderItem( translate( 'LBL_MaximumForce' ), smwData[ translate( 'SMW_MaximumForce' ) ] ),
            infobox:renderItem( translate( 'LBL_OptimalRange' ), smwData[ translate( 'SMW_OptimalRange' ) ] ),
            infobox:renderItem( translate( 'LBL_MaximumRange' ), smwData[ translate( 'SMW_MaximumRange' ) ] ),
            infobox:renderItem( translate( 'LBL_MaximumAngle' ), smwData[ translate( 'SMW_MaximumAngle' ) ] ),
            infobox:renderItem( translate( 'LBL_MaximumVolume' ), smwData[ translate( 'SMW_MaximumVolume' ) ] )
        }
        tabberData[ 'content1' ] = infobox:renderSection( { content = section, col = 2 }, true )
    end

    -- Get the index of the last tab
    local tabCount = 0
    for _, __ in pairs( tabberData ) do
        tabCount = tabCount + 1
    end
    tabCount = tabCount / 2

    -- Engineering
    -- TODO: Make temperatures into a graph?
    -- FIXME: Instead of hardcoding the unit, can we use SMW query to get the unit?
    tabCount = tabCount + 1
    tabberData[ 'label' .. tabCount ] = translate( 'LBL_Engineering' )
    section = {
        infobox:renderItem( translate( 'LBL_PowerDraw' ),
            infobox.addUnitIfExists(
                infobox.formatRange( smwData[ translate( 'SMW_MinimumPowerDraw' ) ],
                    smwData[ translate( 'SMW_MaximumPowerDraw' ) ], true ), '🔌/s' ) ),
        infobox:renderItem( translate( 'LBL_ThermalEnergyOutput' ),
            infobox.addUnitIfExists(
                infobox.formatRange( smwData[ translate( 'SMW_MinimumThermalEnergyOutput' ) ],
                    smwData[ translate( 'SMW_MaximumThermalEnergyOutput' ) ], true ), '🌡️/s' ) ),
        infobox:renderItem( translate( 'LBL_MaximumCoolingRate' ), smwData[ translate( 'SMW_MaximumCoolingRate' ) ] ),
        infobox:renderItem( translate( 'LBL_StartCoolingTemperature' ),
            smwData[ translate( 'SMW_StartCoolingTemperature' ) ] ),
        infobox:renderItem( translate( 'LBL_Temperature' ),
            infobox.addUnitIfExists(
                infobox.formatRange( smwData[ translate( 'SMW_MinimumTemperature' ) ],
                    smwData[ translate( 'SMW_MaximumTemperature' ) ], true ), '°C' ) ),
        infobox:renderItem( translate( 'LBL_MisfireTemperature' ),
            infobox.addUnitIfExists(
                infobox.formatRange( smwData[ translate( 'SMW_MinimumMisfireTemperature' ) ],
                    smwData[ translate( 'SMW_MaximumMisfireTemperature' ) ], true ), '°C' ) ),
        infobox:renderItem( translate( 'LBL_OverheatTemperature' ), smwData[ translate( 'SMW_OverheatTemperature' ) ] )
    }
    tabberData[ 'content' .. tabCount ] = infobox:renderSection( { content = section, col = 2 }, true )

    -- Emission
    local function getMaxIR()
        if
            (type( smwData[ translate( 'SMW_IRTemperatureThreshold' ) ] ) ~= 'number' or type( smwData[ translate( 'SMW_TemperatureToIR' ) ] ~= 'number' ))
            and type( smwData[ translate( 'SMW_MinimumIR' ) ] ) ~= 'number'
        then
            return
        end

        return smwData[ translate( 'SMW_IRTemperatureThreshold' ) ] * smwData[ translate( 'SMW_TemperatureToIR' ) ] +
            smwData[ translate( 'SMW_MinimumIR' ) ]
    end

    tabCount = tabCount + 1
    tabberData[ 'label' .. tabCount ] = translate( 'LBL_Emission' )
    section = {
        infobox:renderItem( translate( 'LBL_EM' ),
            infobox.formatRange( smwData[ translate( 'SMW_MinimumEM' ) ], smwData[ translate( 'SMW_MaximumEM' ) ], true ) ),
        infobox:renderItem( translate( 'LBL_PowerToEM' ), smwData[ translate( 'SMW_PowerToEM' ) ] ),
        infobox:renderItem( translate( 'LBL_EMDecayRate' ), smwData[ translate( 'SMW_EMDecayRate' ) ] ),
        infobox:renderItem( translate( 'LBL_IR' ),
            infobox.formatRange( smwData[ translate( 'SMW_MinimumIR' ) ], getMaxIR(), true ) ),
        infobox:renderItem( translate( 'LBL_TemperatureToIR' ), smwData[ translate( 'SMW_TemperatureToIR' ) ] )
    }
    tabberData[ 'content' .. tabCount ] = infobox:renderSection( { content = section, col = 3 }, true )

    -- Defense
    tabCount = tabCount + 1
    tabberData[ 'label' .. tabCount ] = translate( 'LBL_Defense' )
    section = {
        infobox:renderItem( translate( 'LBL_Health' ), smwData[ translate( 'SMW_HealthPoint' ) ] ),
        infobox:renderItem( translate( 'LBL_DistortionHealthPoint' ), smwData
            [ translate( 'SMW_DistortionHealthPoint' ) ] ),
        infobox:renderItem( translate( 'LBL_DistortionDecayRate' ), smwData[ translate( 'SMW_DistortionDecayRate' ) ] ),
        infobox:renderItem( translate( 'LBL_DistortionDecayDelay' ), smwData[ translate( 'SMW_DistortionDecayDelay' ) ] )
    }
    tabberData[ 'content' .. tabCount ] = infobox:renderSection( { content = section, col = 2 }, true )

    -- Dimensions
    --tabberData[ 'label' .. tabCount ] = translate( 'LBL_Dimensions' )
    --section = {
    --    infobox:renderItem( {
    --        label = translate( 'LBL_Length' ),
    --        data = smwData[ translate( 'SMW_EntityLength' ) ],
    --    } ),
    --    infobox:renderItem( {
    --        label = translate( 'LBL_Width' ),
    --        data = smwData[ translate( 'SMW_EntityWidth' ) ],
    --    } ),
    --    infobox:renderItem( {
    --        label = translate( 'LBL_Height' ),
    --        data = smwData[ translate( 'SMW_EntityHeight' ) ],
    --    } ),
    --    infobox:renderItem( {
    --        label = translate( 'LBL_Mass' ),
    --        data = smwData[ translate( 'SMW_Mass' ) ],
    --    } )
    --}
    --tabberData[ 'content' .. tabCount ] = infobox:renderSection( { content = section, col = 3 }, true )

    infobox:renderSection( {
        class = 'infobox__section--tabber',
        content = tabber( tabberData ),
        border = false
    } )
end

--- Add categories that are set on the page.
--- The categories table should only contain category names, no MW Links, i.e. 'Foo' instead of '[[Category:Foo]]'
---
--- @param categories table The categories table
--- @param frameArgs table Frame arguments from Module:Arguments
--- @param smwData table Data from Semantic MediaWiki
--- @return nil
function p.addCategories( categories, frameArgs, smwData )

end

--- Set the short description for this object
---
--- @param shortdesc string Short description
--- @param frameArgs table Frame arguments from Module:Arguments
--- @param smwData table Data from Semantic MediaWiki
--- @return nil
function p.getShortDescription( shortdesc, frameArgs, smwData )

end

return p