Module:Gear
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Gear/doc
-- Module:Gear handles the myriad of gear types. -- start with p, the package we expose local p = {} -- core is lua-only methods, not for use in the public intertace local core = {} p.core = core local id_field = { field = 'Id', type = 'String' } -- -- imports -- local getArgs = require('Module:Arguments').getArgs local cargo = mw.ext.cargo -- -- schema -- core.schema = {} core.schema.gear = { table = 'Gear', fields = { id_field, { field = 'Cost', -- type defines the type of the field type = 'Integer', },{ field = 'Rarity', type = 'Integer', },{ field = 'Purchasable', type = 'Boolean', },{ field = 'Manufacturer', type = 'String', },{ field = 'Model', type = 'String', },{ field = 'UIName', type = 'String', },{ field = 'Name', type = 'String', },{ field = 'Details', -- Details use the Text type, which is unindexed and intended for -- longer-form data. type = 'Text', },{ field = 'Icon', type = 'String', },{ field = 'ComponentType', type = 'String', },{ field = 'ComponentSubType', type = 'String', },{ field = 'InventorySize', type = 'Integer', },{ field = 'Tonnage', type = 'Float', },{ field = 'AllowedLocations', type = 'List (,) of String', },{ field = 'DisallowedLocations', type = 'List (,) of String', },{ field = 'BattleValue', type = 'Integer', },{ field = 'Bonuses', type = 'List (,) of String', },{ field = 'CustomCategories', type = 'List (,) of String', }, }, } core.schema.heatsink = { table = 'Heatsinks', parent_table = core.schema.gear, fields = { { field = 'Dissipation', type = 'Integer', },{ field = 'EngineSlot', type = 'Boolean', },{ field = 'EnginePart', type = 'Boolean', }, }, } core.schema.cooling = { table = 'Cooling', parent_table = core.schema.heatsink, fields = { { field = 'HeatsinkDefID', type = 'String', }, }, } core.schema.engineheatblock = { table = 'EngineHeatBlocks', parent_table = core.schema.heatsink, fields = { { field = 'HeatsinkCount', type = 'Integer', }, }, } core.schema.engineshield = { table = 'EngineShields', parent_table = core.schema.heatsink, fields = { { field = 'ReservedSlots', type = 'Integer', },{ field = 'EngineFactor', type = 'Float', }, }, } core.schema.enginecore = { parent_table = core.schema.heatsink, table = 'EngineCore', fields = { { field = 'Rating', type = 'Integer', }, }, } core.schema.upgrade = { table = 'UpgradeDef', parent_table = core.schema.gear, fields = {}, } core.schema.jumpjet = { table = 'JumpJets', parent_table = core.schema.gear, fields = { { field = 'MinTonnage', type = 'Integer', },{ field = 'MaxTonnage', type = 'Integer', },{ field = 'JumpCapcity', type = 'Float', }, }, } core.schema.weapon = { table = 'Weapons', parent_table = core.schema.gear, fields = { { field = 'Category', type = 'String', },{ field = 'Type', type = 'String', },{ field = 'WeaponSubType', type = 'String', },{ field = 'MinRange', type = 'Integer', },{ field = 'MaxRange', type = 'Integer', },{ field = 'RangeSplit', type = 'List (,) of Integer', },{ field = 'AmmoCategory', type = 'String', },{ field = 'StartingAmmoCapacity', type = 'Integer', },{ field = 'HeatGenerated', type = 'Integer', },{ field = 'Damage', type = 'Integer', },{ field = 'OverheatedDamageMultiplier', type = 'Float', },{ field = 'EvasiveDamageMultiplier', type = 'Float', },{ field = 'EvasivePipsIgnored', type = 'Integer', },{ field = 'DamageVariance', type = 'Float', },{ field = 'HeatDamage', type = 'Integer', },{ field = 'AccuracyModifier', type = 'Float', },{ field = 'CriticalChanceMultiplier', type = 'Float', },{ field = 'AOECapable', type = 'Boolean', },{ field = 'IndirectFireCapable', type = 'Boolean', },{ field = 'RefireModifier', type = 'Integer', },{ field = 'ShotsWhenFired', type = 'Integer', },{ field = 'ProjectilesPerShot', type = 'Integer', },{ field = 'AttackRecoil', type = 'Integer', },{ field = 'Instability', type = 'Integer', },{ field = 'WeaponEffectID', type = 'String', },{ field = 'NoMelee', type = 'Boolean', }, }, } core.schema.ammunition = { table = 'Ammunition', parent_table = core.schema.gear, fields = { { field = 'Capacity', type = 'Integer', },{ field = 'Category', type = 'String', }, } } -- -- Cargo -- -- Handles the connection between this module and the Cargo database. core.cargo = {} -- core.cargo.cargo_store stores the data for the current schema's fields, and -- then recursively stores the data of the parent tables' fields. function core.cargo.store(frame, schema, tpl_args) frame:expandTemplate{title=string.format('Template:Gear/cargo/attach/%s', schema.table), args={}} local data = {} data._table = schema.table for _, field in ipairs(schema.fields) do local arg = tpl_args[field.field] if arg ~= nil then data[field.field] = arg end end if schema.parent_table ~= nil then data[id_field.field] = tpl_args[id_field.field] core.cargo.store(frame, schema.parent_table, tpl_args) end frame:callParserFunction('#cargo_store:', data) end -- core.cargo.cargo_declare declares a given table. Additionally, if the table is -- has a parent table, we add an Id field. function core.cargo.declare(schema) return function(frame) local dcl_args = {} dcl_args._table = schema.table for _, field in ipairs(schema.fields) do dcl_args[field.field] = field.type end if schema.parent_table ~= nil then dcl_args[id_field.field] = id_field.type end frame:callParserFunction('#cargo_declare:', dcl_args) end end function core.cargo.tables(schema) local table = schema.table if schema.parent_table ~= nil then return table .. "," .. core.cargo.tables(schema.parent_table) else return table end end function core.cargo.join_parents(schema) -- if the schema has no parent, then we have nothing to join to if schema.parent_table == nil then return nil end -- otherwise, if the schema does have a parent, then join to the parent local join = string.format( "%s.%s=%s.%s", schema.table, id_field.field, schema.parent_table.table, id_field.field ) -- now, call join_parents for the parent table. if the result is not nil, -- then we need to add the parent table's join to its parent. local parent_join = core.cargo.join_parents(schema.parent_table) if parent_join ~= nil then return join .. "," .. parent_join end return join end function core.cargo.query(schema, fields, args) if args == nil then args = {} end local tables = core.cargo.tables(schema) local join = core.cargo.join_parents(schema) if join ~= nil then if args.join ~= nil then args.join = join .. ',' .. args.join else args.join = join end end if args.extratables ~= nil and args.extratables ~= '' then tables = tables .. ',' .. args.extratables end return cargo.query(tables, fields, args) end -- -- Helpers -- function core.format_table(title, tpl_args) local rawTable = mw.html.create('table') rawTable:addClass('wikitable') rawTable:tag('tr'):tag('th'):attr('colspan', '2'):wikitext(title) for param, arg in pairs(tpl_args) do rawTable:tag('tr') :tag('th'):wikitext(param):done() :tag('td'):wikitext(arg) end return rawTable end function core.get_gear(chassisID, mechID) local args = { join = 'Gear.Id=MechInventory.ComponentDefID', where = string.format( 'MechInventory.MechID="%s" OR MechInventory.MechID="%s"', chassisID, mechID ), extratables = 'MechInventory', } local gearData = core.cargo.query(core.schema.gear, 'Gear.Name,MechInventory.MountedLocation,MechInventory.FixedEquipment,Gear.Id,Gear.CustomCategories', args) local gear = {} for _, item in ipairs(gearData) do table.insert(gear, { location = item['MechInventory.MountedLocation'], name = item['Gear.Name'], id = item['Gear.Id'], fixed = item['MechInventory.FixedEquipment'], categories = mw.text.split(item['Gear.CustomCategories'], ','), }) end return gear end -- core.get_engine returns an object representing details about the given -- mech. function core.get_engine(chassisID, mechID) local engine = {} local where = string.format( 'MechInventory.MechID="%s" OR MechInventory.MechID="%s"', chassisID, mechID ) local join = 'Gear.Id=MechInventory.ComponentDefID' local extratables = 'MechInventory' local shieldRow = core.cargo.query(core.schema.engineshield, 'Gear.Name,Gear.Id', { where = where, join = join, extratables = extratables } ) engine.shield = { name = shieldRow[1]['Gear.Name'], id = shieldRow[1]['Gear.Id'], } local coreRow = core.cargo.query( core.schema.enginecore, 'EngineCore.Rating,Gear.Tonnage', { where = where, join = join, extratables = extratables } ) engine.core = tonumber(coreRow[1]['EngineCore.Rating'], 10) engine.tonnage = tonumber(coreRow[1]['Gear.Tonnage'], 10) local ecoolingRow = core.cargo.query( core.schema.engineheatblock, 'EngineHeatBlocks.HeatsinkCount', { where = where, join = join, extratables = extratables } ) engine.ecooling = tonumber(ecoolingRow[1]['EngineHeatBlocks.HeatsinkCount'], 10) local coolingRow = core.cargo.query( core.schema.cooling, 'Cooling.HeatsinkDefID', { where = where, join = join, extratables = extratables } ) engine.cooling = coolingRow[1]['Cooling.HeatsinkDefID'] local heatsinks = core.cargo.query( core.schema.heatsink, 'Heatsinks.Dissipation', { where = string.format('%s="%s"', 'Gear.Id', engine.cooling) } ) local dissipation = tonumber(heatsinks[1]['Heatsinks.Dissipation'], 10) -- engines have internal heat sinks, which don't take up any space. smaller -- engines can mount fewer heat sinks, while larger engines can mount more. -- to compute the total effective heat sinks of the engine heat sinks, we -- divide the engine rating by 25 and take the floor value. -- so, for example, a 240-rated engine has 240/25 = 9.6, 9 heat sinks. -- -- additionally, engines over 250 can mount additional heat sinks on the -- engine which add weight but take up no space. these are e-cooling. -- 300 / 25 = 12 -- -- to calculate engine heat sinks, we divide the engine rating by 25 and take -- the floor of that. then, we take the lesser of that value and 10, as 10 -- is the maximum number of heat sinks that an engine can hold outside of -- ecooling -- -- finally, we multiply the number of engine heatsinks by the dissipation -- per sink engine.heat_sinking = dissipation * (math.min(math.floor(engine.core / 25), 10) + engine.ecooling) return engine end function core.get_heatsinks(chassisID, mechID) local where = string.format( 'MechInventory.MechID="%s" OR MechInventory.MechID="%s"', chassisID, mechID ) local join = 'Gear.Id=MechInventory.ComponentDefID' local fields = { 'Gear.Name', 'Heatsinks.Dissipation', } local extratables = 'MechInventory' local heatsinkRows = core.cargo.query( core.schema.heatsink, table.concat(fields, ','), { join = join, where = where, extratables = extratables } ) local heatsinks = {} for _, heatsink in ipairs(heatsinkRows) do local dissipation = tonumber(heatsink['Heatsinks.Dissipation'], 10) if dissipation > 0 then table.insert(heatsinks, { name = heatsink['Gear.Name'], dissipation = dissipation, }) end end return heatsinks end function core.get_weapons(chassisID, mechID) local weapons = {} local where = string.format( 'MechInventory.MechID="%s" OR MechInventory.MechID="%s"', chassisID, mechID ) local join = 'Gear.Id=MechInventory.ComponentDefID' local fields = { 'Gear.Name', 'Weapons.Category', 'Weapons.Type', 'Weapons.AmmoCategory', 'Weapons.MinRange', 'Weapons.MaxRange', 'Weapons.HeatGenerated', 'Weapons.Damage', 'Weapons.HeatDamage', 'Weapons.Instability', } local extratables = 'MechInventory' local weaponsResult = core.cargo.query( core.schema.weapon, table.concat(fields, ','), {where = where, join = join, extratables = extratables} ) for _, weaponRow in ipairs(weaponsResult) do local weapon = { name = weaponRow['Gear.Name'], category = weaponRow['Weapons.Category'], type = weaponRow['Weapons.Type'], ammo = weaponRow['Weapons.AmmoCategory'], minRange = tonumber(weaponRow['Weapons.MinRange'], 10), maxRange = tonumber(weaponRow['Weapons.MaxRange'], 10), heat = tonumber(weaponRow['Weapons.HeatGenerated'], 10), damage = tonumber(weaponRow['Weapons.Damage'], 10), heatDamage = tonumber(weaponRow['Weapons.HeatDamage'], 10), instability = tonumber(weaponRow['Weapons.Instability'], 10), } table.insert(weapons, weapon) end return weapons end -- -- Templates -- -- these functions define templates. -- p.table_gear = core.cargo.declare(core.schema.gear) p.table_heatsink = core.cargo.declare(core.schema.heatsink) p.table_cooling = core.cargo.declare(core.schema.cooling) p.table_engineheatblock = core.cargo.declare(core.schema.engineheatblock) p.table_engineshield = core.cargo.declare(core.schema.engineshield) p.table_enginecore = core.cargo.declare(core.schema.enginecore) p.table_upgrade = core.cargo.declare(core.schema.upgrade) p.table_jumpjet = core.cargo.declare(core.schema.jumpjet) p.table_weapon = core.cargo.declare(core.schema.weapon) p.table_ammunition = core.cargo.declare(core.schema.ammunition) function p.heatsink(frame) local tpl_args = getArgs(frame, {parentFirst = true}) core.cargo.store(frame, core.schema.heatsink, tpl_args) return core.format_table('Heat Sink', tpl_args) end function p.cooling(frame) local tpl_args = getArgs(frame, {parentFirst = true}) core.cargo.store(frame, core.schema.cooling, tpl_args) return core.format_table('Cooling', tpl_args) end function p.engineheatblock(frame) local tpl_args = getArgs(frame, {parentFirst = true}) core.cargo.store(frame, core.schema.engineheatblock, tpl_args) return core.format_table('Engine Heat Block', tpl_args) end function p.engineshield(frame) local tpl_args = getArgs(frame, {parentFirst = true}) core.cargo.store(frame, core.schema.engineshield, tpl_args) return core.format_table('Engine Shield', tpl_args) end function p.enginecore(frame) local tpl_args = getArgs(frame, {parentFirst = true}) core.cargo.store(frame, core.schema.enginecore, tpl_args) return core.format_table('Engine Core', tpl_args) end function p.upgrade(frame) local tpl_args = getArgs(frame, {parentFirst = true}) core.cargo.store(frame, core.schema.upgrade, tpl_args) return core.format_table('Upgrade', tpl_args) end function p.jumpjet(frame) local tpl_args = getArgs(frame, {parentFirst = true}) core.cargo.store(frame, core.schema.jumpjet, tpl_args) return core.format_table('Jump Jet', tpl_args) end function p.weapon(frame) local tpl_args = getArgs(frame, {parentFirst = true}) core.cargo.store(frame, core.schema.weapon, tpl_args) return core.format_table('Weapon', tpl_args) end function p.ammunition(frame) local tpl_args = getArgs(frame, {parentFirst = true}) core.cargo.store(frame, core.schema.ammunition, tpl_args) return core.format_table('Ammunition', tpl_args) end function p.weapons_list(frame) local tpl_args = getArgs(frame, {parentFirst = true}) local simple_columns = { 'UIName', 'AmmoCategory', 'Tonnage', 'InventorySize', 'Damage', 'HeatDamage', 'Instability', 'ShotsWhenFired', 'ProjectilesPerShot', 'HeatGenerated', 'AttackRecoil', 'EvasivePipsIgnored', 'CriticalChanceMultiplier', } local complex_columns = { 'MinRange', 'RangeSplit', 'MaxRange', } local fields = table.concat(simple_columns, ',') .. ',' .. table.concat(complex_columns) local args = {} if tpl_args['Category'] ~= nil then args.where = string.format('Category="%s"',tpl_args['Category']) end local items = core.cargo.query(core.schema.weapon, fields, args) local t = mw.html.create('table') t:addClass('wikitable') local headrow = t:tag('tr') headrow:tag('th'):attr('colspan', '2') headrow:tag('th'):attr('colspan', '2'):wikitext('Size') headrow:tag('th'):attr('colspan', '3'):wikitext('Damage') headrow:tag('th'):attr('colspan', '4'):wikitext('Per Salvo') headrow:tag('th'):attr('colspan', '3'):wikitext('Modifiers') headrow:tag('th'):attr('colspan', '5'):wikitext('Range') local subhead = t:tag('tr') subhead:tag('th'):wikitext('Name') subhead:tag('th'):wikitext('Ammo') subhead:tag('th'):wikitext('Tonnage') subhead:tag('th'):wikitext('Slots') subhead:tag('th'):wikitext('Normal') subhead:tag('th'):wikitext('Heat') subhead:tag('th'):wikitext('Stab') subhead:tag('th'):wikitext('Shots') subhead:tag('th'):wikitext('Projectiles') subhead:tag('th'):wikitext('Heat') subhead:tag('th'):wikitext('Recoil') subhead:tag('th'):wikitext('Accuracy') subhead:tag('th'):wikitext('Evasion Ignored') subhead:tag('th'):wikitext('Bonus Crit Chance') subhead:tag('th'):wikitext('Min') subhead:tag('th'):wikitext('Short') subhead:tag('th'):wikitext('Medium') subhead:tag('th'):wikitext('Long') for _, item in ipairs(items) do local row = t:tag('tr') for _, column in ipairs(columns) do row:tag('td'):wikitext(item[column]) end row:tag('td'):wikitext(item['MinRange']) local range_brackets = mw.text.split(item['RangeSplit'], ',') row:tag('td'):wikitext(range_brackets[1]) row:tag('td'):wikitext(range_brackets[2]) row:tag('td'):wikitext(range_brackets[3]) row:tag('td'):wikitext(item['MaxRange']) end end -- get returns the fields for the given -- first arg is the type, the table name -- second arg is the id of the object -- any subsequent args are the fields to return function p.get(frame) tpl_args = getArgs(frame, {parentFirst=true}) -- the first named local table = tpl_args[1] local id = tpl_args[2] local fields = table.concat(tpl_args, ',', 3) local target_schema for _, schema in pairs(core.schema) do if schema.table == table then target = schema end end local row = core.cargo.query(schema, fields) end -- always return p return p