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
+168 -65
View File
@@ -76,9 +76,6 @@ ctld.vehiclesWeight = {
["M1043 HMMWV Armament"] = 2500 ["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.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! 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 -- end of Simulated Sling load configuration
-- AA SYSTEM CONFIG -- -- 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. -- 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 -- 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 -- 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 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 -- --END AA SYSTEM CONFIG --
-- ***************** JTAC CONFIGURATION ***************** -- ***************** JTAC CONFIGURATION *****************
@@ -702,8 +707,8 @@ ctld.spawnableCrates = {
{ weight = 540, desc = "HAWK Launcher", unit = "Hawk ln", side = 2}, { weight = 540, desc = "HAWK Launcher", unit = "Hawk ln", side = 2},
{ weight = 545, desc = "HAWK Search Radar", unit = "Hawk sr", 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 = 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 = 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 = 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 }, { weight = 549, desc = "HAWK Repair", unit = "HAWK Repair" , side = 2 },
-- End of HAWK -- End of HAWK
@@ -722,12 +727,12 @@ ctld.spawnableCrates = {
}, },
["AA long range"] = { ["AA long range"] = {
-- Patriot System -- 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 = 556, desc = "Patriot Radar", unit = "Patriot str" , side = 2 },
{ weight = 557, desc = "Patriot ECS", unit = "Patriot ECS", side = 2 }, { weight = 557, desc = "Patriot ECS", unit = "Patriot ECS", side = 2 },
-- { weight = 553, desc = "Patriot ICC", unit = "Patriot cp", side = 2 }, -- { weight = 553, desc = "Patriot ICC", unit = "Patriot cp", side = 2 },
-- { weight = 554, desc = "Patriot EPP", unit = "Patriot EPP", 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 }, { weight = 559, desc = "Patriot Repair", unit = "Patriot Repair" , side = 2 },
-- End of Patriot -- End of Patriot
@@ -1598,6 +1603,8 @@ end
--- Tells CTLD What multipart AA Systems there are and what parts they need --- Tells CTLD What multipart AA Systems there are and what parts they need
-- A New system added here also needs the launcher added -- 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 = { ctld.AASystemTemplate = {
{ {
@@ -1605,10 +1612,10 @@ ctld.AASystemTemplate = {
count = 4, count = 4,
parts = { parts = {
{name = "Hawk ln", desc = "HAWK Launcher", launcher = true}, {name = "Hawk ln", desc = "HAWK Launcher", launcher = true},
{name = "Hawk tr", desc = "HAWK Track Radar"}, {name = "Hawk tr", desc = "HAWK Track Radar", amount = 2},
{name = "Hawk sr", desc = "HAWK Search Radar"}, {name = "Hawk sr", desc = "HAWK Search Radar", amount = 2},
{name = "Hawk pcp", desc = "HAWK PCP"}, {name = "Hawk pcp", desc = "HAWK PCP", NoCrate = true},
{name = "Hawk cwar", desc = "HAWK CWAR"}, {name = "Hawk cwar", desc = "HAWK CWAR", amount = 2, NoCrate = true},
}, },
repair = "HAWK Repair", repair = "HAWK Repair",
}, },
@@ -1616,9 +1623,10 @@ ctld.AASystemTemplate = {
name = "Patriot AA System", name = "Patriot AA System",
count = 4, count = 4,
parts = { 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 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", repair = "Patriot Repair",
}, },
@@ -4046,78 +4054,155 @@ end
function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate)
if ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) then if ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) then
-- rearmed hawk -- rearmed system
return return
end end
-- are there all the pieces close enough together
local _systemParts = {} local _systemParts = {}
--initialise list of parts --initialise list of parts
for _,_part in pairs(_aaSystemTemplate.parts) do 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 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 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 if _systemParts[_name] ~= nil then
local _foundPart = _systemParts[_nearbyCrate.details.unit]
_foundPart.found = true local foundCount = _systemParts[_name].found
_foundPart.crate = _nearbyCrate
_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 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 _txt = ""
local _posArray = {} local _posArray = {}
local _typeArray = {}
local _hdgArray = {} local _hdgArray = {}
local _typeArray = {}
-- for each part of the system parts
for _name, _systemPart in pairs(_systemParts) do 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" _txt = _txt.."Missing ".._systemPart.desc.."\n"
else 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) -- use the average heading to spawn the part at
local _point = _systemPart.crate.crateUnit:getPoint() local _hdg = _aveHdg[_name]
local _hdg = mist.getHeading(_systemPart.crate.crateUnit, true) -- if non are found (NoCrate), random heading
if _hdg == nil then
_hdg = math.random(0, arcRad)
end
--handle multiple launchers from one crate -- search for the amount of times this part needs to be spawned, by default 1 for any unit and aaLaunchers for launchers
if (_name == "Hawk ln" and ctld.hawkLaunchers > 1) local partAmount = 1
or (_launcherPart == _name and ctld.aaLaunchers > 1) then if _systemPart.amount == nil then
if _systemPart.launcher ~= nil then
--add multiple launcher partAmount = ctld.aaLaunchers
local _launchers = ctld.aaLaunchers
if _name == "Hawk ln" then
_launchers = ctld.hawkLaunchers
end 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 for _i = 1, partAmount do
local _angle = math.pi * 2 * (_i - 1) / _launchers local _angle = (angular_step * (_i - 1) + _hdg)%arcRad
local _xOffset = math.cos(_angle) * spawnDistance local _xOffset = math.cos(_angle) * spawnDistance
local _yOffset = math.sin(_angle) * spawnDistance local _yOffset = math.sin(_angle) * spawnDistance
local lnPoint = { x = _point.x + _xOffset, y = _point.y, z = _point.z + _yOffset } 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(_posArray, lnPoint)
table.insert(_typeArray, _name) table.insert(_typeArray, _name)
table.insert(_hdgArray, _hdg)
end end
else else
table.insert(_posArray, _point) table.insert(_posArray, _point)
table.insert(_typeArray, _name)
table.insert(_hdgArray, _hdg) table.insert(_hdgArray, _hdg)
table.insert(_typeArray, _name)
end end
end end
end end
@@ -4140,17 +4225,32 @@ function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTempla
-- destroy crates -- destroy crates
for _name, _systemPart in pairs(_systemParts) do 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 -- for each crate found for this part
ctld.spawnedCratesRED[_systemPart.crate.crateUnit:getName()] = nil for _index, _crate in pairs(_systemPart.crates) do
else -- if we still need to delete some crates
ctld.spawnedCratesBLUE[_systemPart.crate.crateUnit:getName()] = nil 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 end
--destroy
-- if ctld.slingLoad == false then
_systemPart.crate.crateUnit:destroy()
--end
end end
-- HAWK / BUK READY! -- HAWK / BUK READY!
@@ -4239,13 +4339,13 @@ function ctld.repairAASystem(_heli, _nearestCrate,_aaSystem)
--spawn new one --spawn new one
local _types = {} local _types = {}
local _points = {}
local _hdgs = {} local _hdgs = {}
local _points = {}
for _, _part in pairs(_oldHawk) do for _, _part in pairs(_oldHawk) do
table.insert(_points, _part.point) table.insert(_points, _part.point)
table.insert(_types, _part.unit)
table.insert(_hdgs, _part.hdg) table.insert(_hdgs, _part.hdg)
table.insert(_types, _part.unit)
end end
--remove old system --remove old system
@@ -4358,15 +4458,18 @@ function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs)
["task"] = {}, ["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 for _i, _pos in ipairs(_positions) do
local _unitId = ctld.getNextUnitId() local _unitId = ctld.getNextUnitId()
local _details = { type = _types[_i], unitId = _unitId, name = string.format("Unpacked %s #%i", _types[_i], _unitId) } local _details = { type = _types[_i], unitId = _unitId, name = string.format("Unpacked %s #%i", _types[_i], _unitId) }
if _hdgs and _hdgs[_i] then if _hdgs and _hdgs[_i] then
defaultHdg = _hdgs[_i] _hdg = _hdgs[_i]
end 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 end
--mist function --mist function
@@ -5320,7 +5423,7 @@ function ctld.addJTACRadioCommand(_side)
local jtacCounter = 0 local jtacCounter = 0
for _jtacGroupName,jtacUnit in pairs(ctld.jtacUnits) do 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 --if the JTAC is on the same team as the group being considered
local jtacCoalition = ctld.jtacUnits[_jtacGroupName].side local jtacCoalition = ctld.jtacUnits[_jtacGroupName].side
@@ -5329,8 +5432,8 @@ function ctld.addJTACRadioCommand(_side)
if ctld.jtacGroupSubMenuPath[_jtacGroupName] and #ctld.jtacGroupSubMenuPath[_jtacGroupName]==2 then if ctld.jtacGroupSubMenuPath[_jtacGroupName] and #ctld.jtacGroupSubMenuPath[_jtacGroupName]==2 then
missionCommands.removeItemForGroup(_groupId, ctld.jtacGroupSubMenuPath[_jtacGroupName]) missionCommands.removeItemForGroup(_groupId, ctld.jtacGroupSubMenuPath[_jtacGroupName])
end 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] - 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] - jtacCurrentTargets = %s", ctld.p(_jtacGroupName), ctld.p(ctld.jtacCurrentTargets[_jtacGroupName])))
local jtacActionMenu = false local jtacActionMenu = false
for _,_specialOptionTable in pairs(ctld.jtacSpecialOptions) do for _,_specialOptionTable in pairs(ctld.jtacSpecialOptions) do
@@ -5353,7 +5456,7 @@ function ctld.addJTACRadioCommand(_side)
end end
--add the JTAC group submenu to the current page --add the JTAC group submenu to the current page
ctld.jtacGroupSubMenuPath[_jtacGroupName] = missionCommands.addSubMenuForGroup(_groupId, jtacGroupSubMenuName, jtacCurrentPagePath) 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 --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]) local jtacTargetPagePath = mist.utils.deepCopy(ctld.jtacGroupSubMenuPath[_jtacGroupName])
@@ -5395,7 +5498,7 @@ function ctld.addJTACRadioCommand(_side)
end end
if #ctld.jtacTargetsList[_jtacGroupName] >= 1 then 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 --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}) missionCommands.addCommandForGroup(_groupId, "Reset TGT Selection", jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = nil})