Module:Gear

From BTAWiki
Revision as of 02:25, 17 January 2021 by Rust dev (talk | contribs)
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 = {}

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',
    },
  },
}

core.schema.heatsink = {
  table = 'Heatsinks',
  parent_table = core.schema.gear,
  fields = {
    {
      field = 'DissipationCapcity',
      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 = 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',
    },
  },
}

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
    args.join = join
  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

--
-- 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)
  mw.logObject(items)

  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(simple_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

  return t
end

-- always return p
return p