Module:LaserWeaponLister
From Terra Invicta Official Wiki
Documentation for this module may be created at Module:LaserWeaponLister/doc
---
---Reads the Laser Weapon data files and presents them in readable formats, with additional calculated statistics.
---
---@module LaserWeaponLister
local LaserWeaponLister = {}
--region Dependencies
local IconModule = require('Module:Icon')
local LaserWeaponData = mw.loadData('Module:CSVReader/LaserWeapon')
local LaserWeaponNames = mw.loadData('Module:ENReader/LaserWeapon')
local ProjectNameBuilder = require('Module:ProjectLister/NameBuilder')
--endregion
--region Private constants
local mountNames = {
OneHull = "One Hull Slot",
TwoHullHoriz = "Two Hull Slots",
FourHull = "Four Hull Slots",
HalfNose = "Half Nose Slot",
OneNose = "One Nose Slot",
TwoNoseVert = "Two Nose Slots",
ThreeNoseAngle = "Three Nose Slots",
FourNose = "Four Nose Slots"
}
local nonShipMountNames = {
T1BaseDefense = "Point Defense Array",
T2BaseDefense = "Layered Defense Array",
T3BaseDefense = "Battlestation",
RegionDefense = "[[Space_Defenses|Region]]"
}
local floor = math.floor
local ceil = math.ceil
local round = function(num, decimals)
local mult = 10^(decimals or 0)
return floor(num * mult + 0.5) / mult
end
--endregion
--region Private methods
local function calculateFireRate(salvo, cooldown, scooldown)
if not salvo or tonumber(salvo) == 1 then
return round(60 / tonumber(cooldown),1)
else
local fullCooldown = tonumber(cooldown) + (tonumber(salvo) - 1) * tonumber(scooldown)
return round(math.max(1,tonumber(salvo)) * 60 / fullCooldown,1)
end
end
local function calculateResourceCost(mass, resourceWeight, crew, crewResource)
local baseCost = tonumber(mass) * (tonumber(resourceWeight) or 0) / 10
local crewCost = tonumber(crew) * (tonumber(crewResource) or 0)
return baseCost + crewCost
end
local function calculatePowerPerShot(shotpower, eff)
return tonumber(shotpower) / 1000 / tonumber(eff)
end
local function calculatePowerPerMinute(fireRate, powerPerShot)
return fireRate * powerPerShot
end
local function calculateArmorEffectiveness(wavelength, beam, jitter, mirror, range)
local mirrorDiameter = tonumber(mirror) * 0.02
local spotDiameterPreciseFactor = 1000 * ((1.22 * tonumber(wavelength) / 1000000000 * tonumber(beam))^2 + (2 * tonumber(jitter) * mirrorDiameter)^2) ^ 0.5 / mirrorDiameter
local spotDiameterPrecise = range * spotDiameterPreciseFactor
local spotAreaPrecise = 0.7853982 * spotDiameterPrecise ^ 2
return spotAreaPrecise / 0.005
end
local function calculateMinArmor(shotpower, wavelength, beam, jitter, mirror, range)
local maxDamage = tonumber(shotpower) * 1.2 / 20
local armorEffectiveness = calculateArmorEffectiveness(wavelength, beam, jitter, mirror, range)
return ceil((maxDamage / armorEffectiveness) ^ (1 / 1.5))
end
local function calculateEstimatedDPS(shotpower, fireRate, wavelength, beam, jitter, mirror, range)
local armorEffectiveness = {}
armorEffectiveness[1] = calculateArmorEffectiveness(wavelength, beam, jitter, mirror, range)
armorEffectiveness[2] = calculateArmorEffectiveness(wavelength, beam, jitter, mirror, 200)
armorEffectiveness[3] = calculateArmorEffectiveness(wavelength, beam, jitter, mirror, 500)
armorEffectiveness[4] = calculateArmorEffectiveness(wavelength, beam, jitter, mirror, 800)
local damage = {}
damage[1] = tonumber(shotpower) / 20 / armorEffectiveness[1]
damage[2] = tonumber(shotpower) / 20 / armorEffectiveness[2]
damage[3] = tonumber(shotpower) / 20 / armorEffectiveness[3]
damage[4] = tonumber(shotpower) / 20 / armorEffectiveness[4]
return (damage[1] * 0.3 + damage[2] * 0.15 + damage[3] * 0.25 + damage[4] * 0.3) * fireRate / 60
end
local function calculatePDValues(shotpower, fireRate, wavelength, beam, jitter, mirror, multiplier)
local PDValues = {}
PDValues[1] = calculateEstimatedDPS(shotpower, fireRate, wavelength, beam, jitter, mirror, 200) * multiplier
PDValues[2] = PDValues[1] + calculateEstimatedDPS(shotpower, fireRate, wavelength, beam, jitter, mirror, 400) * multiplier
PDValues[3] = PDValues[1] + (calculateEstimatedDPS(shotpower, fireRate, wavelength, beam, jitter, mirror, 300) + calculateEstimatedDPS(shotpower, fireRate, wavelength, beam, jitter, mirror, 600)) * multiplier
return PDValues
end
-- Safe value access with fallback
local function getValue(row, key, default)
local value = row[key]
if value == nil or value == '' then
return default or 0
end
return value
end
--endregion
--region Public methods
--- Example Usage: {{#invoke:LaserWeaponLister|ListSpecific|detailed=true|alien=true|shipMounted=true}}
function LaserWeaponLister.ListSpecific(frame)
local detailed = frame.args['detailed'] == 'true'
local alien = frame.args['alien'] == 'true'
local shipMounted = frame.args['shipMounted'] == 'true'
return LaserWeaponLister.ListLaserWeapons(detailed, alien, shipMounted)
end
---@param detailed boolean whether to show the detailed or basic version.
---@param alien boolean whether to show alien or human version.
---@param shipMounted boolean whether to show the ship or other version.
---@return wiki table of batteries
function LaserWeaponLister.ListLaserWeapons(detailed, alien, shipMounted)
local returnString = '{| class="wikitable sortable mw-collapsible" style="text-align: center;"'
returnString = returnString .. '\n! rowspan=2| Laser<br>Weapon'
if detailed and not alien and shipMounted then
returnString = returnString .. '\n! rowspan=2| Required<br>Project'
end
if detailed and shipMounted then
returnString = returnString .. '\n! rowspan=2| Hard<br>Points'
returnString = returnString .. '\n! rowspan=2| Can<br>PD'
end
if not shipMounted then
returnString = returnString .. '\n! rowspan=2| Used<br>By'
end
returnString = returnString .. '\n! rowspan=2| Damage'
if shipMounted then
returnString = returnString .. '\n! rowspan=2| Fire Rate<br>(shots/min)'
returnString = returnString .. '\n! rowspan=2| Range<br>(km)'
end
if detailed then
returnString = returnString .. '\n! rowspan=2| Wavelength<br>(nm)'
returnString = returnString .. '\n! rowspan=2| Mirror Radius<br>(cm)'
returnString = returnString .. '\n! rowspan=2| Beam<br>Quality'
returnString = returnString .. '\n! rowspan=2| Jitter<br>Radius'
if shipMounted then
returnString = returnString .. '\n! rowspan=2| Crew'
returnString = returnString .. '\n! rowspan=2| Mass<br>(tons)'
returnString = returnString .. '\n! colspan=5| Build Resources'
returnString = returnString .. '\n! rowspan=2| Efficiency'
returnString = returnString .. '\n! rowspan=2| Power Per Shot<br>(GJ)'
returnString = returnString .. '\n! rowspan=2| Power Per Minute<br>(GJ/min)'
end
end
if shipMounted then
returnString = returnString .. '\n! colspan=4| Minimum Armor to Block at Range'
else
returnString = returnString .. '\n! colspan=3| Minimum Armor to Block at Range'
returnString = returnString .. '\n! colspan=3| PD Value at Range'
end
returnString = returnString .. '\n|-'
if detailed and shipMounted then
returnString = returnString .. '\n!' .. IconModule.Icon({args={'water'}})
returnString = returnString .. '\n!' .. IconModule.Icon({args={'volatiles'}})
returnString = returnString .. '\n!' .. IconModule.Icon({args={'metals'}})
returnString = returnString .. '\n!' .. IconModule.Icon({args={'nobles'}})
returnString = returnString .. '\n!' .. IconModule.Icon({args={'exotics'}})
end
returnString = returnString .. '\n! 200km'
returnString = returnString .. '\n! 400km'
returnString = returnString .. '\n! 600km'
if shipMounted then
returnString = returnString .. '\n! 800km'
else
returnString = returnString .. '\n! 200km'
returnString = returnString .. '\n! 400km'
returnString = returnString .. '\n! 600km'
end
returnString = returnString .. '\n|-'
for rowID, row in pairs(LaserWeaponData) do
local mount = mountNames[getValue(row, 'mount', '')]
local projectID = getValue(row, 'requiredProjectName', '')
if (alien and (projectID == 'Project_AlienMasterProject' or projectID == 'Project_AlienAdvancedMasterProject')) or ((not alien) and projectID ~= 'Project_AlienMasterProject' and projectID ~= 'Project_AlienAdvancedMasterProject') then
if (mount and shipMounted) or (not mount and not shipMounted) then
returnString = returnString .. '\n| ' .. (LaserWeaponNames['TILaserWeaponTemplate.displayName.' .. rowID] or rowID)
if detailed and not alien and shipMounted then
returnString = returnString .. '\n| ' .. ProjectNameBuilder.buildName({args={row['requiredProjectName']}})
end
if detailed and shipMounted then
returnString = returnString .. '\n| ' .. mount
if getValue(row, 'defenseMode', 'false') == 'true' then
returnString = returnString .. '\n| Yes'
else
returnString = returnString .. '\n| No'
end
end
if not shipMounted then
returnString = returnString .. '\n| ' .. nonShipMountNames[getValue(row, 'mount')]
end
local shotpower = getValue(row, 'shotPower_MJ', 0)
returnString = returnString .. '\n| ' .. tostring(tonumber(shotpower) / 20)
local fireRate = calculateFireRate(getValue(row, 'salvo_shots',1), getValue(row, 'cooldown_s'), getValue(row, 'intraSalvoCooldown_s'))
if shipMounted then
returnString = returnString .. '\n| ' .. fireRate
returnString = returnString .. '\n| ' .. getValue(row, 'targetingRange_km', 0)
end
local wavelength = getValue(row, 'wavelength_nm', 0)
local mirror = getValue(row, 'mirrorRadius_cm', 0)
local beam = getValue(row, 'beam_quality', 0)
local jitter = getValue(row, 'jitter_Rad', 0)
if detailed then
returnString = returnString .. '\n| ' .. wavelength
returnString = returnString .. '\n| ' .. mirror
returnString = returnString .. '\n| ' .. beam
returnString = returnString .. '\n| ' .. jitter
if shipMounted then
returnString = returnString .. '\n| ' .. getValue(row, 'crew', 0)
local baseMass = tonumber(getValue(row, 'baseWeaponMass_tons', 0))
local crewCount = tonumber(getValue(row, 'crew', 0))
local massWithCrew = baseMass + crewCount * 4
returnString = returnString .. '\n| ' .. massWithCrew
local waterCost = calculateResourceCost(getValue(row, 'baseWeaponMass_tons'), 0, getValue(row, 'crew'), 0.2)
local volatilesCost = calculateResourceCost(getValue(row, 'baseWeaponMass_tons'), getValue(row, 'weightedBuildMaterials/volatiles'), getValue(row, 'crew'), 0.2)
local metalsCost = calculateResourceCost(getValue(row, 'baseWeaponMass_tons'), getValue(row, 'weightedBuildMaterials/metals'), getValue(row, 'crew'), 0)
local noblesCost = calculateResourceCost(getValue(row, 'baseWeaponMass_tons'), getValue(row, 'weightedBuildMaterials/nobleMetals'), getValue(row, 'crew'), 0)
local exoticsCost = calculateResourceCost(getValue(row, 'baseWeaponMass_tons'), getValue(row, 'weightedBuildMaterials/exotics'), getValue(row, 'crew'), 0)
returnString = returnString .. '\n| ' .. round(waterCost, 3)
returnString = returnString .. '\n| ' .. round(volatilesCost, 3)
returnString = returnString .. '\n| ' .. round(metalsCost, 3)
returnString = returnString .. '\n| ' .. round(noblesCost, 3)
returnString = returnString .. '\n| ' .. round(exoticsCost, 3)
returnString = returnString .. '\n| ' .. getValue(row, 'efficiency', 1)
local powerPerShot = calculatePowerPerShot(getValue(row, 'shotPower_MJ'), getValue(row, 'efficiency'))
returnString = returnString .. '\n| ' .. round(powerPerShot,3)
local powerPerMinute = calculatePowerPerMinute(fireRate, powerPerShot)
returnString = returnString .. '\n| ' .. round(powerPerMinute,3)
end
end
returnString = returnString .. '\n| ' .. calculateMinArmor(shotpower, wavelength, beam, jitter, mirror, 200)
returnString = returnString .. '\n| ' .. calculateMinArmor(shotpower, wavelength, beam, jitter, mirror, 400)
returnString = returnString .. '\n| ' .. calculateMinArmor(shotpower, wavelength, beam, jitter, mirror, 600)
if shipMounted then
returnString = returnString .. '\n| ' .. calculateMinArmor(shotpower, wavelength, beam, jitter, mirror, 800)
else
local multiplier = 30
if mount ~= 'RegionDefense' then
multiplier = 60
end
local PDValues = calculatePDValues(shotpower, fireRate, wavelength, beam, jitter, mirror, multiplier)
returnString = returnString .. '\n| ' .. round(PDValues[1],1)
returnString = returnString .. '\n| ' .. round(PDValues[2],1)
returnString = returnString .. '\n| ' .. round(PDValues[3],1)
end
returnString = returnString .. '\n|-'
end
end
end
returnString = returnString .. '\n|}'
return returnString
end
--endregion
return LaserWeaponLister


