Compare commits

..

7 Commits

Author SHA1 Message Date
David Pierron c9270a0fd2 changed the code that looks for nearby units (#163) 2025-09-10 14:39:58 +02:00
David Pierron def40f440c added coff.ee information to the readme 2025-07-31 08:47:54 +02:00
David Pierron 417c57f97c Bump version to 1.5.1 for release 2025-07-31 08:16:42 +02:00
David Pierron dec5bf02b4 Stop autosmoke being the default for JTAC and drones 2025-07-31 08:16:25 +02:00
David Pierron 68693300dc By default, recon is disabled 2025-07-31 08:15:57 +02:00
David Pierron f7f603c486 Added checks to avoind nil errors in ctld.adjustRoute 2025-07-31 08:15:31 +02:00
David Pierron 290950a16b updated README 2025-07-22 16:24:08 +02:00
2 changed files with 274 additions and 241 deletions
+257 -230
View File
@@ -40,7 +40,7 @@ end
ctld.Id = "CTLD - " ctld.Id = "CTLD - "
--- Version. --- Version.
ctld.Version = "1.5.0" ctld.Version = "1.5.2"
-- To add debugging messages to dcs.log, change the following log levels to `true`; `Debug` is less detailed than `Trace` -- To add debugging messages to dcs.log, change the following log levels to `true`; `Debug` is less detailed than `Trace`
ctld.Debug = false ctld.Debug = false
@@ -491,8 +491,8 @@ ctld.JTAC_LIMIT_RED = 10 -- max number of JTAC Crates for the RED Si
ctld.JTAC_LIMIT_BLUE = 10 -- max number of JTAC Crates for the BLUE Side ctld.JTAC_LIMIT_BLUE = 10 -- max number of JTAC Crates for the BLUE Side
ctld.JTAC_dropEnabled = true -- allow JTAC Crate spawn from F10 menu ctld.JTAC_dropEnabled = true -- allow JTAC Crate spawn from F10 menu
ctld.JTAC_maxDistance = 10000 -- How far a JTAC can "see" in meters (with Line of Sight) ctld.JTAC_maxDistance = 10000 -- How far a JTAC can "see" in meters (with Line of Sight)
ctld.JTAC_smokeOn_RED = true -- enables marking of target with smoke for RED forces ctld.JTAC_smokeOn_RED = false -- enables marking of target with smoke for RED forces
ctld.JTAC_smokeOn_BLUE = true -- enables marking of target with smoke for BLUE forces ctld.JTAC_smokeOn_BLUE = false -- enables marking of target with smoke for BLUE forces
ctld.JTAC_smokeColour_RED = 4 -- RED side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4 ctld.JTAC_smokeColour_RED = 4 -- RED side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4
ctld.JTAC_smokeColour_BLUE = 1 -- BLUE side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4 ctld.JTAC_smokeColour_BLUE = 1 -- BLUE side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4
ctld.JTAC_smokeMarginOfError = 50 -- error that the JTAC is allowed to make when popping a smoke (in meters) ctld.JTAC_smokeMarginOfError = 50 -- error that the JTAC is allowed to make when popping a smoke (in meters)
@@ -1977,7 +1977,7 @@ function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius)
end end
local unitsNamesList = ctld.getNearbyUnits(unit:getPoint(), _radius, unit:getCoalition()) local unitsNamesList = ctld.getNearbyUnits(unit:getPoint(), _radius, unit:getCoalition())
local repackableUnits = {} local repackableUnits = {}
for i = 1, #unitsNamesList do for i = 1, #unitsNamesList do
local unitObject = Unit.getByName(unitsNamesList[i]) local unitObject = Unit.getByName(unitsNamesList[i])
@@ -1998,11 +1998,13 @@ function ctld.getNearbyUnits(_point, _radius, _coalition)
local unitsByDistance = {} local unitsByDistance = {}
local cpt = 1 local cpt = 1
local _units = {} local _units = {}
local _unitList = mist.DBs.unitsByName for _unitName, _ in pairs(mist.DBs.unitsByName) do
for _unitName, _unit in pairs(mist.DBs.unitsByName) do
local u = Unit.getByName(_unitName) local u = Unit.getByName(_unitName)
if u and u:isExist() and (_coalition == 4 or u:getCoalition() == _coalition) then local e = (u and u:isExist()) or false
--local _dist = ctld.getDistance(u:getPoint(), _point) -- pcall is needed because getCoalition() fails if the unit is an object without coalition (like a smoke effect)
local c = nil
pcall(function() c = (u and e and u:getCoalition()) or nil end)
if u and e and (_coalition == 4 or c == _coalition) then
local _dist = mist.utils.get2DDist(u:getPoint(), _point) local _dist = mist.utils.get2DDist(u:getPoint(), _point)
if _dist <= _radius then if _dist <= _radius then
unitsByDistance[cpt] = {id =cpt, dist = _dist, unit = _unitName, typeName = u:getTypeName()} unitsByDistance[cpt] = {id =cpt, dist = _dist, unit = _unitName, typeName = u:getTypeName()}
@@ -2010,7 +2012,7 @@ function ctld.getNearbyUnits(_point, _radius, _coalition)
end end
end end
end end
--table.sort(unitsByDistance, function(a,b) return a.dist < b.dist end) -- sort the table by distance (the nearest first) --table.sort(unitsByDistance, function(a,b) return a.dist < b.dist end) -- sort the table by distance (the nearest first)
table.sort(unitsByDistance, function(a,b) return a.typeName < b.typeName end) -- sort the table by typeNAme table.sort(unitsByDistance, function(a,b) return a.typeName < b.typeName end) -- sort the table by typeNAme
for i, v in ipairs(unitsByDistance) do for i, v in ipairs(unitsByDistance) do
@@ -2092,7 +2094,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack'
local _name = string.format("%s_%i", v.desc, _unitId) local _name = string.format("%s_%i", v.desc, _unitId)
local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 10 local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 10
local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit
if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then
ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil)
else else
@@ -2105,8 +2107,8 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack'
ctld.repackRequestsStack[ii] = nil -- remove the processed request from the stacking table ctld.repackRequestsStack[ii] = nil -- remove the processed request from the stacking table
end end
if ctld.enableRepackingVehicles == true then if ctld.enableRepackingVehicles == true then
return t + 3 -- reschedule the function in 3 seconds return t + 3 -- reschedule the function in 3 seconds
else else
@@ -3169,7 +3171,7 @@ function ctld.updateTroopsInGame(params, t) -- return count of troops in game b
for index, unitObj in pairs(v:getUnits()) do -- for each unit in group for index, unitObj in pairs(v:getUnits()) do -- for each unit in group
if unitObj:getDesc().attributes.Infantry then if unitObj:getDesc().attributes.Infantry then
ctld.InfantryInGameCount[coalitionId] = ctld.InfantryInGameCount[coalitionId] + 1 ctld.InfantryInGameCount[coalitionId] = ctld.InfantryInGameCount[coalitionId] + 1
end end
end end
end end
end end
@@ -3227,7 +3229,7 @@ function ctld.loadTroopsFromZone(_args)
ctld.logTrace("FG_ ctld.InfantryInGameCount[heloCoa] = %s", ctld.p(ctld.InfantryInGameCount[heloCoa])) ctld.logTrace("FG_ ctld.InfantryInGameCount[heloCoa] = %s", ctld.p(ctld.InfantryInGameCount[heloCoa]))
ctld.logTrace("FG_ _groupTemplate.total = %s", ctld.p(_groupTemplate.total)) ctld.logTrace("FG_ _groupTemplate.total = %s", ctld.p(_groupTemplate.total))
ctld.logTrace("FG_ ctld.nbLimitSpawnedTroops[%s].total = %s", ctld.p(heloCoa), ctld.p(ctld.nbLimitSpawnedTroops[heloCoa])) ctld.logTrace("FG_ ctld.nbLimitSpawnedTroops[%s].total = %s", ctld.p(heloCoa), ctld.p(ctld.nbLimitSpawnedTroops[heloCoa]))
local limitReached = true local limitReached = true
if (ctld.nbLimitSpawnedTroops[1]~=0 or ctld.nbLimitSpawnedTroops[2]~=0) and (ctld.InfantryInGameCount[heloCoa] + _groupTemplate.total > ctld.nbLimitSpawnedTroops[heloCoa]) then -- load troops only if Coa limit not reached if (ctld.nbLimitSpawnedTroops[1]~=0 or ctld.nbLimitSpawnedTroops[2]~=0) and (ctld.InfantryInGameCount[heloCoa] + _groupTemplate.total > ctld.nbLimitSpawnedTroops[heloCoa]) then -- load troops only if Coa limit not reached
ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Count Infantries limit in the mission reached, you can't load more troops"), 10) ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Count Infantries limit in the mission reached, you can't load more troops"), 10)
@@ -3960,7 +3962,7 @@ function ctld.unpackCrates(_arguments)
local _crates = ctld.getCratesAndDistance(_heli) local _crates = ctld.getCratesAndDistance(_heli)
local _crate = ctld.getClosestCrate(_heli, _crates) local _crate = ctld.getClosestCrate(_heli, _crates)
ctld.logTrace("FG_ ctld.unpackCrates._crate = %s", ctld.p(_crate)) ctld.logTrace("FG_ ctld.unpackCrates._crate = %s", ctld.p(_crate))
if ctld.inLogisticsZone(_heli) == true or ctld.farEnoughFromLogisticZone(_heli) == false then if ctld.inLogisticsZone(_heli) == true or ctld.farEnoughFromLogisticZone(_heli) == false then
ctld.displayMessageToGroup(_heli, ctld.displayMessageToGroup(_heli,
ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20)
@@ -4015,7 +4017,7 @@ function ctld.unpackCrates(_arguments)
ctld.logTrace("_crate = %s", ctld.p(_crate)) ctld.logTrace("_crate = %s", ctld.p(_crate))
local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _point }, { _crate.details.unit }, { _crateHdg }) local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _point }, { _crate.details.unit }, { _crateHdg })
ctld.logTrace("_spawnedGroups = %s", ctld.p(_spawnedGroups)) ctld.logTrace("_spawnedGroups = %s", ctld.p(_spawnedGroups))
if _heli:getCoalition() == 1 then if _heli:getCoalition() == 1 then
ctld.spawnedCratesRED[_crateName] = nil ctld.spawnedCratesRED[_crateName] = nil
else else
@@ -5026,7 +5028,7 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates)
if ctld.unitDynamicCargoCapable(_heli) == true then if ctld.unitDynamicCargoCapable(_heli) == true then
_point = ctld.getPointInRearSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName())) _point = ctld.getPointInRearSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName()))
end end
local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true) local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true)
-- destroy crates -- destroy crates
@@ -5864,208 +5866,203 @@ end
-- Adds menuitem to a human unit -- Adds menuitem to a human unit
function ctld.addTransportF10MenuOptions(_unitName) function ctld.addTransportF10MenuOptions(_unitName)
ctld.logDebug("ctld.addTransportF10MenuOptions(_unitName=[%s])", ctld.p(_unitName)) ctld.logDebug("ctld.addTransportF10MenuOptions(_unitName=[%s])", ctld.p(_unitName))
local status, error = pcall(function() local _unit = ctld.getTransportUnit(_unitName)
local _unit = ctld.getTransportUnit(_unitName) ctld.logTrace("_unit = %s", ctld.p(_unit))
ctld.logTrace("_unit = %s", ctld.p(_unit))
if _unit then if _unit then
local _unitTypename = _unit:getTypeName() local _unitTypename = _unit:getTypeName()
local _groupId = ctld.getGroupId(_unit) local _groupId = ctld.getGroupId(_unit)
if _groupId then if _groupId then
-- ctld.logTrace("_groupId = %s", ctld.p(_groupId)) -- ctld.logTrace("_groupId = %s", ctld.p(_groupId))
-- ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo[tostring(_groupId)])) -- ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo[tostring(_groupId)]))
if ctld.addedTo[tostring(_groupId)] == nil then if ctld.addedTo[tostring(_groupId)] == nil then
ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId))
local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD"))
local _unitActions = ctld.getUnitActions(_unitTypename) local _unitActions = ctld.getUnitActions(_unitTypename)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _rootPath, ctld.checkTroopStatus, { _unitName }) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _rootPath, ctld.checkTroopStatus, { _unitName })
if _unitActions.troops then if _unitActions.troops then
local _troopCommandsPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Troop Transport"), _rootPath) local _troopCommandsPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Troop Transport"), _rootPath)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload / Extract Troops"), missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload / Extract Troops"),
_troopCommandsPath, ctld.unloadExtractTroops, { _unitName }) _troopCommandsPath, ctld.unloadExtractTroops, { _unitName })
-- local _loadPath = missionCommands.addSubMenuForGroup(_groupId, "Load From Zone", _troopCommandsPath) -- local _loadPath = missionCommands.addSubMenuForGroup(_groupId, "Load From Zone", _troopCommandsPath)
local _transportLimit = ctld.getTransportLimit(_unitTypename) local _transportLimit = ctld.getTransportLimit(_unitTypename)
local itemNb = 0 local itemNb = 0
local menuEntries = {} local menuEntries = {}
local menuPath = _troopCommandsPath local menuPath = _troopCommandsPath
for _, _loadGroup in pairs(ctld.loadableGroups) do for _, _loadGroup in pairs(ctld.loadableGroups) do
if not _loadGroup.side or _loadGroup.side == _unit:getCoalition() then if not _loadGroup.side or _loadGroup.side == _unit:getCoalition() then
-- check size & unit -- check size & unit
if _transportLimit >= _loadGroup.total then if _transportLimit >= _loadGroup.total then
table.insert(menuEntries, table.insert(menuEntries,
{ text = ctld.i18n_translate("Load ") .. _loadGroup.name, group = _loadGroup }) { text = ctld.i18n_translate("Load ") .. _loadGroup.name, group = _loadGroup })
end
end end
end end
for _i, _menu in ipairs(menuEntries) do
-- add the menu item
itemNb = itemNb + 1
if itemNb == 9 and _i < #menuEntries then -- page limit reached (first item is "unload")
menuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"),
menuPath)
itemNb = 1
end
missionCommands.addCommandForGroup(_groupId, _menu.text, menuPath, ctld.loadTroopsFromZone,
{ _unitName, true, _menu.group, false })
end
if ctld.unitCanCarryVehicles(_unit) then
local _vehicleCommandsPath = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Vehicle / FOB Transport"), _rootPath)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload Vehicles"),
_vehicleCommandsPath, ctld.unloadTroops, { _unitName, false })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"),
_vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true })
if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then
missionCommands.addCommandForGroup(_groupId,
ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath,
ctld.loadUnloadFOBCrate, { _unitName, false })
end
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"),
_vehicleCommandsPath, ctld.checkTroopStatus, { _unitName })
end
end end
for _i, _menu in ipairs(menuEntries) do
-- add the menu item
itemNb = itemNb + 1
if itemNb == 9 and _i < #menuEntries then -- page limit reached (first item is "unload")
menuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"),
menuPath)
itemNb = 1
end
missionCommands.addCommandForGroup(_groupId, _menu.text, menuPath, ctld.loadTroopsFromZone,
{ _unitName, true, _menu.group, false })
end
if ctld.unitCanCarryVehicles(_unit) then
local _vehicleCommandsPath = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Vehicle / FOB Transport"), _rootPath)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload Vehicles"),
_vehicleCommandsPath, ctld.unloadTroops, { _unitName, false })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"),
_vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true })
if ctld.enableCrates and _unitActions.crates then if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then
if ctld.unitCanCarryVehicles(_unit) == false then missionCommands.addCommandForGroup(_groupId,
-- sort the crate categories alphabetically ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath,
local crateCategories = {} ctld.loadUnloadFOBCrate, { _unitName, false })
for category, _ in pairs(ctld.spawnableCrates) do end
table.insert(crateCategories, category) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"),
_vehicleCommandsPath, ctld.checkTroopStatus, { _unitName })
end
end
if ctld.enableCrates and _unitActions.crates then
if ctld.unitCanCarryVehicles(_unit) == false then
-- sort the crate categories alphabetically
local crateCategories = {}
for category, _ in pairs(ctld.spawnableCrates) do
table.insert(crateCategories, category)
end
table.sort(crateCategories)
--ctld.logTrace("crateCategories = [%s]", ctld.p(crateCategories))
-- add menu for spawning crates
local itemNbMain = 0
local _cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Crates: Vehicle / FOB / Drone"), _rootPath)
for _i, _category in ipairs(crateCategories) do
local _subMenuName = _category
local _crates = ctld.spawnableCrates[_subMenuName]
-- add the submenu item
itemNbMain = itemNbMain + 1
if itemNbMain == 10 and _i < #crateCategories then -- page limit reached
_cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Next page"), _cratesMenuPath)
itemNbMain = 1
end end
table.sort(crateCategories) local itemNbSubmenu = 0
--ctld.logTrace("crateCategories = [%s]", ctld.p(crateCategories)) local menuEntries = {}
local _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, _subMenuName, _cratesMenuPath)
-- add menu for spawning crates for _, _crate in pairs(_crates) do
local itemNbMain = 0 --ctld.logTrace("_crate = [%s]", ctld.p(_crate))
local _cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId, if not (_crate.multiple) or ctld.enableAllCrates then
ctld.i18n_translate("Crates: Vehicle / FOB / Drone"), _rootPath) local isJTAC = ctld.isJTACUnitType(_crate.unit)
--ctld.logTrace("isJTAC = [%s]", ctld.p(isJTAC))
for _i, _category in ipairs(crateCategories) do if not isJTAC or (isJTAC and ctld.JTAC_dropEnabled) then
local _subMenuName = _category if _crate.side == nil or (_crate.side == _unit:getCoalition()) then
local _crates = ctld.spawnableCrates[_subMenuName] local _crateRadioMsg = _crate.desc
--add in the number of crates required to build something
-- add the submenu item if _crate.cratesRequired ~= nil and _crate.cratesRequired > 1 then
itemNbMain = itemNbMain + 1 _crateRadioMsg = _crateRadioMsg .. " (" .. _crate.cratesRequired ..
if itemNbMain == 10 and _i < #crateCategories then -- page limit reached ")"
_cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Next page"), _cratesMenuPath)
itemNbMain = 1
end
local itemNbSubmenu = 0
local menuEntries = {}
local _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, _subMenuName, _cratesMenuPath)
for _, _crate in pairs(_crates) do
--ctld.logTrace("_crate = [%s]", ctld.p(_crate))
if not (_crate.multiple) or ctld.enableAllCrates then
local isJTAC = ctld.isJTACUnitType(_crate.unit)
--ctld.logTrace("isJTAC = [%s]", ctld.p(isJTAC))
if not isJTAC or (isJTAC and ctld.JTAC_dropEnabled) then
if _crate.side == nil or (_crate.side == _unit:getCoalition()) then
local _crateRadioMsg = _crate.desc
--add in the number of crates required to build something
if _crate.cratesRequired ~= nil and _crate.cratesRequired > 1 then
_crateRadioMsg = _crateRadioMsg .. " (" .. _crate.cratesRequired ..
")"
end
if _crate.multiple then
_crateRadioMsg = "* " .. _crateRadioMsg
end
local _menuEntry = { text = _crateRadioMsg, crate = _crate }
--ctld.logTrace("_menuEntry = [%s]", ctld.p(_menuEntry))
table.insert(menuEntries, _menuEntry)
end end
if _crate.multiple then
_crateRadioMsg = "* " .. _crateRadioMsg
end
local _menuEntry = { text = _crateRadioMsg, crate = _crate }
--ctld.logTrace("_menuEntry = [%s]", ctld.p(_menuEntry))
table.insert(menuEntries, _menuEntry)
end end
end end
end end
for _i, _menu in ipairs(menuEntries) do end
--ctld.logTrace("_menu = [%s]", ctld.p(_menu)) for _i, _menu in ipairs(menuEntries) do
-- add the submenu item --ctld.logTrace("_menu = [%s]", ctld.p(_menu))
itemNbSubmenu = itemNbSubmenu + 1 -- add the submenu item
if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached itemNbSubmenu = itemNbSubmenu + 1
_subMenuPath = missionCommands.addSubMenuForGroup(_groupId, if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached
ctld.i18n_translate("Next page"), _subMenuPath) _subMenuPath = missionCommands.addSubMenuForGroup(_groupId,
itemNbSubmenu = 1 ctld.i18n_translate("Next page"), _subMenuPath)
end itemNbSubmenu = 1
missionCommands.addCommandForGroup(_groupId, _menu.text, _subMenuPath,
ctld.spawnCrate, { _unitName, _menu.crate.weight })
end end
missionCommands.addCommandForGroup(_groupId, _menu.text, _subMenuPath,
ctld.spawnCrate, { _unitName, _menu.crate.weight })
end end
end end
end end
if (ctld.enabledFOBBuilding or ctld.enableCrates) and _unitActions.crates then
local _crateCommands = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("CTLD Commands"), _rootPath)
if ctld.vehicleCommandsPath[_unitName] == nil then
ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_crateCommands)
end
if ctld.hoverPickup == false or ctld.loadCrateFromMenu == true then
if ctld.loadCrateFromMenu then
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load Nearby Crate(s)"),
_crateCommands, ctld.loadNearbyCrate, _unitName)
end
end
if ctld.loadCrateFromMenu or ctld.hoverPickup then
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Crate(s)"),
_crateCommands, ctld.dropSlingCrate, { _unitName })
end
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unpack Any Crate"),
_crateCommands, ctld.unpackCrates, { _unitName })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Nearby Crates"),
_crateCommands, ctld.listNearbyCrates, { _unitName })
if ctld.enabledFOBBuilding then
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List FOBs"), _crateCommands,
ctld.listFOBS, { _unitName })
end
if ctld.enableRepackingVehicles == true then
ctld.updateRepackMenu( _unitName ) -- add repack menu
end
end
if ctld.enableSmokeDrop then
local _smokeMenu = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Smoke Markers"), _rootPath)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Red Smoke"), _smokeMenu,
ctld.dropSmoke, { _unitName, trigger.smokeColor.Red })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Blue Smoke"), _smokeMenu,
ctld.dropSmoke, { _unitName, trigger.smokeColor.Blue })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Orange Smoke"), _smokeMenu,
ctld.dropSmoke, { _unitName, trigger.smokeColor.Orange })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Green Smoke"), _smokeMenu,
ctld.dropSmoke, { _unitName, trigger.smokeColor.Green })
end
if ctld.enabledRadioBeaconDrop then
local _radioCommands = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Radio Beacons"), _rootPath)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Beacons"), _radioCommands,
ctld.listRadioBeacons, { _unitName })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Beacon"), _radioCommands,
ctld.dropRadioBeacon, { _unitName })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Remove Closest Beacon"),
_radioCommands, ctld.removeRadioBeacon, { _unitName })
elseif ctld.deployedRadioBeacons ~= {} then
local _radioCommands = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Radio Beacons"), _rootPath)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Beacons"), _radioCommands,
ctld.listRadioBeacons, { _unitName })
end
ctld.addedTo[tostring(_groupId)] = true
ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo))
ctld.logTrace("done adding CTLD menu for _groupId = %s", ctld.p(_groupId))
end end
if (ctld.enabledFOBBuilding or ctld.enableCrates) and _unitActions.crates then
local _crateCommands = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("CTLD Commands"), _rootPath)
if ctld.vehicleCommandsPath[_unitName] == nil then
ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_crateCommands)
end
if ctld.hoverPickup == false or ctld.loadCrateFromMenu == true then
if ctld.loadCrateFromMenu then
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load Nearby Crate(s)"),
_crateCommands, ctld.loadNearbyCrate, _unitName)
end
end
if ctld.loadCrateFromMenu or ctld.hoverPickup then
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Crate(s)"),
_crateCommands, ctld.dropSlingCrate, { _unitName })
end
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unpack Any Crate"),
_crateCommands, ctld.unpackCrates, { _unitName })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Nearby Crates"),
_crateCommands, ctld.listNearbyCrates, { _unitName })
if ctld.enabledFOBBuilding then
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List FOBs"), _crateCommands,
ctld.listFOBS, { _unitName })
end
if ctld.enableRepackingVehicles == true then
ctld.updateRepackMenu( _unitName ) -- add repack menu
end
end
if ctld.enableSmokeDrop then
local _smokeMenu = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Smoke Markers"), _rootPath)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Red Smoke"), _smokeMenu,
ctld.dropSmoke, { _unitName, trigger.smokeColor.Red })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Blue Smoke"), _smokeMenu,
ctld.dropSmoke, { _unitName, trigger.smokeColor.Blue })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Orange Smoke"), _smokeMenu,
ctld.dropSmoke, { _unitName, trigger.smokeColor.Orange })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Green Smoke"), _smokeMenu,
ctld.dropSmoke, { _unitName, trigger.smokeColor.Green })
end
if ctld.enabledRadioBeaconDrop then
local _radioCommands = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Radio Beacons"), _rootPath)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Beacons"), _radioCommands,
ctld.listRadioBeacons, { _unitName })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Drop Beacon"), _radioCommands,
ctld.dropRadioBeacon, { _unitName })
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Remove Closest Beacon"),
_radioCommands, ctld.removeRadioBeacon, { _unitName })
elseif ctld.deployedRadioBeacons ~= {} then
local _radioCommands = missionCommands.addSubMenuForGroup(_groupId,
ctld.i18n_translate("Radio Beacons"), _rootPath)
missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Beacons"), _radioCommands,
ctld.listRadioBeacons, { _unitName })
end
ctld.addedTo[tostring(_groupId)] = true
ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo))
ctld.logTrace("done adding CTLD menu for _groupId = %s", ctld.p(_groupId))
end end
end end
end)
if (not status) then
ctld.logError(string.format("Error adding f10 to transport: %s", error))
end end
end end
@@ -7773,7 +7770,7 @@ function ctld.TreatOrbitJTAC(params, t)
if ctld.JTACInRoute[k] == nil and ctld.OrbitInUse[k] == nil then -- if JTAC is in route if ctld.JTACInRoute[k] == nil and ctld.OrbitInUse[k] == nil then -- if JTAC is in route
ctld.JTACInRoute[k] = timer.getTime() -- update time of the last run ctld.JTACInRoute[k] = timer.getTime() -- update time of the last run
end end
if ctld.jtacCurrentTargets[k] ~= nil then -- if target lased by JTAC if ctld.jtacCurrentTargets[k] ~= nil then -- if target lased by JTAC
local droneAlti = Unit.getByName(k):getPoint().y local droneAlti = Unit.getByName(k):getPoint().y
if ctld.OrbitInUse[k] == nil then -- if JTAC is not in orbit => start orbiting and update start time if ctld.OrbitInUse[k] == nil then -- if JTAC is not in orbit => start orbiting and update start time
@@ -7784,7 +7781,7 @@ function ctld.TreatOrbitJTAC(params, t)
if timer.getTime() > (ctld.OrbitInUse[k] + 60) then -- each 60" update orbit coord if timer.getTime() > (ctld.OrbitInUse[k] + 60) then -- each 60" update orbit coord
ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, droneAlti, 100) -- do orbit JTAC ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, droneAlti, 100) -- do orbit JTAC
ctld.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run ctld.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run
end end
end end
else -- if JTAC have no target else -- if JTAC have no target
if ctld.InOrbitList(k) == true then -- JTAC orbiting, without target => stop orbit if ctld.InOrbitList(k) == true then -- JTAC orbiting, without target => stop orbit
@@ -7802,12 +7799,12 @@ end
-- Make orbit the group "_grpName", on target "_unitTargetName". _alti in meters, speed in km/h -- Make orbit the group "_grpName", on target "_unitTargetName". _alti in meters, speed in km/h
function ctld.StartOrbitGroup(_jtacUnitName, _unitTargetName, _alti, _speed) function ctld.StartOrbitGroup(_jtacUnitName, _unitTargetName, _alti, _speed)
if (Unit.getByName(_unitTargetName) ~= nil) and (Unit.getByName(_jtacUnitName) ~= nil) then -- si target unit and JTAC group exist if (Unit.getByName(_unitTargetName) ~= nil) and (Unit.getByName(_jtacUnitName) ~= nil) then -- si target unit and JTAC group exist
local orbit = { id = 'Orbit', local orbit = { id = 'Orbit',
params = {pattern = 'Circle', params = {pattern = 'Circle',
point = mist.utils.makeVec2(mist.getAvgPos(mist.makeUnitTable({_unitTargetName}))), point = mist.utils.makeVec2(mist.getAvgPos(mist.makeUnitTable({_unitTargetName}))),
speed = _speed, speed = _speed,
altitude = _alti altitude = _alti
} }
} }
local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName()
Unit.getByName(_jtacUnitName):getController():popTask() -- stop current Task Unit.getByName(_jtacUnitName):getController():popTask() -- stop current Task
@@ -7818,10 +7815,10 @@ end
-- test if one unitName already is targeted by a JTAC -- test if one unitName already is targeted by a JTAC
function ctld.InOrbitList(_grpName) function ctld.InOrbitList(_grpName)
for k, v in pairs(ctld.OrbitInUse) do -- for each orbit in use for k, v in pairs(ctld.OrbitInUse) do -- for each orbit in use
if k == _grpName then if k == _grpName then
return true return true
end end
end end
return false return false
end end
------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------
@@ -7853,10 +7850,10 @@ function ctld.backToRoute(_jtacUnitName)
local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName()
--local JTACRoute = mist.getGroupRoute(jtacGroupName, true) -- get the initial editor route of the current group --local JTACRoute = mist.getGroupRoute(jtacGroupName, true) -- get the initial editor route of the current group
local JTACRoute = mist.utils.deepCopy(mist.getGroupRoute(jtacGroupName, true)) -- get the initial editor route of the current group local JTACRoute = mist.utils.deepCopy(mist.getGroupRoute(jtacGroupName, true)) -- get the initial editor route of the current group
local newJTACRoute = ctld.adjustRoute(JTACRoute, ctld.getNearestWP(_jtacUnitName)) local newJTACRoute = ctld.adjustRoute(JTACRoute, ctld.getNearestWP(_jtacUnitName))
local Mission = {} local Mission = {}
Mission = { id = 'Mission', params = {route = {points = newJTACRoute}}} Mission = { id = 'Mission', params = {route = {points = newJTACRoute}}}
-- unactive orbit mode if it's on -- unactive orbit mode if it's on
if ctld.InOrbitList(_jtacUnitName) == true then -- if JTAC orbiting => stop it if ctld.InOrbitList(_jtacUnitName) == true then -- if JTAC orbiting => stop it
@@ -7887,26 +7884,56 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r
-- apply offset (_firstWpOfNewRoute) to SwitchWaypoint tasks -- apply offset (_firstWpOfNewRoute) to SwitchWaypoint tasks
local lastWpAsAlreadySwitchWaypoint = false local lastWpAsAlreadySwitchWaypoint = false
for idx = 1, #adjustedRoute do for idx2 = 1, #adjustedRoute do
for j=1, #adjustedRoute[idx].task.params.tasks do if #adjustedRoute[idx2] and
if adjustedRoute[idx].task.params.tasks[j].id ~= "ControlledTask" then #adjustedRoute[idx2].task and
if adjustedRoute[idx].task.params.tasks[j].params.action.id == "SwitchWaypoint" then #adjustedRoute[idx2].task.params and
local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex #adjustedRoute[idx2].task.params.tasks then
adjustedRoute[idx].task.params.tasks[j].params.action.params.fromWaypointIndex = idx
adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] for j=1, #adjustedRoute[idx2].task.params.tasks do
if idx == #adjustedRoute then if adjustedRoute[idx2].task.params.tasks[j].id and
lastWpAsAlreadySwitchWaypoint = true adjustedRoute[idx2].task.params.tasks[j].id ~= "ControlledTask" then
if adjustedRoute[idx2].task.params.tasks[j].params and
adjustedRoute[idx2].task.params.tasks[j].params.action and
adjustedRoute[idx2].task.params.tasks[j].params.action.id and
adjustedRoute[idx2].task.params.tasks[j].params.action.id == "SwitchWaypoint" then
if adjustedRoute[idx2].task.params.tasks[j].params.action.params then
local goToWaypointIndex = adjustedRoute[idx2].task.params.tasks[j].params.action.params.goToWaypointIndex
adjustedRoute[idx2].task.params.tasks[j].params.action.params.fromWaypointIndex = idx2
adjustedRoute[idx2].task.params.tasks[j].params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex]
if idx2 == #adjustedRoute then
lastWpAsAlreadySwitchWaypoint = true
end
end
end end
end
else -- for "ControlledTask" else -- for "ControlledTask"
if adjustedRoute[idx].task.params.tasks[j].params.task.params.action.id == "SwitchWaypoint" then if adjustedRoute[idx2].task.params.tasks[j].params and
local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex adjustedRoute[idx2].task.params.tasks[j].params.task and
adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.fromWaypointIndex = idx adjustedRoute[idx2].task.params.tasks[j].params.task.params and
adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] adjustedRoute[idx2].task.params.tasks[j].params.task.params.action and
if idx == #adjustedRoute then adjustedRoute[idx2].task.params.tasks[j].params.task.params.action.id and
lastWpAsAlreadySwitchWaypoint = true adjustedRoute[idx2].task.params.tasks[j].params.task.params.action.id == "SwitchWaypoint" then
if adjustedRoute[idx2].task.params.tasks[j].params.task.params.action.params then
local goToWaypointIndex = adjustedRoute[idx2].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex
adjustedRoute[idx2].task.params.tasks[j].params.task.params.action.params.fromWaypointIndex = idx2
adjustedRoute[idx2].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex]
if idx2 == #adjustedRoute then
lastWpAsAlreadySwitchWaypoint = true
end
end
end end
end end
end end
end end
end end
@@ -7957,7 +7984,7 @@ ctld.reconMenuName = ctld.i18n_translate("RECON") --name of the
ctld.reconRadioAdded = {} --stores the groups that have had the radio menu added ctld.reconRadioAdded = {} --stores the groups that have had the radio menu added
ctld.reconLosSearchRadius = 2000 -- search radius in meters ctld.reconLosSearchRadius = 2000 -- search radius in meters
ctld.reconLosMarkRadius = 100 -- mark radius dimension in meters ctld.reconLosMarkRadius = 100 -- mark radius dimension in meters
ctld.reconAutoRefreshLosTargetMarks = true -- if true recon LOS marks are automaticaly refreshed on F10 map ctld.reconAutoRefreshLosTargetMarks = false -- if true recon LOS marks are automaticaly refreshed on F10 map
ctld.reconLastScheduleIdAutoRefresh = 0 ctld.reconLastScheduleIdAutoRefresh = 0
---- F10 RECON Menus ------------------------------------------------------------------ ---- F10 RECON Menus ------------------------------------------------------------------
@@ -8465,7 +8492,7 @@ end
--- Handle world events. --- Handle world events.
ctld.eventHandler = {} ctld.eventHandler = {}
function ctld.eventHandler:onEvent(event) function ctld.eventHandler:onEvent(event)
ctld.logTrace("ctld.eventHandler:onEvent()") --ctld.logTrace("ctld.eventHandler:onEvent()")
if event == nil then if event == nil then
ctld.logError("Event handler was called with a nil event!") ctld.logError("Event handler was called with a nil event!")
return return
@@ -8478,7 +8505,7 @@ function ctld.eventHandler:onEvent(event)
elseif event.id == world.event.S_EVENT_BIRTH then elseif event.id == world.event.S_EVENT_BIRTH then
eventName = "S_EVENT_BIRTH" eventName = "S_EVENT_BIRTH"
else else
ctld.logTrace("Ignoring event %s", ctld.p(event)) --ctld.logTrace("Ignoring event %s", ctld.p(event))
return return
end end
ctld.logDebug("caught event %s: %s", ctld.p(eventName), ctld.p(event)) ctld.logDebug("caught event %s: %s", ctld.p(eventName), ctld.p(event))
+17 -11
View File
@@ -11,6 +11,8 @@ It's open-source and free as in free beer (you don't have to pay to use it), and
We're always looking for help, please reach out to [Zip on Discord](https://discordapp.com/users/421317390807203850) if you want to participate in maintenance or development. We're always looking for help, please reach out to [Zip on Discord](https://discordapp.com/users/421317390807203850) if you want to participate in maintenance or development.
And if you'd like to support our work, you can [buy me a coffee](https://coff.ee/veaf_zip)!
## Contents ## Contents
This script is a rewrite of some of the functionality of the original Complete Combat Troop Transport Script (CTTS) by Geloxo (http://forums.eagle.ru/showthread.php?t=108523), as well as adding new features. This script is a rewrite of some of the functionality of the original Complete Combat Troop Transport Script (CTTS) by Geloxo (http://forums.eagle.ru/showthread.php?t=108523), as well as adding new features.
@@ -865,10 +867,11 @@ For example, if the laser code is *1688*, the frequency will be *40.40Mhz*.
JTAC frequency is available through the "JTAC Status" radio menu JTAC frequency is available through the "JTAC Status" radio menu
#### Jtac-automatic-orbiting-over-lased-target #### Jtac-automatic-orbiting-over-lased-target
By setting parameter ctld.enableAutoOrbitingFlyingJtacOnTarget = true, a script
dedicated script puts in orbit each flying JTAC over his detected target. By setting parameter ctld.enableAutoOrbitingFlyingJtacOnTarget = true, a script dedicated script puts in orbit each flying JTAC over his detected target.
Associated with CTLD/JTAC functions, you can assign a fly route to the JTAC drone,
this one follow it, and start orbiting when he detects a target. Associated with CTLD/JTAC functions, you can assign a fly route to the JTAC drone, this one follow it, and start orbiting when he detects a target.
As soon as it don't detect a target, it restart following its initial route at the nearest waypoint As soon as it don't detect a target, it restart following its initial route at the nearest waypoint
# In Game # In Game
@@ -927,11 +930,13 @@ ctld.JTAC_WEIGHT = 15 -- kg
``` ```
## Limit troop Loading ## Limit troop Loading
The number of Infantries units in mission can be limited by setting the table below : The number of Infantries units in mission can be limited by setting the table below :
ctld.nbLimitSpwanedTroops = {0, 0} -- {redLimitInfantryCount, blueLimitInfantryCount}
`ctld.nbLimitSpawnedTroops = {0, 0} -- {redLimitInfantryCount, blueLimitInfantryCount}`
When this cumulative number of troops is reached for a coalition, no more troops can be loaded onboard, and a message is sent to player. When this cumulative number of troops is reached for a coalition, no more troops can be loaded onboard, and a message is sent to player.
If ctld.nbLimitSpwanedTroops = {0, 0} (both values at 0) the limit control is disabled. If set to `ctld.nbLimitSpawnedTroops = {0, 0}` (both values at 0) the limit control is disabled. This is the default.
If either value is non-zero, limit control becomes active for both coalitions. If either value is non-zero, limit control becomes active for both coalitions.
## Cargo Spawning and Sling Loading ## Cargo Spawning and Sling Loading
@@ -1024,13 +1029,14 @@ Rearming:
You can also repair a partially destroyed HAWK / BUK or KUB system by dropping a repair crate next to it and unpacking. A repair crate will also re-arm the system. You can also repair a partially destroyed HAWK / BUK or KUB system by dropping a repair crate next to it and unpacking. A repair crate will also re-arm the system.
## Crate Repacking ## Crate Repacking
The F10 menu allows you to repack units having associated crate types in the "ctld.spawnableCrates" table.
Simply land near the unit you wish to repack and select it from the list presented by the "CTLD//Vehicle/FOB transport...//Repack Vehicles" menu.
The defined radius of vehicles detection is specified by the parameter
ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units in meters The F10 menu allows you to repack units having associated crate types in the "ctld.spawnableCrates" table.
Simply land near the unit you wish to repack and select it from the list presented by the "CTLD//Vehicle/FOB transport...//Repack Vehicles" menu.
The defined radius of vehicles detection is specified by the parameter `ctld.maximumDistanceRepackableUnitsSearch` (default 200 meters).
WARNING: Due to technical reasons related to the refresh time of the F10 menus, there may be inconsistencies between the type of vehicles requested and those provided. It is recommended to wait 5 to 10 seconds without moving after landing and opening the F10 menu for a packaging order. *WARNING*: Due to technical reasons related to the refresh time of the F10 menus, there may be inconsistencies between the type of vehicles requested and those provided. It is recommended to wait 5 to 10 seconds without moving after landing and opening the F10 menu for a packaging order.
## Forward Operating Base (FOB) Construction ## Forward Operating Base (FOB) Construction
FOBs can be built by loading special FOB crates from a **Logistics** unit into a C-130 or other large aircraft configured in the script. To load the crate use the F10 - Troop Commands Menu. The idea behind FOBs is to make player vs player missions even more dynamic as these can be deployed in most locations. Once destroyed the FOB can no longer be used. FOBs can be built by loading special FOB crates from a **Logistics** unit into a C-130 or other large aircraft configured in the script. To load the crate use the F10 - Troop Commands Menu. The idea behind FOBs is to make player vs player missions even more dynamic as these can be deployed in most locations. Once destroyed the FOB can no longer be used.