mirror of
https://github.com/ciribob/DCS-CTLD.git
synced 2025-08-15 06:17:22 +00:00
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:
parent
4f15513fc8
commit
a3debfe000
233
CTLD.lua
233
CTLD.lua
@ -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})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user