VEAF - AA crates stacking feature:

Allows players to create systems using as many crates as they like
Example : an amount X of patriot launcher crates allows for Y launchers to be deployed, if a player brings 2*X+Z crates (Z being lower then X), then deploys the patriot site, 2*Y launchers will be in the group and Z launcher crate will be left over
This commit is contained in:
David Pierron 2024-11-09 20:13:16 +01:00
parent 4f15513fc8
commit a3debfe000

233
CTLD.lua
View File

@ -76,9 +76,6 @@ ctld.vehiclesWeight = {
["M1043 HMMWV Armament"] = 2500
}
ctld.aaLaunchers = 3 -- controls how many launchers to add to the kub/buk when its spawned.
ctld.hawkLaunchers = 8 -- controls how many launchers to add to the hawk when its spawned.
ctld.spawnRPGWithCoalition = true --spawns a friendly RPG unit with Coalition forces
ctld.spawnStinger = false -- spawns a stinger / igla soldier with a group of 6 or more soldiers!
@ -118,6 +115,9 @@ ctld.hoverTime = 10 -- Time to hold hover above a crate for loading in seconds
-- end of Simulated Sling load configuration
-- AA SYSTEM CONFIG --
ctld.aaLaunchers = 3 -- controls how many launchers to add to the AA systems when its spawned if no amount is specified in the template.
-- Sets a limit on the number of active AA systems that can be built for RED.
-- A system is counted as Active if its fully functional and has all parts
-- If a system is partially destroyed, it no longer counts towards the total
@ -128,6 +128,11 @@ ctld.AASystemLimitRED = 20 -- Red side limit
ctld.AASystemLimitBLUE = 20 -- Blue side limit
-- Allows players to create systems using as many crates as they like
-- Example : an amount X of patriot launcher crates allows for Y launchers to be deployed, if a player brings 2*X+Z crates (Z being lower then X), then deploys the patriot site, 2*Y launchers will be in the group and Z launcher crate will be left over
ctld.AASystemCrateStacking = false
--END AA SYSTEM CONFIG --
-- ***************** JTAC CONFIGURATION *****************
@ -702,8 +707,8 @@ ctld.spawnableCrates = {
{ weight = 540, desc = "HAWK Launcher", unit = "Hawk ln", side = 2},
{ weight = 545, desc = "HAWK Search Radar", unit = "Hawk sr", side = 2 },
{ weight = 546, desc = "HAWK Track Radar", unit = "Hawk tr", side = 2 },
{ weight = 547, desc = "HAWK PCP", unit = "Hawk pcp" , side = 2 }, -- Remove this if on 1.2
{ weight = 548, desc = "HAWK CWAR", unit = "Hawk cwar" , side = 2 }, -- Remove this if on 2.5
--{ weight = 547, desc = "HAWK PCP", unit = "Hawk pcp" , side = 2 }, -- Remove this if on 1.2
--{ weight = 548, desc = "HAWK CWAR", unit = "Hawk cwar" , side = 2 }, -- Remove this if on 2.5
{ weight = 549, desc = "HAWK Repair", unit = "HAWK Repair" , side = 2 },
-- End of HAWK
@ -722,12 +727,12 @@ ctld.spawnableCrates = {
},
["AA long range"] = {
-- Patriot System
{ weight = 555, desc = "Patriot Launcher", unit = "Patriot ln", side = 2 },
{ weight = 555, desc = "Patriot Launcher", unit = "Patriot ln", side = 2, cratesRequired = 2},
{ weight = 556, desc = "Patriot Radar", unit = "Patriot str" , side = 2 },
{ weight = 557, desc = "Patriot ECS", unit = "Patriot ECS", side = 2 },
-- { weight = 553, desc = "Patriot ICC", unit = "Patriot cp", side = 2 },
-- { weight = 554, desc = "Patriot EPP", unit = "Patriot EPP", side = 2 },
{ weight = 558, desc = "Patriot AMG (optional)", unit = "Patriot AMG" , side = 2 },
--{ weight = 558, desc = "Patriot AMG (optional)", unit = "Patriot AMG" , side = 2 },
{ weight = 559, desc = "Patriot Repair", unit = "Patriot Repair" , side = 2 },
-- End of Patriot
@ -1598,6 +1603,8 @@ end
--- Tells CTLD What multipart AA Systems there are and what parts they need
-- A New system added here also needs the launcher added
-- The number of times that each part is spawned for each system is specified by the entry "amount", NOTE : they will be spawned in a circle with the corresponding headings, NOTE 2 : launchers will use the default ctld.aaLauncher amount if nothing is specified
-- If a component does not require a crate, it can be specified via the entry "NoCrate" set to true
ctld.AASystemTemplate = {
{
@ -1605,10 +1612,10 @@ ctld.AASystemTemplate = {
count = 4,
parts = {
{name = "Hawk ln", desc = "HAWK Launcher", launcher = true},
{name = "Hawk tr", desc = "HAWK Track Radar"},
{name = "Hawk sr", desc = "HAWK Search Radar"},
{name = "Hawk pcp", desc = "HAWK PCP"},
{name = "Hawk cwar", desc = "HAWK CWAR"},
{name = "Hawk tr", desc = "HAWK Track Radar", amount = 2},
{name = "Hawk sr", desc = "HAWK Search Radar", amount = 2},
{name = "Hawk pcp", desc = "HAWK PCP", NoCrate = true},
{name = "Hawk cwar", desc = "HAWK CWAR", amount = 2, NoCrate = true},
},
repair = "HAWK Repair",
},
@ -1616,9 +1623,10 @@ ctld.AASystemTemplate = {
name = "Patriot AA System",
count = 4,
parts = {
{name = "Patriot ln", desc = "Patriot Launcher", launcher = true},
{name = "Patriot ln", desc = "Patriot Launcher", launcher = true, amount = 8},
{name = "Patriot ECS", desc = "Patriot Control Unit"},
{name = "Patriot str", desc = "Patriot Search and Track Radar"},
{name = "Patriot str", desc = "Patriot Search and Track Radar", amount = 2},
{name = "Patriot AMG", desc = "Patriot AMG DL relay", NoCrate = true},
},
repair = "Patriot Repair",
},
@ -4046,78 +4054,155 @@ end
function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate)
if ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) then
-- rearmed hawk
-- rearmed system
return
end
-- are there all the pieces close enough together
local _systemParts = {}
--initialise list of parts
for _,_part in pairs(_aaSystemTemplate.parts) do
_systemParts[_part.name] = {name = _part.name,desc = _part.desc,found = false}
local _systemPart = {name = _part.name, desc = _part.desc, launcher = _part.launcher, amount = _part.amount, NoCrate = _part.NoCrate, found = 0, required = 1}
-- if the part is a NoCrate required, it's found by default
if _systemPart.NoCrate ~= nil then
_systemPart.found = 1
end
_systemParts[_part.name] = _systemPart
end
-- find all nearest crates and add them to the list if they're part of the AA System
local _cratePositions = {}
local _crateHdg = {}
local crateDistance = 500
-- find all crates close enough and add them to the list if they're part of the AA System
for _, _nearbyCrate in pairs(_nearbyCrates) do
if _nearbyCrate.dist < crateDistance then
if _nearbyCrate.dist < 500 then
local _name = _nearbyCrate.details.unit
if _systemParts[_nearbyCrate.details.unit] ~= nil and _systemParts[_nearbyCrate.details.unit].found == false then
local _foundPart = _systemParts[_nearbyCrate.details.unit]
if _systemParts[_name] ~= nil then
_foundPart.found = true
_foundPart.crate = _nearbyCrate
local foundCount = _systemParts[_name].found
_systemParts[_nearbyCrate.details.unit] = _foundPart
-- if this is our first time encountering this part of the system
if foundCount == 0 then
local _foundPart = _systemParts[_name]
_foundPart.found = 1
_foundPart.crates = {}
-- store the number of crates required to compute how many crates will have to be removed later and to see if the system can be deployed
local cratesRequired = _nearbyCrate.details.cratesRequired
if cratesRequired ~= nil then
_foundPart.required = cratesRequired
end
_systemParts[_name] = _foundPart
_cratePositions[_name] = {}
_crateHdg[_name] = {}
else
-- otherwise, we found another crate for the same part
_systemParts[_name].found = foundCount + 1
end
-- add the crate to the part info along with it's position and heading
local crateUnit = _nearbyCrate.crateUnit
table.insert(_systemParts[_name].crates, _nearbyCrate)
table.insert(_cratePositions[_name], crateUnit:getPoint())
table.insert(_crateHdg[_name], mist.getHeading(crateUnit))
end
end
end
local spawnDistance = 20 -- radius to spawn launchers in a circle relative to the crate location
-- Compute the centroids for each type of crates and then the centroid of all the system crates which is used to find the spawn location for each part and a position for the NoCrate parts respectively
-- One issue, all crates are considered for the centroid and the headings but not all of them may be used if crate stacking is allowed
local _crateCentroids = {}
local _idxCentroids = {}
for _partName, _partPositions in pairs(_cratePositions) do
_crateCentroids[_partName] = ctld.getCentroid(_partPositions)
table.insert(_idxCentroids, _crateCentroids[_partName])
end
local _crateCentroid = ctld.getCentroid(_idxCentroids)
-- Compute the average heading for each type of crates to know the heading to spawn the part
local _aveHdg = {}
-- Headings of each group of crates
for _partName, _crateHeadings in pairs(_crateHdg) do
local crateCount = #_crateHeadings
_aveHdg[_partName] = 0
-- Heading of each crate within a group
for _index, _crateHeading in pairs(_crateHeadings) do
_aveHdg[_partName] = _crateHeading / crateCount + _aveHdg[_partName]
end
end
local spawnDistance = 50 -- circle diameter to spawn units in a circle and randomize position relative to the crate location
local arcRad = math.pi * 2
local _txt = ""
local _posArray = {}
local _typeArray = {}
local _hdgArray = {}
local _typeArray = {}
-- for each part of the system parts
for _name, _systemPart in pairs(_systemParts) do
if _systemPart.found == false then
-- check if enough crates were found to build the part
if _systemPart.found < _systemPart.required then
_txt = _txt.."Missing ".._systemPart.desc.."\n"
else
-- use the centroid of the crates for this part as a spawn location
local _point = _crateCentroids[_name]
-- in the case this centroid does not exist (NoCrate), use the centroid of all crates found and add some randomness
if _point == nil then
_point = _crateCentroid
_point = { x = _point.x + math.random(0,3)*spawnDistance, y = _point.y, z = _point.z + math.random(0,3)*spawnDistance}
end
local _launcherPart = ctld.getLauncherUnitFromAATemplate(_aaSystemTemplate)
local _point = _systemPart.crate.crateUnit:getPoint()
local _hdg = mist.getHeading(_systemPart.crate.crateUnit, true)
-- use the average heading to spawn the part at
local _hdg = _aveHdg[_name]
-- if non are found (NoCrate), random heading
if _hdg == nil then
_hdg = math.random(0, arcRad)
end
--handle multiple launchers from one crate
if (_name == "Hawk ln" and ctld.hawkLaunchers > 1)
or (_launcherPart == _name and ctld.aaLaunchers > 1) then
--add multiple launcher
local _launchers = ctld.aaLaunchers
if _name == "Hawk ln" then
_launchers = ctld.hawkLaunchers
-- search for the amount of times this part needs to be spawned, by default 1 for any unit and aaLaunchers for launchers
local partAmount = 1
if _systemPart.amount == nil then
if _systemPart.launcher ~= nil then
partAmount = ctld.aaLaunchers
end
else
-- but the amount may also be specified in the template
partAmount = _systemPart.amount
end
-- if crate stacking is allowed, then find the multiplication factor for the amount depending on how many crates are required and how many were found
if ctld.AASystemCrateStacking then
_systemPart.amountFactor = _systemPart.found - _systemPart.found%_systemPart.required
else
_systemPart.amountFactor = 1
end
partAmount = partAmount * _systemPart.amountFactor
for _i = 1, _launchers do
--handle multiple units per part by spawning them in a circle around the crate
if partAmount > 1 then
local angular_step = arcRad / partAmount
-- spawn in a circle around the crate
local _angle = math.pi * 2 * (_i - 1) / _launchers
for _i = 1, partAmount do
local _angle = (angular_step * (_i - 1) + _hdg)%arcRad
local _xOffset = math.cos(_angle) * spawnDistance
local _yOffset = math.sin(_angle) * spawnDistance
local lnPoint = { x = _point.x + _xOffset, y = _point.y, z = _point.z + _yOffset }
table.insert(_posArray, lnPoint)
table.insert(_posArray, { x = _point.x + _xOffset, y = _point.y, z = _point.z + _yOffset })
table.insert(_hdgArray, _angle) -- also spawn them perpendicular to that point of the circle
table.insert(_typeArray, _name)
table.insert(_hdgArray, _hdg)
end
else
table.insert(_posArray, _point)
table.insert(_typeArray, _name)
table.insert(_hdgArray, _hdg)
table.insert(_typeArray, _name)
end
end
end
@ -4140,17 +4225,32 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla
-- destroy crates
for _name, _systemPart in pairs(_systemParts) do
-- if there is a crate to delete in the first place
if _systemPart.NoCrate ~= true then
-- figure out how many crates to delete since we searched for as many as possible, not all of them might have been used
local amountToDel = _systemPart.amountFactor*_systemPart.required
local DelCounter = 0
if _heli:getCoalition() == 1 then
ctld.spawnedCratesRED[_systemPart.crate.crateUnit:getName()] = nil
else
ctld.spawnedCratesBLUE[_systemPart.crate.crateUnit:getName()] = nil
-- for each crate found for this part
for _index, _crate in pairs(_systemPart.crates) do
-- if we still need to delete some crates
if DelCounter < amountToDel then
if _heli:getCoalition() == 1 then
ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil
else
ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil
end
--destroy
-- if ctld.slingLoad == false then
_crate.crateUnit:destroy()
DelCounter = DelCounter + 1 -- count up for one more crate has been deleted
--end
else
break
end
end
end
--destroy
-- if ctld.slingLoad == false then
_systemPart.crate.crateUnit:destroy()
--end
end
-- HAWK / BUK READY!
@ -4239,13 +4339,13 @@ function ctld.repairAASystem(_heli, _nearestCrate,_aaSystem)
--spawn new one
local _types = {}
local _points = {}
local _hdgs = {}
local _points = {}
for _, _part in pairs(_oldHawk) do
table.insert(_points, _part.point)
table.insert(_types, _part.unit)
table.insert(_hdgs, _part.hdg)
table.insert(_types, _part.unit)
end
--remove old system
@ -4358,15 +4458,18 @@ function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs)
["task"] = {},
}
local defaultHdg = 120 * math.pi / 180 -- radians = 120 degrees
local _hdg = 120 * math.pi / 180 -- radians = 120 degrees
local _spreadMin = 2
local _spreadMax = 5
local _spreadMult = 2
for _i, _pos in ipairs(_positions) do
local _unitId = ctld.getNextUnitId()
local _details = { type = _types[_i], unitId = _unitId, name = string.format("Unpacked %s #%i", _types[_i], _unitId) }
if _hdgs and _hdgs[_i] then
defaultHdg = _hdgs[_i]
_hdg = _hdgs[_i]
end
_group.units[_i] = ctld.createUnit(_pos.x + 5, _pos.z + 5, defaultHdg, _details)
_group.units[_i] = ctld.createUnit(_pos.x +math.random(_spreadMin,_spreadMax)*_spreadMult, _pos.z +math.random(_spreadMin,_spreadMax)*_spreadMult, _hdg, _details)
end
--mist function
@ -5320,7 +5423,7 @@ function ctld.addJTACRadioCommand(_side)
local jtacCounter = 0
for _jtacGroupName,jtacUnit in pairs(ctld.jtacUnits) do
ctld.logTrace(string.format("JTAC - MENU - [%s] - processing menu", ctld.p(_jtacGroupName)))
--ctld.logTrace(string.format("JTAC - MENU - [%s] - processing menu", ctld.p(_jtacGroupName)))
--if the JTAC is on the same team as the group being considered
local jtacCoalition = ctld.jtacUnits[_jtacGroupName].side
@ -5329,8 +5432,8 @@ function ctld.addJTACRadioCommand(_side)
if ctld.jtacGroupSubMenuPath[_jtacGroupName] and #ctld.jtacGroupSubMenuPath[_jtacGroupName]==2 then
missionCommands.removeItemForGroup(_groupId, ctld.jtacGroupSubMenuPath[_jtacGroupName])
end
ctld.logTrace(string.format("JTAC - MENU - [%s] - jtacTargetsList = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacTargetsList[_jtacGroupName])))
ctld.logTrace(string.format("JTAC - MENU - [%s] - jtacCurrentTargets = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacCurrentTargets[_jtacGroupName])))
--ctld.logTrace(string.format("JTAC - MENU - [%s] - jtacTargetsList = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacTargetsList[_jtacGroupName])))
--ctld.logTrace(string.format("JTAC - MENU - [%s] - jtacCurrentTargets = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacCurrentTargets[_jtacGroupName])))
local jtacActionMenu = false
for _,_specialOptionTable in pairs(ctld.jtacSpecialOptions) do
@ -5353,7 +5456,7 @@ function ctld.addJTACRadioCommand(_side)
end
--add the JTAC group submenu to the current page
ctld.jtacGroupSubMenuPath[_jtacGroupName] = missionCommands.addSubMenuForGroup(_groupId, jtacGroupSubMenuName, jtacCurrentPagePath)
ctld.logTrace(string.format("JTAC - MENU - [%s] - jtacGroupSubMenuPath = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacGroupSubMenuPath[_jtacGroupName])))
--ctld.logTrace(string.format("JTAC - MENU - [%s] - jtacGroupSubMenuPath = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacGroupSubMenuPath[_jtacGroupName])))
--make a copy of the JTAC group submenu's path to insert the target's list on as many pages as required. The JTAC's group submenu path only leads to the first page
local jtacTargetPagePath = mist.utils.deepCopy(ctld.jtacGroupSubMenuPath[_jtacGroupName])
@ -5395,7 +5498,7 @@ function ctld.addJTACRadioCommand(_side)
end
if #ctld.jtacTargetsList[_jtacGroupName] >= 1 then
ctld.logTrace(string.format("JTAC - MENU - [%s] - adding targets menu", ctld.p(_jtacGroupName)))
--ctld.logTrace(string.format("JTAC - MENU - [%s] - adding targets menu", ctld.p(_jtacGroupName)))
--add a reset targeting option to revert to automatic JTAC unit targeting
missionCommands.addCommandForGroup(_groupId, "Reset TGT Selection", jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = nil})