Module:ParticleWeaponLister

From Terra Invicta Official Wiki

Documentation for this module may be created at Module:ParticleWeaponLister/doc

---
---Reads the Particle Weapon data files and presents them in readable formats, with additional calculated statistics.
---
---@module ParticleWeaponLister
local ParticleWeaponLister = {}

--region Dependencies
local IconModule = require('Module:Icon')
local ParticleWeaponData = mw.loadData('Module:CSVReader/ParticleWeapon')
local ParticleWeaponNames = mw.loadData('Module:ENReader/ParticleWeapon')
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 floor = math.floor
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

	--{{#ifeq: {{{dispM}}} | Neutral | 
		--{{#expr: 3.1415927 * ({{{emit}}} * 20 / {{{lensR}}})^2 round 4}} 
		--| {{#expr: 3.1415927 * ({{{lensR}}} / 100 * 2 ^ (200 / {{{doublingRange}}}))^2 round 4}} 
	--}}
	
--	if (particleBeamDispersionModel == ParticleBeamDispersionModel.Charged || particleBeamDispersionModel != ParticleBeamDispersionModel.Neutral)
--	{
--		num = this.lensRadius_cm * Mathf.Pow(2f, range_km / this.doublingRange_km) * 0.01f;
--	}
--	else
--	{
--		num = this.emittance_mrad * (range_km * 1000f) * 1E-06f / (this.lensRadius_cm * 0.01f);
--	}
--	return 3.1415927f * num * num;

local function calculateBeamArea(dispM, emit, lensR, doublingRange, range)
	local beamRadius
	if dispM ~= 'Neutral' then
		beamRadius = tonumber(lensR) * 2 ^ (range / tonumber(doublingRange)) * 0.01
	else
		beamRadius = tonumber(emit) * range * 0.1 / tonumber(lensR)
	end
	return beamRadius ^ 2 * 3.1415927
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:ParticleWeaponLister|ListSpecific|detailed=true|alien=true}} 
function ParticleWeaponLister.ListSpecific(frame)
	local detailed = frame.args['detailed'] == 'true'
	local alien = frame.args['alien'] == 'true'
	return ParticleWeaponLister.ListParticleWeapons(detailed, alien)
end

---@param detailed boolean whether to show the detailed or basic version.
---@param alien boolean whether to show alien or human version.
---@return wiki table of batteries
function ParticleWeaponLister.ListParticleWeapons(detailed, alien)
    local returnString = '{| class="wikitable sortable mw-collapsible" style="text-align: center;"'
	returnString = returnString .. '\n! rowspan=2| Particle<br>Weapon'
	if detailed and not alien then
		returnString = returnString .. '\n! rowspan=2| Required<br>Project'
	end
	if detailed then
		returnString = returnString .. '\n! rowspan=2| Hard<br>Points'
		returnString = returnString .. '\n! rowspan=2| Can<br>PD'
	end
    
    returnString = returnString .. '\n! rowspan=2| Damage'
    returnString = returnString .. '\n! colspan=3| Damage Type Multipliers'
    
    returnString = returnString .. '\n! rowspan=2| Estimated Baryon Damage'
    
	returnString = returnString .. '\n! rowspan=2| Fire Rate<br>(shots/min)'
	returnString = returnString .. '\n! rowspan=2| Range<br>(km)'
    
	if detailed then
		returnString = returnString .. '\n! rowspan=2| Dispersion<br>Model'
		returnString = returnString .. '\n! rowspan=2| Lens Radius<br>(cm)'
		returnString = returnString .. '\n! rowspan=2| Emittence'
		returnString = returnString .. '\n! rowspan=2| Doubling<br>Range'
		
		returnString = returnString .. '\n! rowspan=2| Crew'
		returnString = returnString .. '\n! rowspan=2| Mass<br>(tons)'
		returnString = returnString .. '\n! colspan=6| 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
    
    returnString = returnString .. '\n! colspan=4| Beam Area (m²) at Range'
    returnString = returnString .. '\n|-'
    
    returnString = returnString .. '\n! Heat'
    returnString = returnString .. '\n! X-Ray'
    returnString = returnString .. '\n! Baryon'
    if detailed 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'}})
		returnString = returnString .. '\n!' .. IconModule.Icon({args={'antimatter'}})
    end
    returnString = returnString .. '\n! 200km'
    returnString = returnString .. '\n! 400km'
    returnString = returnString .. '\n! 600km'
    returnString = returnString .. '\n! 800km'
    returnString = returnString .. '\n|-'
    
	for rowID, row in pairs(ParticleWeaponData) 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

			returnString = returnString .. '\n| ' .. (ParticleWeaponNames['TIParticleWeaponTemplate.displayName.' .. rowID] or rowID)
			if detailed and not alien then
				returnString = returnString .. '\n| ' .. ProjectNameBuilder.buildName({args={row['requiredProjectName']}})
			end
			if detailed then
				returnString = returnString .. '\n| ' .. mount
				if getValue(row, 'defenseMode', 'false') == 'true' then
					if getValue(row, 'dispersionModel', 0) == 'Neutral' then
						returnString = returnString .. '\n| Yes'
					else
						returnString = returnString .. '\n| Only Missiles'
					end
				else
					returnString = returnString .. '\n| No'
				end
			end
			
			returnString = returnString .. '\n| ' .. tostring(tonumber(getValue(row, 'shotPower_MJ', 0)) / 20)
			returnString = returnString .. '\n| ' .. getValue(row, 'heatFraction', 0)
			returnString = returnString .. '\n| ' .. getValue(row, 'xRayFraction', 0)
			returnString = returnString .. '\n| ' .. getValue(row, 'baryonFraction', 0)
			
			--{{#expr: {{{shotpower}}} / 20 * {{{baryF}}} / 16 * 5 }}
			returnString = returnString .. '\n| ' .. tostring(round(tonumber(getValue(row, 'shotPower_MJ', 0)) * tonumber(getValue(row, 'baryonFraction', 0)) / 64, 4))
	
			
			local fireRate = calculateFireRate(getValue(row, 'salvo_shots',1), getValue(row, 'cooldown_s'), getValue(row, 'intraSalvoCooldown_s'))

			returnString = returnString .. '\n| ' .. fireRate
			local trange = tonumber(getValue(row, 'targetingRange_km', 0))
			returnString = returnString .. '\n| ' .. trange
			
			if detailed then
				returnString = returnString .. '\n| ' .. getValue(row, 'dispersionModel', 0)
				returnString = returnString .. '\n| ' .. getValue(row, 'lensRadius_cm', 0)
				returnString = returnString .. '\n| ' .. getValue(row, 'emittance_mrad', 0)
				returnString = returnString .. '\n| ' .. getValue(row, 'doublingRange_km', 0)

				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'), getValue(row, 'weightedBuildMaterials/water'), 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)
				local antimatterCost = calculateResourceCost(getValue(row, 'baseWeaponMass_tons'), getValue(row, 'weightedBuildMaterials/antimatter'), 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| ' .. antimatterCost
				
				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
			
			returnString = returnString .. '\n| ' 
			if trange >= 200 then
				returnString = returnString .. round(calculateBeamArea(getValue(row, 'dispersionModel'), getValue(row, 'emittance_mrad', 0), getValue(row, 'lensRadius_cm', 0), getValue(row, 'doublingRange_km', 0), 200),4)
			end
			returnString = returnString .. '\n| ' 
			if trange >= 400 then
				returnString = returnString .. round(calculateBeamArea(getValue(row, 'dispersionModel'), getValue(row, 'emittance_mrad', 0), getValue(row, 'lensRadius_cm', 0), getValue(row, 'doublingRange_km', 0), 400),4)
			end
			returnString = returnString .. '\n| ' 
			if trange >= 600 then
				returnString = returnString .. round(calculateBeamArea(getValue(row, 'dispersionModel'), getValue(row, 'emittance_mrad', 0), getValue(row, 'lensRadius_cm', 0), getValue(row, 'doublingRange_km', 0), 600),4)
			end
			returnString = returnString .. '\n| ' 
			if trange >= 800 then
				returnString = returnString .. round(calculateBeamArea(getValue(row, 'dispersionModel'), getValue(row, 'emittance_mrad', 0), getValue(row, 'lensRadius_cm', 0), getValue(row, 'doublingRange_km', 0), 800),4)
			end
			
			returnString = returnString .. '\n|-'
		end
	end
	returnString = returnString .. '\n|}'
	return returnString
end

--endregion

return ParticleWeaponLister