From 9c2f982d03cb4f67ad0cf0d4fecc5a7d91c000da Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Fri, 21 Feb 2025 23:48:02 +0100 Subject: [PATCH 001/166] ctld.getNearbyUnits --- CTLD.lua | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index c821cd0..e8203dc 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1958,6 +1958,44 @@ function ctld.spawnCrateAtPoint(_side, _weight, _point,_hdg) end +function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) + local _unit = ctld.getTransportUnit(_PlayerTransportUnitName) + + if _unit == nil then + return + end + + local _point = _unit:getPoint() + local _units = {} + local _unitList = ctld.getNearbyUnits(_point, _radius) + + for _, _nearbyUnit in pairs(_unitList) do + if _nearbyUnit:isActive() then + table.insert(_units, _nearbyUnit) + end + end + + return _units + +end + +function ctld.getNearbyUnits(_point, _radius) + local _units = {} + local _unitList = mist.DBs.unitsByName + for k, _unit in pairs(mist.DBs.unitsByName) do + local u = Unit.getByName(k) + if u and u:isActive() then + --local _dist = ctld.getDistance(u:getPoint(), _point) + local _dist = mist.utils.get2DDist(u:getPoin(), _point) + if _dist <= _radius then + table.insert(_units, u) + end + end + end + return _units +end + + -- *************************************************************** -- **************** BE CAREFUL BELOW HERE ************************ -- *************************************************************** @@ -2161,16 +2199,14 @@ ctld.getNextGroupId = function() end function ctld.getTransportUnit(_unitName) - if _unitName == nil then return nil end - local _heli = Unit.getByName(_unitName) + local transportUnitObject = Unit.getByName(_unitName) - if _heli ~= nil and _heli:isActive() and _heli:getLife() > 0 then - - return _heli + if _hetransportUnitObjectli ~= nil and transportUnitObject:isActive() and transportUnitObject:getLife() > 0 then + return transportUnitObject end return nil From 6beaedb79a4f86e3778d9e21c24c1cbfd24ef8da Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 22 Feb 2025 04:35:25 +0100 Subject: [PATCH 002/166] ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) added --- CTLD.lua | 80 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 13 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index e8203dc..070f0b7 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -409,6 +409,8 @@ ctld.slingLoad = false -- if false, crates can be used WITHOUT slingloading, by ctld.enableSmokeDrop = true -- if false, helis and c-130 will not be able to drop smoke ctld.maxExtractDistance = 125 -- max distance from vehicle to troops to allow a group extraction ctld.maximumDistanceLogistic = 200 -- max distance from vehicle to logistics to allow a loading or spawning operation +ctld.enableRepackingVehicles = true -- if true, vehicles can be repacked into crates +ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units ctld.maximumSearchDistance = 4000 -- max distance for troops to search for enemy ctld.maximumMoveDistance = 2000 -- max distance for troops to move from drop point if no enemy is nearby ctld.minimumDeployDistance = 1000 -- minimum distance from a friendly pickup zone where you can deploy a crate @@ -1958,25 +1960,67 @@ function ctld.spawnCrateAtPoint(_side, _weight, _point,_hdg) end +--ctld.maximumDistanceRepackableUnitsSearch = 200 function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) - local _unit = ctld.getTransportUnit(_PlayerTransportUnitName) - - if _unit == nil then + local unit = ctld.getTransportUnit(_PlayerTransportUnitName) + if unit == nil then return end - local _point = _unit:getPoint() - local _units = {} - local _unitList = ctld.getNearbyUnits(_point, _radius) - - for _, _nearbyUnit in pairs(_unitList) do - if _nearbyUnit:isActive() then - table.insert(_units, _nearbyUnit) + local point = unit:getPoint() + local unitList = ctld.getNearbyUnits(point, _radius) + local repackableUnits = {} + for i=1, #unitList do + local repackableUnit = isRepackableUnit(unitList[i]) + if repackableUnit then + repackableUnit["repackableUnitGroupID"] = unitList[i]:getGroup():getID() + table.insert(repackableUnits, mist.utils.deepCopy(repackableUnit)) end end + return repackableUnits +end - return _units +function isRepackableUnit(_unitID) + local unitType = _unitID:getTypeName() + for k,v in pairs(ctld.spawnableCrates) do + for i=1, #ctld.spawnableCrates[k] do + if _unitID then + if ctld.spawnableCrates[k][i].unit == unitType then + return ctld.spawnableCrates[k][i] + end + end + end + end + return nil +end + +--[[ + { + "cratesRequired": 2, + "desc": "Humvee - TOW", + "repackableUnitGroupID": 15, + "side": 2, + "unit": "M1045 HMMWV TOW", + "weight": 1000.02 + } + +]]-- +function ctld.repackVehicle(_repackableUnit, _PlayerTransportUnitName) + local TransportUnit = ctld.getTransportUnit(_PlayerTransportUnitName) + local spawnRefPoint = TransportUnit:getPoint() + if _repackableUnit then + --ici calculer le heading des spwan à effectuer + for i=1, _repackableUnit.cratesRequired do + local _point = mist.utils.makeVec3GL(spawnRefPoint.x+(i*5), spawnRefPoint.z, spawnRefPoint.y) + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s #%i", _repackableUnit.desc, _unitId) + ctld.spawnCrateStatic(TransportUnit:getCountry(), _unitId, _point, _name, _repackableUnit.weight, TransportUnit:getCoalition(), TransportUnit:getHeading()) + end + + _repackableUnit.repackableUnitGroupID:destroy() -- destroy unit repacked + return + end end function ctld.getNearbyUnits(_point, _radius) @@ -1986,7 +2030,7 @@ function ctld.getNearbyUnits(_point, _radius) local u = Unit.getByName(k) if u and u:isActive() then --local _dist = ctld.getDistance(u:getPoint(), _point) - local _dist = mist.utils.get2DDist(u:getPoin(), _point) + local _dist = mist.utils.get2DDist(u:getPoint(), _point) if _dist <= _radius then table.insert(_units, u) end @@ -5832,8 +5876,18 @@ function ctld.addTransportF10MenuOptions(_unitName) 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 + if ctld.enableRepackingVehicles then + --missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Repack Vehicle"), _vehicleCommandsPath, ctld.repackVehicle, { _unitName }) + local _RepackCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _rootPath) + local repackableVehicles = ctld.getUnitsInRepackRadius(_unitName, ctld.maximumDistanceRepackableUnitsSearch) + if repackableVehicles then + for _, _vehicle in pairs(repackableVehicles) do + missionCommands.addCommandForGroup(_groupId, _vehicle.repackableUnitGroupID:getName(), _RepackCommandsPath, ctld.repackVehicle, { _vehicle, _unitName }) + end + end + end + 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 }) From 665bdddbddbfdce81e95f2d828fdc83019a6d3ad Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Feb 2025 03:34:05 +0100 Subject: [PATCH 003/166] wip --- CTLD.lua | 127 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 37 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 070f0b7..7b1762e 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -41,8 +41,8 @@ ctld.Id = "CTLD - " ctld.Version = "1.4.0" -- To add debugging messages to dcs.log, change the following log levels to `true`; `Debug` is less detailed than `Trace` -ctld.Debug = false -ctld.Trace = false +ctld.Debug = true +ctld.Trace = true ctld.dontInitialize = false -- if true, ctld.initialize() will not run; instead, you'll have to run it from your own code - it's useful when you want to override some functions/parameters before the initialization takes place @@ -793,6 +793,7 @@ ctld.logisticUnits = { ctld.vehicleTransportEnabled = { "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR "Hercules", + "UH-1H", } -- ************** Units able to use DCS dynamic cargo system ****************** @@ -1960,7 +1961,10 @@ function ctld.spawnCrateAtPoint(_side, _weight, _point,_hdg) end ---ctld.maximumDistanceRepackableUnitsSearch = 200 +-- *************************************************************** +-- Repack vehicules crates functions +-- *************************************************************** +-- ctld.maximumDistanceRepackableUnitsSearch = 200 function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) local unit = ctld.getTransportUnit(_PlayerTransportUnitName) if unit == nil then @@ -1979,7 +1983,7 @@ function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) end return repackableUnits end - +-- *************************************************************** function isRepackableUnit(_unitID) local unitType = _unitID:getTypeName() for k,v in pairs(ctld.spawnableCrates) do @@ -1993,9 +1997,8 @@ function isRepackableUnit(_unitID) end return nil end - - ---[[ +-- *************************************************************** +--[[ _repackableUnit: { "cratesRequired": 2, "desc": "Humvee - TOW", @@ -2005,17 +2008,25 @@ end "weight": 1000.02 } + table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._loadGroup.name, + groupId = _groupId, + RepackCommandsPath = _RepackCommandsPath, + menufunction = ctld.repackVehicle, + menuArgsTable = { _vehicle, _unitName }}) + ]]-- function ctld.repackVehicle(_repackableUnit, _PlayerTransportUnitName) +ctld.logTrace(" ctld.repackVehicle._repackableUnit = %s", ctld.p(mist.utils.tableShow(_repackableUnit))) +trigger.action.outText("_PlayerTransportUnitName = ".._PlayerTransportUnitName, 10) local TransportUnit = ctld.getTransportUnit(_PlayerTransportUnitName) - local spawnRefPoint = TransportUnit:getPoint() + local spawnRefPoint = _PlayerTransportUnitName:getPoint() if _repackableUnit then --ici calculer le heading des spwan à effectuer for i=1, _repackableUnit.cratesRequired do local _point = mist.utils.makeVec3GL(spawnRefPoint.x+(i*5), spawnRefPoint.z, spawnRefPoint.y) local _unitId = ctld.getNextUnitId() local _name = string.format("%s #%i", _repackableUnit.desc, _unitId) - ctld.spawnCrateStatic(TransportUnit:getCountry(), _unitId, _point, _name, _repackableUnit.weight, TransportUnit:getCoalition(), TransportUnit:getHeading()) + ctld.spawnCrateStatic(_PlayerTransportUnitName:getCountry(), _unitId, _point, _name, _repackableUnit.weight, _PlayerTransportUnitName:getCoalition(), _PlayerTransportUnitName:getHeading()) end _repackableUnit.repackableUnitGroupID:destroy() -- destroy unit repacked @@ -2249,7 +2260,7 @@ function ctld.getTransportUnit(_unitName) local transportUnitObject = Unit.getByName(_unitName) - if _hetransportUnitObjectli ~= nil and transportUnitObject:isActive() and transportUnitObject:getLife() > 0 then + if transportUnitObject ~= nil and transportUnitObject:isActive() and transportUnitObject:getLife() > 0 then return transportUnitObject end @@ -5819,33 +5830,26 @@ end -- Adds menuitem to a human unit function ctld.addTransportF10MenuOptions(_unitName) ctld.logDebug("ctld.addTransportF10MenuOptions(_unitName=[%s])", ctld.p(_unitName)) - local status, error = pcall(function() local _unit = ctld.getTransportUnit(_unitName) ctld.logTrace("_unit = %s", ctld.p(_unit)) - if _unit then + + if _unit then local _unitTypename = _unit:getTypeName() local _groupId = ctld.getGroupId(_unit) - if _groupId then ctld.logTrace("_groupId = %s", ctld.p(_groupId)) ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) if ctld.addedTo[tostring(_groupId)] == nil then ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) - local _unitActions = ctld.getUnitActions(_unitTypename) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _rootPath, ctld.checkTroopStatus, { _unitName }) - if _unitActions.troops then - local _troopCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Troop Transport"), _rootPath) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload / Extract Troops"), _troopCommandsPath, ctld.unloadExtractTroops, { _unitName }) - -- local _loadPath = missionCommands.addSubMenuForGroup(_groupId, "Load From Zone", _troopCommandsPath) local _transportLimit = ctld.getTransportLimit(_unitTypename) local itemNb = 0 @@ -5870,20 +5874,34 @@ function ctld.addTransportF10MenuOptions(_unitName) 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.enableRepackingVehicles then --missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Repack Vehicle"), _vehicleCommandsPath, ctld.repackVehicle, { _unitName }) - local _RepackCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _rootPath) + --local _RepackCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath) + + ctld.logTrace("FG_ menuEntries = %s", ctld.p(mist.utils.tableShow(menuEntries))) local repackableVehicles = ctld.getUnitsInRepackRadius(_unitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then + local menuEntries = {} + local RepackCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) + RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") for _, _vehicle in pairs(repackableVehicles) do - missionCommands.addCommandForGroup(_groupId, _vehicle.repackableUnitGroupID:getName(), _RepackCommandsPath, ctld.repackVehicle, { _vehicle, _unitName }) + --missionCommands.addCommandForGroup(_groupId, _vehicle.repackableUnitGroupID:getName(), _RepackCommandsPath, ctld.repackVehicle, { _vehicle, _unitName }) + -- table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, + -- groupId = _groupId, + -- subMenuPath = RepackCommandsPath, + -- menufunction = ctld.repackVehicle, + -- menuArgsTable = {repackableVehicles, _unitName} }) + menuEntries[#menuEntries+1] = { text = ctld.i18n_translate("repack ").._vehicle.unit, + groupId = _groupId, + subMenuPath = RepackCommandsPath, + menufunction = ctld.repackVehicle, + menuArgsTable = {repackableVehicles, _unitName} } end + local RepackCommandsPath = missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath, ctld.buildPaginatedMenu, menuEntries) +ctld.logTrace("FG_ menuEntries = %s", ctld.p(mist.utils.tableShow(menuEntries))) end end @@ -5892,7 +5910,6 @@ function ctld.addTransportF10MenuOptions(_unitName) end missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _vehicleCommandsPath, ctld.checkTroopStatus, { _unitName }) end - end if ctld.enableCrates and _unitActions.crates then @@ -6007,7 +6024,45 @@ function ctld.addTransportF10MenuOptions(_unitName) ctld.logError(string.format("Error adding f10 to transport: %s", error)) end end - +--[[] +[1]["groupId"] = 1, +[1]["subMenuPath"] = table: 00000135F7959278 { + [1]["subMenuPath"][1] = "CTLD", + [1]["subMenuPath"][2] = "Vehicle / FOB Transport", + [1]["subMenuPath"][3] = "Repack Vehicles", + }, +[1]["text"] = "repack M1045 HMMWV TOW", +[1]["menufunction"] = "function: 0000013692E51F28, defined in (2018-2035)", +[1]["menuArgsTable"] = table: 00000135F7959378 { + [1]["menuArgsTable"][1] = table: 00000135F79590F8 { + [1]["menuArgsTable"][1][1] = table: 00000135F79591B8 { + [1]["menuArgsTable"][1][1]["repackableUnitGroupID"] = 3, + [1]["menuArgsTable"][1][1]["side"] = 2, + [1]["menuArgsTable"][1][1]["weight"] = 1000.02, + [1]["menuArgsTable"][1][1]["desc"] = "Humvee - TOW", + [1]["menuArgsTable"][1][1]["cratesRequired"] = 2, + [1]["menuArgsTable"][1][1]["unit"] = "M1045 HMMWV TOW", + }, + }, + [1]["menuArgsTable"][2] = "helicargo1",]]-- +--****************************************************************************************************** +function ctld.buildPaginatedMenu(_menuEntries) + ctld.logTrace("ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) + local itemNbSubmenu = 0 + for i, menu in ipairs(_menuEntries) do + --ctld.logTrace("ctld.buildPaginatedMenu.menu = [%s]", ctld.p(mist.utils.tableShow(menu))) + ctld.logTrace("ctld.buildPaginatedMenu.menu = [%s]", ctld.p(menu)) + -- add the submenu item + itemNbSubmenu = itemNbSubmenu + 1 + if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached + menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) + itemNbSubmenu = 1 + end +ctld.logTrace("ctld.buildPaginatedMenu.type(menu.menuFunction) = [%s]", ctld.p(type(menu.menuFunction))) + missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) + end +end +--****************************************************************************************************** function ctld.addOtherF10MenuOptions() ctld.logDebug("ctld.addOtherF10MenuOptions") -- reschedule every 10 seconds @@ -8019,16 +8074,13 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 5) timer.scheduleFunction(function() - - timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 5) - timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 5) - timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 5) - - if ctld.enableCrates == true and ctld.hoverPickup == true then - timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) - end - - end,nil, timer.getTime()+1 ) + timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 5) + if ctld.enableCrates == true and ctld.hoverPickup == true then + timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) + end + end,nil, timer.getTime()+1 ) --event handler for deaths --world.addEventHandler(ctld.eventHandler) @@ -8098,7 +8150,6 @@ function ctld.initialize() -- register event handler ctld.logInfo("registering event handler") world.addEventHandler(ctld.eventHandler) - env.info("CTLD READY") end @@ -8136,6 +8187,7 @@ function ctld.eventHandler:onEvent(event) local function processHumanPlayer() ctld.logTrace("in the 'processHumanPlayer' function") +trigger.action.outText("1 - in the 'processHumanPlayer' function / unitName = "..unitName, 15) if mist.DBs.humansByName[unitName] then -- it's a human unit ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) local _unit = Unit.getByName(unitName) @@ -8163,6 +8215,7 @@ function ctld.eventHandler:onEvent(event) if _unitName == unitName then ctld.logTrace("adding by transportPilotNames, unitName = %s", ctld.p(unitName)) -- add transport radio menu +trigger.action.outText("2 - in the 'processHumanPlayer' function", 15) ctld.addTransportF10MenuOptions(unitName) break end From fb804b4a22c0d32b73462f7140c945a02d33019b Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Feb 2025 15:05:22 +0100 Subject: [PATCH 004/166] 1rst ok version --- CTLD.lua | 138 ++++++++++++++++++++++++------------------------------- 1 file changed, 60 insertions(+), 78 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 7b1762e..79938ac 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -773,6 +773,7 @@ ctld.extractableGroups = { -- Use any of the predefined names or set your own ones -- When a logistic unit is destroyed, you will no longer be able to spawn crates +ctld.dynamicLogisticUnitsIndex = 0 -- This is the unit that will be spawned first and then subsequent units will be from the next in the list ctld.logisticUnits = { "logistic1", "logistic2", @@ -1581,7 +1582,11 @@ function ctld.countDroppedUnitsInZone(_zone, _blueFlag, _redFlag) -- env.info("Units in zone ".._blueCount.." ".._redCount) end - +--*************************************************************** +function ctld.getNextDynamicLogisticUnitIndex() + ctld.dynamicLogisticUnitsIndex = ctld.dynamicLogisticUnitsIndex + 1 + return ctld.dynamicLogisticUnitsIndex +end -- Creates a radio beacon on a random UHF - VHF and HF/FM frequency for homing -- This WILL NOT WORK if you dont add beacon.ogg and beaconsilent.ogg to the mission!!! @@ -1964,8 +1969,11 @@ end -- *************************************************************** -- Repack vehicules crates functions -- *************************************************************** --- ctld.maximumDistanceRepackableUnitsSearch = 200 function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) + if _radius == nil then + _radius = ctld.maximumDistanceRepackableUnitsSearch + end + local unit = ctld.getTransportUnit(_PlayerTransportUnitName) if unit == nil then return @@ -1975,7 +1983,7 @@ function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) local unitList = ctld.getNearbyUnits(point, _radius) local repackableUnits = {} for i=1, #unitList do - local repackableUnit = isRepackableUnit(unitList[i]) + local repackableUnit = ctld.isRepackableUnit(unitList[i]) if repackableUnit then repackableUnit["repackableUnitGroupID"] = unitList[i]:getGroup():getID() table.insert(repackableUnits, mist.utils.deepCopy(repackableUnit)) @@ -1984,13 +1992,15 @@ function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) return repackableUnits end -- *************************************************************** -function isRepackableUnit(_unitID) - local unitType = _unitID:getTypeName() +function ctld.isRepackableUnit(_unitId) + local unitType = _unitId:getTypeName() for k,v in pairs(ctld.spawnableCrates) do for i=1, #ctld.spawnableCrates[k] do - if _unitID then + if _unitId then if ctld.spawnableCrates[k][i].unit == unitType then - return ctld.spawnableCrates[k][i] + local repackableUnit = mist.utils.deepCopy(ctld.spawnableCrates[k][i]) + repackableUnit["vehicleId"] = _unitId + return repackableUnit end end end @@ -1998,42 +2008,32 @@ function isRepackableUnit(_unitID) return nil end -- *************************************************************** ---[[ _repackableUnit: - { - "cratesRequired": 2, - "desc": "Humvee - TOW", - "repackableUnitGroupID": 15, - "side": 2, - "unit": "M1045 HMMWV TOW", - "weight": 1000.02 - } - - table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._loadGroup.name, - groupId = _groupId, - RepackCommandsPath = _RepackCommandsPath, - menufunction = ctld.repackVehicle, - menuArgsTable = { _vehicle, _unitName }}) - -]]-- -function ctld.repackVehicle(_repackableUnit, _PlayerTransportUnitName) -ctld.logTrace(" ctld.repackVehicle._repackableUnit = %s", ctld.p(mist.utils.tableShow(_repackableUnit))) -trigger.action.outText("_PlayerTransportUnitName = ".._PlayerTransportUnitName, 10) - local TransportUnit = ctld.getTransportUnit(_PlayerTransportUnitName) - local spawnRefPoint = _PlayerTransportUnitName:getPoint() - if _repackableUnit then +function ctld.repackVehicle(_params) + local repackableUnit = _params[1] + local PlayerTransportUnitName = _params[2] + local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) + ctld.logTrace(" ctld.repackVehicle.repackableUnit = %s", ctld.p(mist.utils.tableShow(repackableUnit))) + local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) + local spawnRefPoint = PlayerTransportUnit:getPoint() + local refCountry = PlayerTransportUnit:getCountry() + if repackableUnit then --ici calculer le heading des spwan à effectuer - for i=1, _repackableUnit.cratesRequired do - local _point = mist.utils.makeVec3GL(spawnRefPoint.x+(i*5), spawnRefPoint.z, spawnRefPoint.y) + for i=1, repackableUnit.cratesRequired do + --local _point = mist.utils.makeVec3GL(spawnRefPoint.x+(i*5), spawnRefPoint.z, spawnRefPoint.y) + local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", _repackableUnit.desc, _unitId) - ctld.spawnCrateStatic(_PlayerTransportUnitName:getCountry(), _unitId, _point, _name, _repackableUnit.weight, _PlayerTransportUnitName:getCoalition(), _PlayerTransportUnitName:getHeading()) + local _name = string.format("%s #%i", repackableUnit.desc, _unitId) + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end - - _repackableUnit.repackableUnitGroupID:destroy() -- destroy unit repacked + -- create a temporary logistic unit to be able to repack the vehicle + local dynamicLogisticUnitName = "dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) + ctld.logisticUnits[#ctld.logisticUnits+1] = dynamicLogisticUnitName + ctld.spawnStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, dynamicLogisticUnitName, refCountry) + repackableUnit.vehicleId:destroy() -- destroy repacked unit return end end - +-- *************************************************************** function ctld.getNearbyUnits(_point, _radius) local _units = {} local _unitList = mist.DBs.unitsByName @@ -2302,7 +2302,6 @@ function ctld.spawnCrateStatic(_country, _unitId, _point, _name, _weight, _side, _spawnedCrate = Unit.getByName(_name) else - if _model_type ~= nil then _crate = mist.utils.deepCopy(ctld.spawnableCratesModels[_model_type]) elseif ctld.slingLoad then @@ -2500,7 +2499,23 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) env.error(string.format("CTLD ERROR: %s", _err)) end end - +-- *************************************************************** +function ctld.spawnStaticLogisticUnit(_point, _name, _country) + local LogUnit = { + ["category"] = "Fortifications", + ["shape_name"] = "H-Windsock_RW", + ["type"] = "Windsock", + ["y"] = _point.z, + ["x"] = _point.x, + ["name"] = _name, + ["canCargo"] = false, + ["heading"] = 0, + } + LogUnit["country"] = _country + mist.dynAddStatic(LogUnit) + return StaticObject.getByName(LogUnit["name"]) +end +--*************************************************************** ctld.randomCrateSpacing = 12 -- meters function ctld.getPointAt12Oclock(_unit, _offset) return ctld.getPointAtDirection(_unit, _offset, 0) @@ -5872,39 +5887,28 @@ function ctld.addTransportF10MenuOptions(_unitName) 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.enableRepackingVehicles then - --missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Repack Vehicle"), _vehicleCommandsPath, ctld.repackVehicle, { _unitName }) - --local _RepackCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath) - - ctld.logTrace("FG_ menuEntries = %s", ctld.p(mist.utils.tableShow(menuEntries))) local repackableVehicles = ctld.getUnitsInRepackRadius(_unitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then local menuEntries = {} local RepackCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") for _, _vehicle in pairs(repackableVehicles) do - --missionCommands.addCommandForGroup(_groupId, _vehicle.repackableUnitGroupID:getName(), _RepackCommandsPath, ctld.repackVehicle, { _vehicle, _unitName }) - -- table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, - -- groupId = _groupId, - -- subMenuPath = RepackCommandsPath, - -- menufunction = ctld.repackVehicle, - -- menuArgsTable = {repackableVehicles, _unitName} }) - menuEntries[#menuEntries+1] = { text = ctld.i18n_translate("repack ").._vehicle.unit, + table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, groupId = _groupId, subMenuPath = RepackCommandsPath, - menufunction = ctld.repackVehicle, - menuArgsTable = {repackableVehicles, _unitName} } + menuFunction = ctld.repackVehicle, + menuArgsTable = {_vehicle, _unitName} }) end - local RepackCommandsPath = missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath, ctld.buildPaginatedMenu, menuEntries) + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath) ctld.logTrace("FG_ menuEntries = %s", ctld.p(mist.utils.tableShow(menuEntries))) + ctld.buildPaginatedMenu(menuEntries) end end - if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath, ctld.loadUnloadFOBCrate, { _unitName, false }) end @@ -6024,41 +6028,19 @@ ctld.logTrace("FG_ menuEntries = %s", ctld.p(mist.utils.tableShow(menuEntries)) ctld.logError(string.format("Error adding f10 to transport: %s", error)) end end ---[[] -[1]["groupId"] = 1, -[1]["subMenuPath"] = table: 00000135F7959278 { - [1]["subMenuPath"][1] = "CTLD", - [1]["subMenuPath"][2] = "Vehicle / FOB Transport", - [1]["subMenuPath"][3] = "Repack Vehicles", - }, -[1]["text"] = "repack M1045 HMMWV TOW", -[1]["menufunction"] = "function: 0000013692E51F28, defined in (2018-2035)", -[1]["menuArgsTable"] = table: 00000135F7959378 { - [1]["menuArgsTable"][1] = table: 00000135F79590F8 { - [1]["menuArgsTable"][1][1] = table: 00000135F79591B8 { - [1]["menuArgsTable"][1][1]["repackableUnitGroupID"] = 3, - [1]["menuArgsTable"][1][1]["side"] = 2, - [1]["menuArgsTable"][1][1]["weight"] = 1000.02, - [1]["menuArgsTable"][1][1]["desc"] = "Humvee - TOW", - [1]["menuArgsTable"][1][1]["cratesRequired"] = 2, - [1]["menuArgsTable"][1][1]["unit"] = "M1045 HMMWV TOW", - }, - }, - [1]["menuArgsTable"][2] = "helicargo1",]]-- --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) ctld.logTrace("ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do --ctld.logTrace("ctld.buildPaginatedMenu.menu = [%s]", ctld.p(mist.utils.tableShow(menu))) - ctld.logTrace("ctld.buildPaginatedMenu.menu = [%s]", ctld.p(menu)) +ctld.logTrace("ctld.buildPaginatedMenu. mist.utils.tableShow(menu) = [%s]", mist.utils.tableShow(menu)) -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) itemNbSubmenu = 1 end -ctld.logTrace("ctld.buildPaginatedMenu.type(menu.menuFunction) = [%s]", ctld.p(type(menu.menuFunction))) missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) end end From 3a3bcaf5685c81a4eed3be5fab11de3192e7c38d Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Feb 2025 15:13:25 +0100 Subject: [PATCH 005/166] wip --- CTLD.lua | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 79938ac..8805b27 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2025,10 +2025,8 @@ function ctld.repackVehicle(_params) local _name = string.format("%s #%i", repackableUnit.desc, _unitId) ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end - -- create a temporary logistic unit to be able to repack the vehicle - local dynamicLogisticUnitName = "dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) - ctld.logisticUnits[#ctld.logisticUnits+1] = dynamicLogisticUnitName - ctld.spawnStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, dynamicLogisticUnitName, refCountry) + + ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle repackableUnit.vehicleId:destroy() -- destroy repacked unit return end @@ -2500,14 +2498,16 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) end end -- *************************************************************** -function ctld.spawnStaticLogisticUnit(_point, _name, _country) +function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit to be able to repack the vehicle + local dynamicLogisticUnitName = "dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) + ctld.logisticUnits[#ctld.logisticUnits+1] = dynamicLogisticUnitName local LogUnit = { ["category"] = "Fortifications", ["shape_name"] = "H-Windsock_RW", ["type"] = "Windsock", ["y"] = _point.z, ["x"] = _point.x, - ["name"] = _name, + ["name"] = dynamicLogisticUnitName, ["canCargo"] = false, ["heading"] = 0, } @@ -8169,7 +8169,6 @@ function ctld.eventHandler:onEvent(event) local function processHumanPlayer() ctld.logTrace("in the 'processHumanPlayer' function") -trigger.action.outText("1 - in the 'processHumanPlayer' function / unitName = "..unitName, 15) if mist.DBs.humansByName[unitName] then -- it's a human unit ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) local _unit = Unit.getByName(unitName) @@ -8196,9 +8195,7 @@ trigger.action.outText("1 - in the 'processHumanPlayer' function / unitName = " for _, _unitName in pairs(ctld.transportPilotNames) do if _unitName == unitName then ctld.logTrace("adding by transportPilotNames, unitName = %s", ctld.p(unitName)) - -- add transport radio menu -trigger.action.outText("2 - in the 'processHumanPlayer' function", 15) - ctld.addTransportF10MenuOptions(unitName) + ctld.addTransportF10MenuOptions(unitName) -- add transport radio menu break end end From fb26645996a4366b3f191c73436627c3927fd91f Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Feb 2025 21:27:21 +0100 Subject: [PATCH 006/166] add ctld.updateDynamicLogisticUnitsZones --- CTLD.lua | 69 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 8805b27..163b3b4 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2021,6 +2021,7 @@ function ctld.repackVehicle(_params) for i=1, repackableUnit.cratesRequired do --local _point = mist.utils.makeVec3GL(spawnRefPoint.x+(i*5), spawnRefPoint.z, spawnRefPoint.y) local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} + -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) local _unitId = ctld.getNextUnitId() local _name = string.format("%s #%i", repackableUnit.desc, _unitId) ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) @@ -2047,7 +2048,51 @@ function ctld.getNearbyUnits(_point, _radius) end return _units end - +-- *************************************************************** +function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit to be able to repack the vehicle + local dynamicLogisticUnitName = "%dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) + ctld.logisticUnits[#ctld.logisticUnits+1] = dynamicLogisticUnitName + local LogUnit = { + ["category"] = "Fortifications", + ["shape_name"] = "H-Windsock_RW", + ["type"] = "Windsock", + ["y"] = _point.z, + ["x"] = _point.x, + ["name"] = dynamicLogisticUnitName, + ["canCargo"] = false, + ["heading"] = 0, + } + LogUnit["country"] = _country + mist.dynAddStatic(LogUnit) + return StaticObject.getByName(LogUnit["name"]) +end +-- *************************************************************** +function ctld.updateDynamicLogisticUnitsZones() -- remove Dynamic Logistic Units if no statics units (crates) are in the zone + local _units = {} + for i, logUnit in ipairs(ctld.logisticUnits) do + if string.sub(phrase, 1, 17) == "%dynLogisticName_" then -- check if the unit is a dynamic logistic unit + local unitsInLogisticUnitZone = ctld.getUnitsInLogisticZone(logUnit) + if #unitsInLogisticUnitZone == 0 then + local _logUnit = StaticObject.getByName(logUnit) + if _logUnit then + _logUnit:destroy() -- destroy the dynamic Logistic unit object from map + ctld.logisticUnits[i] = nil -- remove the dynamic Logistic unit from the list + end + end + end + end + return 5 -- reschedule the function in 5 second +end +-- *************************************************************** +function ctld.getUnitsInLogisticZone(_logisticUnitName) + local _unit = StaticObject.getByName(_logisticUnitName) + if _unit then + local _point = _unit:getPoint() + local _unitList = ctld.getNearbyUnits(_point, ctld.maximumDistanceLogistic) + return _unitList + end + return {} +end -- *************************************************************** -- **************** BE CAREFUL BELOW HERE ************************ @@ -2497,24 +2542,6 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) env.error(string.format("CTLD ERROR: %s", _err)) end end --- *************************************************************** -function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit to be able to repack the vehicle - local dynamicLogisticUnitName = "dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) - ctld.logisticUnits[#ctld.logisticUnits+1] = dynamicLogisticUnitName - local LogUnit = { - ["category"] = "Fortifications", - ["shape_name"] = "H-Windsock_RW", - ["type"] = "Windsock", - ["y"] = _point.z, - ["x"] = _point.x, - ["name"] = dynamicLogisticUnitName, - ["canCargo"] = false, - ["heading"] = 0, - } - LogUnit["country"] = _country - mist.dynAddStatic(LogUnit) - return StaticObject.getByName(LogUnit["name"]) -end --*************************************************************** ctld.randomCrateSpacing = 12 -- meters function ctld.getPointAt12Oclock(_unit, _offset) @@ -2533,7 +2560,8 @@ function ctld.getPointAtDirection(_unit, _offset, _directionInRadian) ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) local _position = _unit:getPosition() - local _angle = math.atan2(_position.x.z, _position.x.x) + _directionInRadian + --local _angle = math.atan2(_position.x.z, _position.x.x) + _directionInRadian + local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian local _xOffset = math.cos(_angle) * _offset + _randomOffsetX local _zOffset = math.sin(_angle) * _offset + _randomOffsetZ @@ -8059,6 +8087,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 5) timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 5) timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.updateDynamicLogisticUnitsZones, nil, timer.getTime() + 5) if ctld.enableCrates == true and ctld.hoverPickup == true then timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end From 5aa37c52a46ecb8b4a011a07313288ed2de08bc3 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 22 Mar 2025 21:42:46 +0100 Subject: [PATCH 007/166] fixes --- CTLD.lua | 137 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 163b3b4..ea46dad 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1969,6 +1969,8 @@ end -- *************************************************************** -- Repack vehicules crates functions -- *************************************************************** +ctld.repackRequestStack = {} -- table to store the repack request + function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) if _radius == nil then _radius = ctld.maximumDistanceRepackableUnitsSearch @@ -1979,8 +1981,9 @@ function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) return end - local point = unit:getPoint() - local unitList = ctld.getNearbyUnits(point, _radius) + local playerCoalition = unit:getCoalition() + local point = unit:getPoint() + local unitList = ctld.getNearbyUnits(point, _radius, playerCoalition) local repackableUnits = {} for i=1, #unitList do local repackableUnit = ctld.isRepackableUnit(unitList[i]) @@ -1992,6 +1995,25 @@ function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) return repackableUnits end -- *************************************************************** +function ctld.getNearbyUnits(_point, _radius, _coalition) + if _coalition == nil then + _coalition = 4 -- all coalitions + end + local _units = {} + local _unitList = mist.DBs.unitsByName + for k, _unit in pairs(mist.DBs.unitsByName) do + local u = Unit.getByName(k) + if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then + --local _dist = ctld.getDistance(u:getPoint(), _point) + local _dist = mist.utils.get2DDist(u:getPoint(), _point) + if _dist <= _radius then + table.insert(_units, u) + end + end + end + return _units +end +-- *************************************************************** function ctld.isRepackableUnit(_unitId) local unitType = _unitId:getTypeName() for k,v in pairs(ctld.spawnableCrates) do @@ -2008,7 +2030,64 @@ function ctld.isRepackableUnit(_unitId) return nil end -- *************************************************************** -function ctld.repackVehicle(_params) +function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request + local repackableUnit = _params[1] + local PlayerTransportUnitName = _params[2] + local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) + local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) + local spawnRefPoint = PlayerTransportUnit:getPoint() + local refCountry = PlayerTransportUnit:getCountry() + + ctld.repackRequestsStack[#ctld.repackRequestsStack+1] = _params + + --[[ + if repackableUnit then + local _point = {x = spawnRefPoint.x+5, z = spawnRefPoint.z} + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s #%i", repackableUnit.desc, _unitId) + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) + ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle + return + end ]]-- + +end +-- *************************************************************** +function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' to process each request + if t == nil then + t = timer.getTime() + end + + for ii, v in ipairs(ctld.repackRequestsStack) do + local repackableUnit = v[1] + local PlayerTransportUnitName = v[2] + local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) + ctld.logTrace(" ctld.repackVehicle.repackableUnit = %s", ctld.p(mist.utils.tableShow(repackableUnit))) + local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) + local spawnRefPoint = PlayerTransportUnit:getPoint() + local refCountry = PlayerTransportUnit:getCountry() + if repackableUnit then + if repackableUnit:isExist() then + --ici calculer le heading des spwans à effectuer + for i=1, repackableUnit.cratesRequired do + local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} + -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s #%i", repackableUnit.desc, _unitId) + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) + end + + if ctld.isUnitInALogisticZone(repackableUnit) == nil then + ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle + end + repackableUnit.vehicleId:destroy() -- destroy repacked unit + end + ctld.repackRequestsStack[ii] = nil + end + end + return 3 -- reschedule the function in 3 seconds +end +--[[ *************************************************************** +function ctld.repackVehicle_old(_params) local repackableUnit = _params[1] local PlayerTransportUnitName = _params[2] local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) @@ -2031,23 +2110,7 @@ function ctld.repackVehicle(_params) repackableUnit.vehicleId:destroy() -- destroy repacked unit return end -end --- *************************************************************** -function ctld.getNearbyUnits(_point, _radius) - local _units = {} - local _unitList = mist.DBs.unitsByName - for k, _unit in pairs(mist.DBs.unitsByName) do - local u = Unit.getByName(k) - if u and u:isActive() then - --local _dist = ctld.getDistance(u:getPoint(), _point) - local _dist = mist.utils.get2DDist(u:getPoint(), _point) - if _dist <= _radius then - table.insert(_units, u) - end - end - end - return _units -end +end ]]-- -- *************************************************************** function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit to be able to repack the vehicle local dynamicLogisticUnitName = "%dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) @@ -2070,7 +2133,7 @@ end function ctld.updateDynamicLogisticUnitsZones() -- remove Dynamic Logistic Units if no statics units (crates) are in the zone local _units = {} for i, logUnit in ipairs(ctld.logisticUnits) do - if string.sub(phrase, 1, 17) == "%dynLogisticName_" then -- check if the unit is a dynamic logistic unit + if string.sub(logUnit, 1, 17) == "%dynLogisticName_" then -- check if the unit is a dynamic logistic unit local unitsInLogisticUnitZone = ctld.getUnitsInLogisticZone(logUnit) if #unitsInLogisticUnitZone == 0 then local _logUnit = StaticObject.getByName(logUnit) @@ -2084,15 +2147,35 @@ function ctld.updateDynamicLogisticUnitsZones() -- remove Dynamic Logistic return 5 -- reschedule the function in 5 second end -- *************************************************************** -function ctld.getUnitsInLogisticZone(_logisticUnitName) +function ctld.getUnitsInLogisticZone(_logisticUnitName, _coalition) local _unit = StaticObject.getByName(_logisticUnitName) if _unit then local _point = _unit:getPoint() - local _unitList = ctld.getNearbyUnits(_point, ctld.maximumDistanceLogistic) + local _unitList = ctld.getNearbyUnits(_point, ctld.maximumDistanceLogistic, _coalition) return _unitList end return {} end +-- *************************************************************** +function ctld.isUnitInNamedLogisticZone(_unit, _logisticUnitName) -- check if a unit is in the named logistic zone + local unitPoint = _unit:getPoint() + local logisticUnitPoint = StaticObject.getByName(_logisticUnitName):getPoint() + + local _dist = ctld.getDistance(unitPoint, logisticUnitPoint) + if _dist <= ctld.maximumDistanceLogistic then + return true + end + return false +end +-- *************************************************************** +function ctld.isUnitInALogisticZone(_unit) -- check if a unit is in a logistic zone if true then return the logisticUnitName of the zone + for i, logUnit in ipairs(ctld.logisticUnits) do + if ctld.isUnitInNamedLogisticZone(_unit, logUnit) then + return logUnit + end + end + return nil +end -- *************************************************************** -- **************** BE CAREFUL BELOW HERE ************************ @@ -5929,7 +6012,7 @@ function ctld.addTransportF10MenuOptions(_unitName) table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, groupId = _groupId, subMenuPath = RepackCommandsPath, - menuFunction = ctld.repackVehicle, + menuFunction = ctld.repackVehicleRequest, menuArgsTable = {_vehicle, _unitName} }) end local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath) @@ -6061,8 +6144,7 @@ function ctld.buildPaginatedMenu(_menuEntries) ctld.logTrace("ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do - --ctld.logTrace("ctld.buildPaginatedMenu.menu = [%s]", ctld.p(mist.utils.tableShow(menu))) -ctld.logTrace("ctld.buildPaginatedMenu. mist.utils.tableShow(menu) = [%s]", mist.utils.tableShow(menu)) + ctld.logTrace("ctld.buildPaginatedMenu. mist.utils.tableShow(menu) = [%s]", mist.utils.tableShow(menu)) -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached @@ -8091,6 +8173,9 @@ function ctld.initialize() if ctld.enableCrates == true and ctld.hoverPickup == true then timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end + if ctld.enableRepackingVehicles == true then + timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) + end end,nil, timer.getTime()+1 ) --event handler for deaths From 791ee4e63e2d55e885b55cce7ae272c71584d14a Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 22 Mar 2025 23:02:57 +0100 Subject: [PATCH 008/166] fixes --- CTLD.lua | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index ea46dad..48ab4ec 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1969,7 +1969,7 @@ end -- *************************************************************** -- Repack vehicules crates functions -- *************************************************************** -ctld.repackRequestStack = {} -- table to store the repack request +ctld.repackRequestsStack = {} -- table to store the repack request function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) if _radius == nil then @@ -2031,25 +2031,15 @@ function ctld.isRepackableUnit(_unitId) end -- *************************************************************** function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request - local repackableUnit = _params[1] + --[[ +local repackableUnit = _params[1] local PlayerTransportUnitName = _params[2] local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) local spawnRefPoint = PlayerTransportUnit:getPoint() local refCountry = PlayerTransportUnit:getCountry() - +]]-- ctld.repackRequestsStack[#ctld.repackRequestsStack+1] = _params - - --[[ - if repackableUnit then - local _point = {x = spawnRefPoint.x+5, z = spawnRefPoint.z} - local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", repackableUnit.desc, _unitId) - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) - ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle - return - end ]]-- - end -- *************************************************************** function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' to process each request From c397bad10766a90e75652ae946d416df2d25df7f Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 22 Mar 2025 23:30:53 +0100 Subject: [PATCH 009/166] fixes --- CTLD.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 48ab4ec..e2132d6 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6131,14 +6131,19 @@ ctld.logTrace("FG_ menuEntries = %s", ctld.p(mist.utils.tableShow(menuEntries)) end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - ctld.logTrace("ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) + ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) + local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do ctld.logTrace("ctld.buildPaginatedMenu. mist.utils.tableShow(menu) = [%s]", mist.utils.tableShow(menu)) + if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then + menu.subMenuPath = nextSubMenuPath + end -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) + nextSubMenuPath = menu.subMenuPath itemNbSubmenu = 1 end missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) From 0793d3bda5ff8bfa70b074f7db3b84f3c15138ac Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 22 Mar 2025 23:47:06 +0100 Subject: [PATCH 010/166] fixes --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index e2132d6..bce497c 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6135,7 +6135,7 @@ function ctld.buildPaginatedMenu(_menuEntries) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do - ctld.logTrace("ctld.buildPaginatedMenu. mist.utils.tableShow(menu) = [%s]", mist.utils.tableShow(menu)) + ctld.logTrace("FG ctld.buildPaginatedMenu. [%s] mist.utils.tableShow(menu) = [%s]", i, mist.utils.tableShow(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then menu.subMenuPath = nextSubMenuPath end From a58efad9859a954707e485bccb2591d9683b2ed3 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 00:09:30 +0100 Subject: [PATCH 011/166] fixes --- CTLD.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index bce497c..38afbfb 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2031,6 +2031,7 @@ function ctld.isRepackableUnit(_unitId) end -- *************************************************************** function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request + --[[ local repackableUnit = _params[1] local PlayerTransportUnitName = _params[2] @@ -2040,6 +2041,7 @@ local repackableUnit = _params[1] local refCountry = PlayerTransportUnit:getCountry() ]]-- ctld.repackRequestsStack[#ctld.repackRequestsStack+1] = _params + ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) end -- *************************************************************** function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' to process each request @@ -6135,7 +6137,7 @@ function ctld.buildPaginatedMenu(_menuEntries) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do - ctld.logTrace("FG ctld.buildPaginatedMenu. [%s] mist.utils.tableShow(menu) = [%s]", i, mist.utils.tableShow(menu)) + --ctld.logTrace("FG ctld.buildPaginatedMenu. [%s] mist.utils.tableShow(menu) = [%s]", i, mist.utils.tableShow(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then menu.subMenuPath = nextSubMenuPath end From b0d8f8b5c7ea13cd3923c14954686096a3132c06 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 16:36:31 +0100 Subject: [PATCH 012/166] fixes --- CTLD.lua | 50 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 38afbfb..04bcd69 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1981,14 +1981,13 @@ function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) return end - local playerCoalition = unit:getCoalition() - local point = unit:getPoint() - local unitList = ctld.getNearbyUnits(point, _radius, playerCoalition) + local unitsNamesList = ctld.getNearbyUnits(unit:getPoint(), _radius, unit:getCoalition()) local repackableUnits = {} - for i=1, #unitList do - local repackableUnit = ctld.isRepackableUnit(unitList[i]) + for i=1, #unitsNamesList do + local unitObject = Unit.getByName(unitsNamesList[i]) + local repackableUnit = ctld.isRepackableUnit(unitsNamesList[i]) if repackableUnit then - repackableUnit["repackableUnitGroupID"] = unitList[i]:getGroup():getID() + repackableUnit["repackableUnitGroupID"] = unitObject:getGroup():getID() table.insert(repackableUnits, mist.utils.deepCopy(repackableUnit)) end end @@ -2007,21 +2006,22 @@ function ctld.getNearbyUnits(_point, _radius, _coalition) --local _dist = ctld.getDistance(u:getPoint(), _point) local _dist = mist.utils.get2DDist(u:getPoint(), _point) if _dist <= _radius then - table.insert(_units, u) + table.insert(_units, k) -- insert nearby unitName end end end return _units end -- *************************************************************** -function ctld.isRepackableUnit(_unitId) - local unitType = _unitId:getTypeName() +function ctld.isRepackableUnit(_unitName) + local unitObject = Unit.getByName(_unitName) + local unitType = unitObject:getTypeName() for k,v in pairs(ctld.spawnableCrates) do for i=1, #ctld.spawnableCrates[k] do - if _unitId then + if _unitName then if ctld.spawnableCrates[k][i].unit == unitType then local repackableUnit = mist.utils.deepCopy(ctld.spawnableCrates[k][i]) - repackableUnit["vehicleId"] = _unitId + repackableUnit["repackableUnitName"] = _unitName return repackableUnit end end @@ -2030,12 +2030,25 @@ function ctld.isRepackableUnit(_unitId) return nil end -- *************************************************************** +function ctld.getCrateDesc(_crateWeight) + for k,v in pairs(ctld.spawnableCrates) do + for i=1, #ctld.spawnableCrates[k] do + if _crateWeight then + if ctld.spawnableCrates[k][i].weight == _crateWeight then + return ctld.spawnableCrates[k][i] + end + end + end + end + return nil +end +-- *************************************************************** function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request --[[ local repackableUnit = _params[1] local PlayerTransportUnitName = _params[2] - local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) + local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) local spawnRefPoint = PlayerTransportUnit:getPoint() local refCountry = PlayerTransportUnit:getCountry() @@ -2048,7 +2061,7 @@ function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' if t == nil then t = timer.getTime() end - + ctld.logTrace("FG_ ctld.repackVehicle = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) for ii, v in ipairs(ctld.repackRequestsStack) do local repackableUnit = v[1] local PlayerTransportUnitName = v[2] @@ -2149,7 +2162,12 @@ function ctld.getUnitsInLogisticZone(_logisticUnitName, _coalition) return {} end -- *************************************************************** -function ctld.isUnitInNamedLogisticZone(_unit, _logisticUnitName) -- check if a unit is in the named logistic zone +function ctld.isUnitInNamedLogisticZone(_unitName, _logisticUnitName) -- check if a unit is in the named logistic zone + trigger.action.outText('ctld.isUnitInNamedLogisticZone._logisticUnitName = ',tostring(_logisticUnitName), 10) + local _unit = Unit.getByName(_unitName) + if _unit == nil then + return false + end local unitPoint = _unit:getPoint() local logisticUnitPoint = StaticObject.getByName(_logisticUnitName):getPoint() @@ -2160,9 +2178,9 @@ function ctld.isUnitInNamedLogisticZone(_unit, _logisticUnitName) -- check if a return false end -- *************************************************************** -function ctld.isUnitInALogisticZone(_unit) -- check if a unit is in a logistic zone if true then return the logisticUnitName of the zone +function ctld.isUnitInALogisticZone(_unitName) -- check if a unit is in a logistic zone if true then return the logisticUnitName of the zone for i, logUnit in ipairs(ctld.logisticUnits) do - if ctld.isUnitInNamedLogisticZone(_unit, logUnit) then + if ctld.isUnitInNamedLogisticZone(_unitName, logUnit) then return logUnit end end From 38e86bead302f7a2e0e04fd126e6e7292fb96444 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 16:37:30 +0100 Subject: [PATCH 013/166] fixes --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 04bcd69..1f7ce62 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2163,7 +2163,7 @@ function ctld.getUnitsInLogisticZone(_logisticUnitName, _coalition) end -- *************************************************************** function ctld.isUnitInNamedLogisticZone(_unitName, _logisticUnitName) -- check if a unit is in the named logistic zone - trigger.action.outText('ctld.isUnitInNamedLogisticZone._logisticUnitName = ',tostring(_logisticUnitName), 10) + trigger.action.outText('ctld.isUnitInNamedLogisticZone._logisticUnitName = '..tostring(_logisticUnitName), 10) local _unit = Unit.getByName(_unitName) if _unit == nil then return false From f152f58a9703e2dc4850f3982bcd6d10e485a28b Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 16:49:29 +0100 Subject: [PATCH 014/166] fixes --- CTLD.lua | 47 ++++++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 1f7ce62..3a442af 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2044,15 +2044,6 @@ function ctld.getCrateDesc(_crateWeight) end -- *************************************************************** function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request - - --[[ -local repackableUnit = _params[1] - local PlayerTransportUnitName = _params[2] - local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) - local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) - local spawnRefPoint = PlayerTransportUnit:getPoint() - local refCountry = PlayerTransportUnit:getCountry() -]]-- ctld.repackRequestsStack[#ctld.repackRequestsStack+1] = _params ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) end @@ -2063,32 +2054,34 @@ function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' end ctld.logTrace("FG_ ctld.repackVehicle = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) for ii, v in ipairs(ctld.repackRequestsStack) do - local repackableUnit = v[1] - local PlayerTransportUnitName = v[2] - local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) - ctld.logTrace(" ctld.repackVehicle.repackableUnit = %s", ctld.p(mist.utils.tableShow(repackableUnit))) + local repackableUnitName = v[1].repackableUnitName + local crateWeight = v[1].weight + local repackableUnit = Unit.getByName(repackableUnitName) + local PlayerTransportUnit = Unit.getByName(v[2][1]) local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) local spawnRefPoint = PlayerTransportUnit:getPoint() - local refCountry = PlayerTransportUnit:getCountry() - if repackableUnit then + local refCountry = PlayerTransportUnit:getCountry() + + if repackableUnit then if repackableUnit:isExist() then --ici calculer le heading des spwans à effectuer - for i=1, repackableUnit.cratesRequired do + for i=1, v[1].cratesRequired do local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", repackableUnit.desc, _unitId) - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) + local _name = string.format("%s_%i", v[1].desc, _unitId) + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end - if ctld.isUnitInALogisticZone(repackableUnit) == nil then + if ctld.isUnitInALogisticZone(repackableUnitName) == nil then ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle end - repackableUnit.vehicleId:destroy() -- destroy repacked unit + repackableUnit:destroy() -- destroy repacked unit end ctld.repackRequestsStack[ii] = nil end - end + + end return 3 -- reschedule the function in 3 seconds end --[[ *************************************************************** @@ -2169,11 +2162,12 @@ function ctld.isUnitInNamedLogisticZone(_unitName, _logisticUnitName) -- check i return false end local unitPoint = _unit:getPoint() - local logisticUnitPoint = StaticObject.getByName(_logisticUnitName):getPoint() - - local _dist = ctld.getDistance(unitPoint, logisticUnitPoint) - if _dist <= ctld.maximumDistanceLogistic then - return true + if StaticObject.getByName(_logisticUnitName) then + local logisticUnitPoint = StaticObject.getByName(_logisticUnitName):getPoint() + local _dist = ctld.getDistance(unitPoint, logisticUnitPoint) + if _dist <= ctld.maximumDistanceLogistic then + return true + end end return false end @@ -6026,7 +6020,6 @@ function ctld.addTransportF10MenuOptions(_unitName) menuArgsTable = {_vehicle, _unitName} }) end local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath) -ctld.logTrace("FG_ menuEntries = %s", ctld.p(mist.utils.tableShow(menuEntries))) ctld.buildPaginatedMenu(menuEntries) end end From 08ebd7e82dd6756d17d074f14953a6ddff4ac321 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 16:54:27 +0100 Subject: [PATCH 015/166] fixes --- CTLD.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/CTLD.lua b/CTLD.lua index 3a442af..356a85c 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2049,6 +2049,7 @@ function ctld.repackVehicleRequest(_params) -- update rrs table 'repackReque end -- *************************************************************** function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' to process each request + trigger.action.outText("Repacking vehicle...", 10) if t == nil then t = timer.getTime() end From 61d9e206b2e43d116616c73a2f126f7b52b0650f Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 16:58:32 +0100 Subject: [PATCH 016/166] fixes --- CTLD.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 356a85c..4953a42 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2058,10 +2058,11 @@ function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' local repackableUnitName = v[1].repackableUnitName local crateWeight = v[1].weight local repackableUnit = Unit.getByName(repackableUnitName) + local PlayerTransportUnit = Unit.getByName(v[2][1]) - local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) - local spawnRefPoint = PlayerTransportUnit:getPoint() - local refCountry = PlayerTransportUnit:getCountry() + local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) + local spawnRefPoint = PlayerTransportUnit:getPoint() + local refCountry = PlayerTransportUnit:getCountry() if repackableUnit then if repackableUnit:isExist() then @@ -2081,7 +2082,6 @@ function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' end ctld.repackRequestsStack[ii] = nil end - end return 3 -- reschedule the function in 3 seconds end From 111fd73b94ba7a12ae710d428e460144026f0a1c Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 17:03:37 +0100 Subject: [PATCH 017/166] fixes --- CTLD.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 4953a42..695c1ab 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2048,7 +2048,7 @@ function ctld.repackVehicleRequest(_params) -- update rrs table 'repackReque ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) end -- *************************************************************** -function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' to process each request +function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request trigger.action.outText("Repacking vehicle...", 10) if t == nil then t = timer.getTime() @@ -2083,7 +2083,11 @@ function ctld.repackVehicle(_params,t) -- scan rrs table 'repackRequestsStack' ctld.repackRequestsStack[ii] = nil end end - return 3 -- reschedule the function in 3 seconds + if ctld.enableRepackingVehicles == true then + return t + 3 -- reschedule the function in 3 seconds + else + return nil --stop scheduling + end end --[[ *************************************************************** function ctld.repackVehicle_old(_params) From b6232d3c152bf2965fed97ec9ba85ba989fc6e4c Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 17:09:35 +0100 Subject: [PATCH 018/166] fixes --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 695c1ab..a9d2d1e 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2059,7 +2059,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack local crateWeight = v[1].weight local repackableUnit = Unit.getByName(repackableUnitName) - local PlayerTransportUnit = Unit.getByName(v[2][1]) + local PlayerTransportUnit = Unit.getByName(v[2]) local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) local spawnRefPoint = PlayerTransportUnit:getPoint() local refCountry = PlayerTransportUnit:getCountry() From 6fc9f146ae03c684ec4d42eeb3c0fb597aa6f15f Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 17:38:24 +0100 Subject: [PATCH 019/166] fixes --- CTLD.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CTLD.lua b/CTLD.lua index a9d2d1e..a6e453f 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6164,6 +6164,8 @@ function ctld.buildPaginatedMenu(_menuEntries) nextSubMenuPath = menu.subMenuPath itemNbSubmenu = 1 end + menu.menuArgsTable.subMenuPath = menu.subMenuPath + menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) end end From c5bae34ed1e22243f9cde91d3b4b483d7f2a0f09 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 19:31:30 +0100 Subject: [PATCH 020/166] fixes --- CTLD.lua | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index a6e453f..ff605c9 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6009,13 +6009,16 @@ function ctld.addTransportF10MenuOptions(_unitName) 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.vehicleCommandsPath == nil then + ctld.vehicleCommandsPath = _vehicleCommandsPath + end + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload Vehicles"), ctld.vehicleCommandsPath, ctld.unloadTroops, { _unitName, false }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), ctld.vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false,"",true }) if ctld.enableRepackingVehicles then local repackableVehicles = ctld.getUnitsInRepackRadius(_unitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then local menuEntries = {} - local RepackCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") for _, _vehicle in pairs(repackableVehicles) do table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, @@ -6024,14 +6027,14 @@ function ctld.addTransportF10MenuOptions(_unitName) menuFunction = ctld.repackVehicleRequest, menuArgsTable = {_vehicle, _unitName} }) end - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath) + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) ctld.buildPaginatedMenu(menuEntries) end end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath, ctld.loadUnloadFOBCrate, { _unitName, false }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Unload FOB Crate"), ctld.vehicleCommandsPath, ctld.loadUnloadFOBCrate, { _unitName, false }) end - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _vehicleCommandsPath, ctld.checkTroopStatus, { _unitName }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), ctld.vehicleCommandsPath, ctld.checkTroopStatus, { _unitName }) end end From 8f2a3356093e0e193ef0c85bf45c03062a8976b1 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 22:12:19 +0100 Subject: [PATCH 021/166] fixes --- CTLD.lua | 72 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index ff605c9..71103e4 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6009,32 +6009,35 @@ function ctld.addTransportF10MenuOptions(_unitName) 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.vehicleCommandsPath == nil then - ctld.vehicleCommandsPath = _vehicleCommandsPath + ctld.vehicleCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) end - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload Vehicles"), ctld.vehicleCommandsPath, ctld.unloadTroops, { _unitName, false }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), ctld.vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false,"",true }) + if ctld.enableRepackingVehicles then - local repackableVehicles = ctld.getUnitsInRepackRadius(_unitName, ctld.maximumDistanceRepackableUnitsSearch) - if repackableVehicles then - local menuEntries = {} - local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) - RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") - for _, _vehicle in pairs(repackableVehicles) do - table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, - groupId = _groupId, - subMenuPath = RepackCommandsPath, - menuFunction = ctld.repackVehicleRequest, - menuArgsTable = {_vehicle, _unitName} }) - end - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) - ctld.buildPaginatedMenu(menuEntries) - end + -- local repackableVehicles = ctld.getUnitsInRepackRadius(_unitName, ctld.maximumDistanceRepackableUnitsSearch) + -- if repackableVehicles then + -- local menuEntries = {} + -- local RepackCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) + -- RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") + + -- for _, _vehicle in pairs(repackableVehicles) do + -- table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, + -- groupId = _groupId, + -- subMenuPath = RepackCommandsPath, + -- menuFunction = ctld.repackVehicleRequest, + -- menuArgsTable = {_vehicle, _unitName} }) + -- end + -- local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath) + -- ctld.buildPaginatedMenu(menuEntries) + -- end end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Unload FOB Crate"), ctld.vehicleCommandsPath, ctld.loadUnloadFOBCrate, { _unitName, false }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath, ctld.loadUnloadFOBCrate, { _unitName, false }) end - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), ctld.vehicleCommandsPath, ctld.checkTroopStatus, { _unitName }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _vehicleCommandsPath, ctld.checkTroopStatus, { _unitName }) end end @@ -6173,6 +6176,35 @@ function ctld.buildPaginatedMenu(_menuEntries) end end --****************************************************************************************************** +function ctld.updateRepackMenu(_playerUnitName) + local playerUnit = ctld.getTransportUnit(_playerUnitName) + if playerUnit then + local _unitTypename = playerUnit:getTypeName() + local _groupId = ctld.getGroupId(playerUnit) + if ctld.enableRepackingVehicles then + local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) + if repackableVehicles then + + missionCommands.removeItemForGroup(1, ctld.vehicleCommandsPath) + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) + + local menuEntries = {} + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) + RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") + for _, _vehicle in pairs(repackableVehicles) do + table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, + groupId = _groupId, + subMenuPath = RepackCommandsPath, + menuFunction = ctld.repackVehicleRequest, + menuArgsTable = {_vehicle, _playerUnitName} }) + end + ctld.buildPaginatedMenu(menuEntries) + end + end + end + end +end +--****************************************************************************************************** function ctld.addOtherF10MenuOptions() ctld.logDebug("ctld.addOtherF10MenuOptions") -- reschedule every 10 seconds From 9eb4fc66b5652b4a1a4799e8f34443453d1e72f9 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 22:25:28 +0100 Subject: [PATCH 022/166] fixes --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 71103e4..0651234 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6185,7 +6185,7 @@ function ctld.updateRepackMenu(_playerUnitName) local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then - missionCommands.removeItemForGroup(1, ctld.vehicleCommandsPath) + missionCommands.removeItemForGroup(1, ctld.vehicleCommandsPath) -- remove the old menu local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) local menuEntries = {} From c51a3d80ca2bffad59af2d386ae451039a8396b6 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 23 Mar 2025 22:26:51 +0100 Subject: [PATCH 023/166] fixes --- CTLD.lua | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 0651234..644b633 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6017,22 +6017,7 @@ function ctld.addTransportF10MenuOptions(_unitName) end if ctld.enableRepackingVehicles then - -- local repackableVehicles = ctld.getUnitsInRepackRadius(_unitName, ctld.maximumDistanceRepackableUnitsSearch) - -- if repackableVehicles then - -- local menuEntries = {} - -- local RepackCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) - -- RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") - - -- for _, _vehicle in pairs(repackableVehicles) do - -- table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, - -- groupId = _groupId, - -- subMenuPath = RepackCommandsPath, - -- menuFunction = ctld.repackVehicleRequest, - -- menuArgsTable = {_vehicle, _unitName} }) - -- end - -- local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), _vehicleCommandsPath) - -- ctld.buildPaginatedMenu(menuEntries) - -- end + ctld.updateRepackMenu(_unitName) end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath, ctld.loadUnloadFOBCrate, { _unitName, false }) From 0180cf33d21095f1339191322e20c2225758ce1f Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 24 Mar 2025 00:37:19 +0100 Subject: [PATCH 024/166] fixes --- CTLD.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 644b633..592e091 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2059,7 +2059,8 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack local crateWeight = v[1].weight local repackableUnit = Unit.getByName(repackableUnitName) - local PlayerTransportUnit = Unit.getByName(v[2]) + local playerUnitName = v[2] + local PlayerTransportUnit = Unit.getByName(playerUnitName) local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) local spawnRefPoint = PlayerTransportUnit:getPoint() local refCountry = PlayerTransportUnit:getCountry() @@ -2082,6 +2083,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack end ctld.repackRequestsStack[ii] = nil end + ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units end if ctld.enableRepackingVehicles == true then return t + 3 -- reschedule the function in 3 seconds From 4db3c71728e46c246706b4d72e49a979e8d8c125 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 24 Mar 2025 00:49:58 +0100 Subject: [PATCH 025/166] fixes --- CTLD.lua | 11451 +++++++++++++++++++++++++++-------------------------- 1 file changed, 5736 insertions(+), 5715 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 592e091..a891dc4 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1,37 +1,37 @@ --[[ - Combat Troop and Logistics Drop + Combat Troop and Logistics Drop - Allows Huey, Mi-8 and C130 to transport troops internally and Helicopters to transport Logistic / Vehicle units to the field via sling-loads - without requiring external mods. + Allows Huey, Mi-8 and C130 to transport troops internally and Helicopters to transport Logistic / Vehicle units to the field via sling-loads + without requiring external mods. - Supports all of the original CTTS functionality such as AI auto troop load and unload as well as group spawning and preloading of troops into units. + Supports all of the original CTTS functionality such as AI auto troop load and unload as well as group spawning and preloading of troops into units. - Supports deployment of Auto Lasing JTAC to the field + Supports deployment of Auto Lasing JTAC to the field - See https://github.com/ciribob/DCS-CTLD for a user manual and the latest version + See https://github.com/ciribob/DCS-CTLD for a user manual and the latest version - Contributors: - - Steggles - https://github.com/Bob7heBuilder - - mvee - https://github.com/mvee - - jmontleon - https://github.com/jmontleon - - emilianomolina - https://github.com/emilianomolina - - davidp57 - https://github.com/veaf - - Queton1-1 - https://github.com/Queton1-1 - - Proxy404 - https://github.com/Proxy404 - - atcz - https://github.com/atcz - - marcos2221- https://github.com/marcos2221 - - FullGas1 - https://github.com/FullGas1 (i18n concept, FR and SP translations) + Contributors: + - Steggles - https://github.com/Bob7heBuilder + - mvee - https://github.com/mvee + - jmontleon - https://github.com/jmontleon + - emilianomolina - https://github.com/emilianomolina + - davidp57 - https://github.com/veaf + - Queton1-1 - https://github.com/Queton1-1 + - Proxy404 - https://github.com/Proxy404 + - atcz - https://github.com/atcz + - marcos2221- https://github.com/marcos2221 + - FullGas1 - https://github.com/FullGas1 (i18n concept, FR and SP translations) - Add [issues](https://github.com/ciribob/DCS-CTLD/issues) to the GitHub repository if you want to report a bug or suggest a new feature. + Add [issues](https://github.com/ciribob/DCS-CTLD/issues) to the GitHub repository if you want to report a bug or suggest a new feature. - Contact Zip [on Discord](https://discordapp.com/users/421317390807203850) or [on Github](https://github.com/davidp57) if you need help or want to have a friendly chat. + Contact Zip [on Discord](https://discordapp.com/users/421317390807203850) or [on Github](https://github.com/davidp57) if you need help or want to have a friendly chat. - Send beers (or kind messages) to Ciribob [on Discord](https://discordapp.com/users/204712384747536384), he's the reason we have CTLD ^^ + Send beers (or kind messages) to Ciribob [on Discord](https://discordapp.com/users/204712384747536384), he's the reason we have CTLD ^^ ]] if not ctld then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here - trigger.action.outText("\n\n** HEY MISSION-DESIGNER! **\n\nCTLD-i18n has not been loaded!\n\nMake sure CTLD-i18n is loaded\n*before* running this script!\n\nIt contains all the translations!\n", 10) - ctld = {} -- DONT REMOVE! + trigger.action.outText("\n\n** HEY MISSION-DESIGNER! **\n\nCTLD-i18n has not been loaded!\n\nMake sure CTLD-i18n is loaded\n*before* running this script!\n\nIt contains all the translations!\n", 10) + ctld = {} -- DONT REMOVE! end --- Identifier. All output in DCS.log will start with this. @@ -52,17 +52,17 @@ ctld.dontInitialize = false -- if true, ctld.initialize() will not run; instead, -- If you want to change the language replace "en" with the language you want to use ---======== ENGLISH - the reference =========================================================================== +--======== ENGLISH - the reference =========================================================================== ctld.i18n_lang = "en" ---======== FRENCH - FRANCAIS ================================================================================= +--======== FRENCH - FRANCAIS ================================================================================= --ctld.i18n_lang = "fr" ---====== SPANISH : ESPAÑOL ==================================================================================== +--====== SPANISH : ESPAÑOL ==================================================================================== --ctld.i18n_lang = "es" ---====== Korean : 한국어 ==================================================================================== +--====== Korean : 한국어 ==================================================================================== --ctld.i18n_lang = "ko" if not ctld.i18n then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here - ctld.i18n = {} -- DONT REMOVE! + ctld.i18n = {} -- DONT REMOVE! end -- This is the default language @@ -355,49 +355,49 @@ ctld.i18n["en"]["STOP autoRefresh targets in LOS"] = "" ---@param ... any (list) The parameters to replace in the text, in order (all paremeters will be converted to string) ---@return string the translated and formatted text function ctld.i18n_translate(text, ...) - local _text + local _text - if not ctld.i18n[ctld.i18n_lang] then - env.info(string.format(" E - CTLD.i18n_translate: Language %s not found, defaulting to 'en'", tostring(ctld.i18n_lang))) - _text = ctld.i18n["en"][text] - else - _text = ctld.i18n[ctld.i18n_lang][text] - end - - -- default to english - if _text == nil then - _text = ctld.i18n["en"][text] - end - - -- default to the provided text - if _text == nil or _text == "" then - _text = text - end - - if arg and arg.n and arg.n > 0 then - local _args = {} - for i=1,arg.n do - _args[i] = tostring(arg[i]) or "" + if not ctld.i18n[ctld.i18n_lang] then + env.info(string.format(" E - CTLD.i18n_translate: Language %s not found, defaulting to 'en'", tostring(ctld.i18n_lang))) + _text = ctld.i18n["en"][text] + else + _text = ctld.i18n[ctld.i18n_lang][text] end - for i = 1, #_args do - _text = string.gsub(_text, "%%" .. i, _args[i]) - end - end - return _text + -- default to english + if _text == nil then + _text = ctld.i18n["en"][text] + end + + -- default to the provided text + if _text == nil or _text == "" then + _text = text + end + + if arg and arg.n and arg.n > 0 then + local _args = {} + for i=1,arg.n do + _args[i] = tostring(arg[i]) or "" + end + for i = 1, #_args do + _text = string.gsub(_text, "%%" .. i, _args[i]) + end + end + + return _text end -- ************************************************************************ --- ********************* USER CONFIGURATION ****************************** +-- ********************* USER CONFIGURATION ****************************** -- ************************************************************************ -ctld.staticBugWorkaround = false -- DCS had a bug where destroying statics would cause a crash. If this happens again, set this to TRUE +ctld.staticBugWorkaround = false -- DCS had a bug where destroying statics would cause a crash. If this happens again, set this to TRUE ctld.disableAllSmoke = false -- if true, all smoke is diabled at pickup and drop off zones regardless of settings below. Leave false to respect settings below -- Allow units to CTLD by aircraft type and not by pilot name - this is done everytime a player enters a new unit ctld.addPlayerAircraftByType = true -ctld.hoverPickup = true -- if set to false you can load crates with the F10 menu instead of hovering... Only if not using real crates! +ctld.hoverPickup = true -- if set to false you can load crates with the F10 menu instead of hovering... Only if not using real crates! ctld.loadCrateFromMenu = true -- if set to true, you can load crates with the F10 menu OR hovering, in case of using choppers and planes for example. ctld.enableCrates = true -- if false, Helis will not be able to spawn or unpack crates so will be normal CTTS @@ -405,33 +405,33 @@ ctld.enableAllCrates = true -- if false, the "all crates" menu items will not be ctld.slingLoad = false -- if false, crates can be used WITHOUT slingloading, by hovering above the crate, simulating slingloading but not the weight... -- There are some bug with Sling-loading that can cause crashes, if these occur set slingLoad to false -- to use the other method. --- Set staticBugFix to FALSE if use set ctld.slingLoad to TRUE +-- Set staticBugFix to FALSE if use set ctld.slingLoad to TRUE ctld.enableSmokeDrop = true -- if false, helis and c-130 will not be able to drop smoke ctld.maxExtractDistance = 125 -- max distance from vehicle to troops to allow a group extraction ctld.maximumDistanceLogistic = 200 -- max distance from vehicle to logistics to allow a loading or spawning operation ctld.enableRepackingVehicles = true -- if true, vehicles can be repacked into crates -ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units +ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units ctld.maximumSearchDistance = 4000 -- max distance for troops to search for enemy ctld.maximumMoveDistance = 2000 -- max distance for troops to move from drop point if no enemy is nearby ctld.minimumDeployDistance = 1000 -- minimum distance from a friendly pickup zone where you can deploy a crate ctld.numberOfTroops = 10 -- default number of troops to load on a transport heli or C-130 - -- also works as maximum size of group that'll fit into a helicopter unless overridden + -- also works as maximum size of group that'll fit into a helicopter unless overridden ctld.enableFastRopeInsertion = true -- allows you to drop troops by fast rope ctld.fastRopeMaximumHeight = 18.28 -- in meters which is 60 ft max fast rope (not rappell) safe height ctld.vehiclesForTransportRED = { "BRDM-2", "BTR_D" } -- vehicles to load onto Il-76 - Alternatives {"Strela-1 9P31","BMP-1"} ctld.vehiclesForTransportBLUE = { "M1045 HMMWV TOW", "M1043 HMMWV Armament" } -- vehicles to load onto c130 - Alternatives {"M1128 Stryker MGS","M1097 Avenger"} ctld.vehiclesWeight = { - ["BRDM-2"] = 7000, - ["BTR_D"] = 8000, - ["M1045 HMMWV TOW"] = 3220, - ["M1043 HMMWV Armament"] = 2500 + ["BRDM-2"] = 7000, + ["BTR_D"] = 8000, + ["M1045 HMMWV TOW"] = 3220, + ["M1043 HMMWV Armament"] = 2500 } 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.enabledFOBBuilding = true -- if true, you can load a crate INTO a C-130 than when unpacked creates a Forward Operating Base (FOB) which is a new place to spawn (crates) and carry crates from - -- In future i'd like it to be a FARP but so far that seems impossible... - -- You can also enable troop Pickup at FOBS +ctld.enabledFOBBuilding = true -- if true, you can load a crate INTO a C-130 than when unpacked creates a Forward Operating Base (FOB) which is a new place to spawn (crates) and carry crates from + -- In future i'd like it to be a FARP but so far that seems impossible... + -- You can also enable troop Pickup at FOBS ctld.cratesRequiredForFOB = 3 -- The amount of crates required to build a FOB. Once built, helis can spawn crates at this outpost to be carried and deployed in another area. -- The large crates can only be loaded and dropped by large aircraft, like the C-130 and listed in ctld.vehicleTransportEnabled -- Small FOB crates can be moved by helicopter. The FOB will require ctld.cratesRequiredForFOB larges crates and small crates are 1/3 of a large fob crate @@ -507,284 +507,284 @@ ctld.JTAC_allow9Line = true -- if true, allow players to ask for a 9Line (indivi -- Flag Number - Optional last field. If set the current number of groups remaining can be obtained from the flag value --pickupZones = { "Zone name or Ship Unit Name", "smoke color", "limit (-1 unlimited)", "ACTIVE (yes/no)", "side (0 = Both sides / 1 = Red / 2 = Blue )", flag number (optional) } ctld.pickupZones = { - { "pickzone1", "blue", -1, "yes", 0 }, - { "pickzone2", "red", -1, "yes", 0 }, - { "pickzone3", "none", -1, "yes", 0 }, - { "pickzone4", "none", -1, "yes", 0 }, - { "pickzone5", "none", -1, "yes", 0 }, - { "pickzone6", "none", -1, "yes", 0 }, - { "pickzone7", "none", -1, "yes", 0 }, - { "pickzone8", "none", -1, "yes", 0 }, - { "pickzone9", "none", 5, "yes", 1 }, -- limits pickup zone 9 to 5 groups of soldiers or vehicles, only red can pick up - { "pickzone10", "none", 10, "yes", 2 }, -- limits pickup zone 10 to 10 groups of soldiers or vehicles, only blue can pick up + { "pickzone1", "blue", -1, "yes", 0 }, + { "pickzone2", "red", -1, "yes", 0 }, + { "pickzone3", "none", -1, "yes", 0 }, + { "pickzone4", "none", -1, "yes", 0 }, + { "pickzone5", "none", -1, "yes", 0 }, + { "pickzone6", "none", -1, "yes", 0 }, + { "pickzone7", "none", -1, "yes", 0 }, + { "pickzone8", "none", -1, "yes", 0 }, + { "pickzone9", "none", 5, "yes", 1 }, -- limits pickup zone 9 to 5 groups of soldiers or vehicles, only red can pick up + { "pickzone10", "none", 10, "yes", 2 }, -- limits pickup zone 10 to 10 groups of soldiers or vehicles, only blue can pick up - { "pickzone11", "blue", 20, "no", 2 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! - { "pickzone12", "red", 20, "no", 1 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! - { "pickzone13", "none", -1, "yes", 0 }, - { "pickzone14", "none", -1, "yes", 0 }, - { "pickzone15", "none", -1, "yes", 0 }, - { "pickzone16", "none", -1, "yes", 0 }, - { "pickzone17", "none", -1, "yes", 0 }, - { "pickzone18", "none", -1, "yes", 0 }, - { "pickzone19", "none", 5, "yes", 0 }, - { "pickzone20", "none", 10, "yes", 0, 1000 }, -- optional extra flag number to store the current number of groups available in + { "pickzone11", "blue", 20, "no", 2 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! + { "pickzone12", "red", 20, "no", 1 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! + { "pickzone13", "none", -1, "yes", 0 }, + { "pickzone14", "none", -1, "yes", 0 }, + { "pickzone15", "none", -1, "yes", 0 }, + { "pickzone16", "none", -1, "yes", 0 }, + { "pickzone17", "none", -1, "yes", 0 }, + { "pickzone18", "none", -1, "yes", 0 }, + { "pickzone19", "none", 5, "yes", 0 }, + { "pickzone20", "none", 10, "yes", 0, 1000 }, -- optional extra flag number to store the current number of groups available in - { "USA Carrier", "blue", 10, "yes", 0, 1001 }, -- instead of a Zone Name you can also use the UNIT NAME of a ship + { "USA Carrier", "blue", 10, "yes", 0, 1001 }, -- instead of a Zone Name you can also use the UNIT NAME of a ship } -- dropOffZones = {"name","smoke colour",0,side 1 = Red or 2 = Blue or 0 = Both sides} ctld.dropOffZones = { - { "dropzone1", "green", 2 }, - { "dropzone2", "blue", 2 }, - { "dropzone3", "orange", 2 }, - { "dropzone4", "none", 2 }, - { "dropzone5", "none", 1 }, - { "dropzone6", "none", 1 }, - { "dropzone7", "none", 1 }, - { "dropzone8", "none", 1 }, - { "dropzone9", "none", 1 }, - { "dropzone10", "none", 1 }, + { "dropzone1", "green", 2 }, + { "dropzone2", "blue", 2 }, + { "dropzone3", "orange", 2 }, + { "dropzone4", "none", 2 }, + { "dropzone5", "none", 1 }, + { "dropzone6", "none", 1 }, + { "dropzone7", "none", 1 }, + { "dropzone8", "none", 1 }, + { "dropzone9", "none", 1 }, + { "dropzone10", "none", 1 }, } ---wpZones = { "Zone name", "smoke color", "ACTIVE (yes/no)", "side (0 = Both sides / 1 = Red / 2 = Blue )", } +--wpZones = { "Zone name", "smoke color", "ACTIVE (yes/no)", "side (0 = Both sides / 1 = Red / 2 = Blue )", } ctld.wpZones = { - { "wpzone1", "green","yes", 2 }, - { "wpzone2", "blue","yes", 2 }, - { "wpzone3", "orange","yes", 2 }, - { "wpzone4", "none","yes", 2 }, - { "wpzone5", "none","yes", 2 }, - { "wpzone6", "none","yes", 1 }, - { "wpzone7", "none","yes", 1 }, - { "wpzone8", "none","yes", 1 }, - { "wpzone9", "none","yes", 1 }, - { "wpzone10", "none","no", 0 }, -- Both sides as its set to 0 + { "wpzone1", "green","yes", 2 }, + { "wpzone2", "blue","yes", 2 }, + { "wpzone3", "orange","yes", 2 }, + { "wpzone4", "none","yes", 2 }, + { "wpzone5", "none","yes", 2 }, + { "wpzone6", "none","yes", 1 }, + { "wpzone7", "none","yes", 1 }, + { "wpzone8", "none","yes", 1 }, + { "wpzone9", "none","yes", 1 }, + { "wpzone10", "none","no", 0 }, -- Both sides as its set to 0 } -- ******************** Transports names ********************** -- If ctld.addPlayerAircraftByType = True, comment or uncomment lines to allow aircraft's type carry CTLD ctld.aircraftTypeTable = { - --%%%%% MODS %%%%% - --"Bronco-OV-10A", - --"Hercules", - --"SK-60", - --"UH-60L", - --"T-45", + --%%%%% MODS %%%%% + --"Bronco-OV-10A", + --"Hercules", + --"SK-60", + --"UH-60L", + --"T-45", - --%%%%% CHOPPERS %%%%% - --"Ka-50", - --"Ka-50_3", - "Mi-8MT", - "Mi-24P", - --"SA342L", - --"SA342M", - --"SA342Mistral", - --"SA342Minigun", - "UH-1H", - "CH-47Fbl1", + --%%%%% CHOPPERS %%%%% + --"Ka-50", + --"Ka-50_3", + "Mi-8MT", + "Mi-24P", + --"SA342L", + --"SA342M", + --"SA342Mistral", + --"SA342Minigun", + "UH-1H", + "CH-47Fbl1", - --%%%%% AIRCRAFTS %%%%% - --"C-101EB", - --"C-101CC", - --"Christen Eagle II", - --"L-39C", - --"L-39ZA", - --"MB-339A", - --"MB-339APAN", - --"Mirage-F1B", - --"Mirage-F1BD", - --"Mirage-F1BE", - --"Mirage-F1BQ", - --"Mirage-F1DDA", - --"Su-25T", - --"Yak-52", + --%%%%% AIRCRAFTS %%%%% + --"C-101EB", + --"C-101CC", + --"Christen Eagle II", + --"L-39C", + --"L-39ZA", + --"MB-339A", + --"MB-339APAN", + --"Mirage-F1B", + --"Mirage-F1BD", + --"Mirage-F1BE", + --"Mirage-F1BQ", + --"Mirage-F1DDA", + --"Su-25T", + --"Yak-52", - --%%%%% WARBIRDS %%%%% - --"Bf-109K-4", - --"Fw 190A8", - --"FW-190D9", - --"I-16", - --"MosquitoFBMkVI", - --"P-47D-30", - --"P-47D-40", - --"P-51D", - --"P-51D-30-NA", - --"SpitfireLFMkIX", - --"SpitfireLFMkIXCW", - --"TF-51D", + --%%%%% WARBIRDS %%%%% + --"Bf-109K-4", + --"Fw 190A8", + --"FW-190D9", + --"I-16", + --"MosquitoFBMkVI", + --"P-47D-30", + --"P-47D-40", + --"P-51D", + --"P-51D-30-NA", + --"SpitfireLFMkIX", + --"SpitfireLFMkIXCW", + --"TF-51D", } -- Use any of the predefined names or set your own ones ctld.transportPilotNames = { - "helicargo1", - "helicargo2", - "helicargo3", - "helicargo4", - "helicargo5", - "helicargo6", - "helicargo7", - "helicargo8", - "helicargo9", - "helicargo10", + "helicargo1", + "helicargo2", + "helicargo3", + "helicargo4", + "helicargo5", + "helicargo6", + "helicargo7", + "helicargo8", + "helicargo9", + "helicargo10", - "helicargo11", - "helicargo12", - "helicargo13", - "helicargo14", - "helicargo15", - "helicargo16", - "helicargo17", - "helicargo18", - "helicargo19", - "helicargo20", + "helicargo11", + "helicargo12", + "helicargo13", + "helicargo14", + "helicargo15", + "helicargo16", + "helicargo17", + "helicargo18", + "helicargo19", + "helicargo20", - "helicargo21", - "helicargo22", - "helicargo23", - "helicargo24", - "helicargo25", + "helicargo21", + "helicargo22", + "helicargo23", + "helicargo24", + "helicargo25", - "MEDEVAC #1", - "MEDEVAC #2", - "MEDEVAC #3", - "MEDEVAC #4", - "MEDEVAC #5", - "MEDEVAC #6", - "MEDEVAC #7", - "MEDEVAC #8", - "MEDEVAC #9", - "MEDEVAC #10", - "MEDEVAC #11", - "MEDEVAC #12", - "MEDEVAC #13", - "MEDEVAC #14", - "MEDEVAC #15", - "MEDEVAC #16", + "MEDEVAC #1", + "MEDEVAC #2", + "MEDEVAC #3", + "MEDEVAC #4", + "MEDEVAC #5", + "MEDEVAC #6", + "MEDEVAC #7", + "MEDEVAC #8", + "MEDEVAC #9", + "MEDEVAC #10", + "MEDEVAC #11", + "MEDEVAC #12", + "MEDEVAC #13", + "MEDEVAC #14", + "MEDEVAC #15", + "MEDEVAC #16", - "MEDEVAC RED #1", - "MEDEVAC RED #2", - "MEDEVAC RED #3", - "MEDEVAC RED #4", - "MEDEVAC RED #5", - "MEDEVAC RED #6", - "MEDEVAC RED #7", - "MEDEVAC RED #8", - "MEDEVAC RED #9", - "MEDEVAC RED #10", - "MEDEVAC RED #11", - "MEDEVAC RED #12", - "MEDEVAC RED #13", - "MEDEVAC RED #14", - "MEDEVAC RED #15", - "MEDEVAC RED #16", - "MEDEVAC RED #17", - "MEDEVAC RED #18", - "MEDEVAC RED #19", - "MEDEVAC RED #20", - "MEDEVAC RED #21", + "MEDEVAC RED #1", + "MEDEVAC RED #2", + "MEDEVAC RED #3", + "MEDEVAC RED #4", + "MEDEVAC RED #5", + "MEDEVAC RED #6", + "MEDEVAC RED #7", + "MEDEVAC RED #8", + "MEDEVAC RED #9", + "MEDEVAC RED #10", + "MEDEVAC RED #11", + "MEDEVAC RED #12", + "MEDEVAC RED #13", + "MEDEVAC RED #14", + "MEDEVAC RED #15", + "MEDEVAC RED #16", + "MEDEVAC RED #17", + "MEDEVAC RED #18", + "MEDEVAC RED #19", + "MEDEVAC RED #20", + "MEDEVAC RED #21", - "MEDEVAC BLUE #1", - "MEDEVAC BLUE #2", - "MEDEVAC BLUE #3", - "MEDEVAC BLUE #4", - "MEDEVAC BLUE #5", - "MEDEVAC BLUE #6", - "MEDEVAC BLUE #7", - "MEDEVAC BLUE #8", - "MEDEVAC BLUE #9", - "MEDEVAC BLUE #10", - "MEDEVAC BLUE #11", - "MEDEVAC BLUE #12", - "MEDEVAC BLUE #13", - "MEDEVAC BLUE #14", - "MEDEVAC BLUE #15", - "MEDEVAC BLUE #16", - "MEDEVAC BLUE #17", - "MEDEVAC BLUE #18", - "MEDEVAC BLUE #19", - "MEDEVAC BLUE #20", - "MEDEVAC BLUE #21", + "MEDEVAC BLUE #1", + "MEDEVAC BLUE #2", + "MEDEVAC BLUE #3", + "MEDEVAC BLUE #4", + "MEDEVAC BLUE #5", + "MEDEVAC BLUE #6", + "MEDEVAC BLUE #7", + "MEDEVAC BLUE #8", + "MEDEVAC BLUE #9", + "MEDEVAC BLUE #10", + "MEDEVAC BLUE #11", + "MEDEVAC BLUE #12", + "MEDEVAC BLUE #13", + "MEDEVAC BLUE #14", + "MEDEVAC BLUE #15", + "MEDEVAC BLUE #16", + "MEDEVAC BLUE #17", + "MEDEVAC BLUE #18", + "MEDEVAC BLUE #19", + "MEDEVAC BLUE #20", + "MEDEVAC BLUE #21", - -- *** AI transports names (different names only to ease identification in mission) *** + -- *** AI transports names (different names only to ease identification in mission) *** - -- Use any of the predefined names or set your own ones - "transport1", - "transport2", - "transport3", - "transport4", - "transport5", - "transport6", - "transport7", - "transport8", - "transport9", - "transport10", + -- Use any of the predefined names or set your own ones + "transport1", + "transport2", + "transport3", + "transport4", + "transport5", + "transport6", + "transport7", + "transport8", + "transport9", + "transport10", - "transport11", - "transport12", - "transport13", - "transport14", - "transport15", - "transport16", - "transport17", - "transport18", - "transport19", - "transport20", + "transport11", + "transport12", + "transport13", + "transport14", + "transport15", + "transport16", + "transport17", + "transport18", + "transport19", + "transport20", - "transport21", - "transport22", - "transport23", - "transport24", - "transport25", + "transport21", + "transport22", + "transport23", + "transport24", + "transport25", } -- *************** Optional Extractable GROUPS ***************** -- Use any of the predefined names or set your own ones ctld.extractableGroups = { - "extract1", - "extract2", - "extract3", - "extract4", - "extract5", - "extract6", - "extract7", - "extract8", - "extract9", - "extract10", + "extract1", + "extract2", + "extract3", + "extract4", + "extract5", + "extract6", + "extract7", + "extract8", + "extract9", + "extract10", - "extract11", - "extract12", - "extract13", - "extract14", - "extract15", - "extract16", - "extract17", - "extract18", - "extract19", - "extract20", + "extract11", + "extract12", + "extract13", + "extract14", + "extract15", + "extract16", + "extract17", + "extract18", + "extract19", + "extract20", - "extract21", - "extract22", - "extract23", - "extract24", - "extract25", + "extract21", + "extract22", + "extract23", + "extract24", + "extract25", } -- ************** Logistics UNITS FOR CRATE SPAWNING ****************** -- Use any of the predefined names or set your own ones -- When a logistic unit is destroyed, you will no longer be able to spawn crates -ctld.dynamicLogisticUnitsIndex = 0 -- This is the unit that will be spawned first and then subsequent units will be from the next in the list +ctld.dynamicLogisticUnitsIndex = 0 -- This is the unit that will be spawned first and then subsequent units will be from the next in the list ctld.logisticUnits = { - "logistic1", - "logistic2", - "logistic3", - "logistic4", - "logistic5", - "logistic6", - "logistic7", - "logistic8", - "logistic9", - "logistic10", + "logistic1", + "logistic2", + "logistic3", + "logistic4", + "logistic5", + "logistic6", + "logistic7", + "logistic8", + "logistic9", + "logistic10", } -- ************** UNITS ABLE TO TRANSPORT VEHICLES ****************** @@ -792,9 +792,9 @@ ctld.logisticUnits = { -- units db has all the names or you can extract a mission.miz file by making it a zip and looking -- in the contained mission file ctld.vehicleTransportEnabled = { - "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR - "Hercules", - "UH-1H", + "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR + "Hercules", + "UH-1H", } -- ************** Units able to use DCS dynamic cargo system ****************** @@ -802,7 +802,7 @@ ctld.vehicleTransportEnabled = { -- Units listed here will spawn a cargo static that can be loaded with the standard DCS cargo system -- We will also use this to make modifications to the menu and other checks and messages ctld.dynamicCargoUnits = { - "CH-47Fbl1", + "CH-47Fbl1", } -- ************** Maximum Units SETUP for UNITS ****************** @@ -814,65 +814,65 @@ ctld.dynamicCargoUnits = { -- Make sure the unit name is exactly right or it wont work ctld.unitLoadLimits = { - -- Remove the -- below to turn on options - -- ["SA342Mistral"] = 4, - -- ["SA342L"] = 4, - -- ["SA342M"] = 4, + -- Remove the -- below to turn on options + -- ["SA342Mistral"] = 4, + -- ["SA342L"] = 4, + -- ["SA342M"] = 4, - --%%%%% MODS %%%%% - --["Bronco-OV-10A"] = 4, - ["Hercules"] = 30, - --["SK-60"] = 1, - ["UH-60L"] = 12, - --["T-45"] = 1, + --%%%%% MODS %%%%% + --["Bronco-OV-10A"] = 4, + ["Hercules"] = 30, + --["SK-60"] = 1, + ["UH-60L"] = 12, + --["T-45"] = 1, - --%%%%% CHOPPERS %%%%% - ["Mi-8MT"] = 16, - ["Mi-24P"] = 10, - --["SA342L"] = 4, - --["SA342M"] = 4, - --["SA342Mistral"] = 4, - --["SA342Minigun"] = 3, - ["UH-1H"] = 8, - ["CH-47Fbl1"] = 33, + --%%%%% CHOPPERS %%%%% + ["Mi-8MT"] = 16, + ["Mi-24P"] = 10, + --["SA342L"] = 4, + --["SA342M"] = 4, + --["SA342Mistral"] = 4, + --["SA342Minigun"] = 3, + ["UH-1H"] = 8, + ["CH-47Fbl1"] = 33, - --%%%%% AIRCRAFTS %%%%% - --["C-101EB"] = 1, - --["C-101CC"] = 1, - --["Christen Eagle II"] = 1, - --["L-39C"] = 1, - --["L-39ZA"] = 1, - --["MB-339A"] = 1, - --["MB-339APAN"] = 1, - --["Mirage-F1B"] = 1, - --["Mirage-F1BD"] = 1, - --["Mirage-F1BE"] = 1, - --["Mirage-F1BQ"] = 1, - --["Mirage-F1DDA"] = 1, - --["Su-25T"] = 1, - --["Yak-52"] = 1, + --%%%%% AIRCRAFTS %%%%% + --["C-101EB"] = 1, + --["C-101CC"] = 1, + --["Christen Eagle II"] = 1, + --["L-39C"] = 1, + --["L-39ZA"] = 1, + --["MB-339A"] = 1, + --["MB-339APAN"] = 1, + --["Mirage-F1B"] = 1, + --["Mirage-F1BD"] = 1, + --["Mirage-F1BE"] = 1, + --["Mirage-F1BQ"] = 1, + --["Mirage-F1DDA"] = 1, + --["Su-25T"] = 1, + --["Yak-52"] = 1, - --%%%%% WARBIRDS %%%%% - --["Bf-109K-4"] = 1, - --["Fw 190A8"] = 1, - --["FW-190D9"] = 1, - --["I-16"] = 1, - --["MosquitoFBMkVI"] = 1, - --["P-47D-30"] = 1, - --["P-47D-40"] = 1, - --["P-51D"] = 1, - --["P-51D-30-NA"] = 1, - --["SpitfireLFMkIX"] = 1, - --["SpitfireLFMkIXCW"] = 1, - --["TF-51D"] = 1, + --%%%%% WARBIRDS %%%%% + --["Bf-109K-4"] = 1, + --["Fw 190A8"] = 1, + --["FW-190D9"] = 1, + --["I-16"] = 1, + --["MosquitoFBMkVI"] = 1, + --["P-47D-30"] = 1, + --["P-47D-40"] = 1, + --["P-51D"] = 1, + --["P-51D-30-NA"] = 1, + --["SpitfireLFMkIX"] = 1, + --["SpitfireLFMkIXCW"] = 1, + --["TF-51D"] = 1, } -- Put the name of the Unit you want to enable loading multiple crates ctld.internalCargoLimits = { - -- Remove the -- below to turn on options - ["Mi-8MT"] = 2, - ["CH-47Fbl1"] = 8, + -- Remove the -- below to turn on options + ["Mi-8MT"] = 2, + ["CH-47Fbl1"] = 8, } @@ -891,59 +891,59 @@ ctld.internalCargoLimits = { ctld.unitActions = { - -- Remove the -- below to turn on options - -- ["SA342Mistral"] = {crates=true, troops=true}, - -- ["SA342L"] = {crates=false, troops=true}, - -- ["SA342M"] = {crates=false, troops=true}, + -- Remove the -- below to turn on options + -- ["SA342Mistral"] = {crates=true, troops=true}, + -- ["SA342L"] = {crates=false, troops=true}, + -- ["SA342M"] = {crates=false, troops=true}, - --%%%%% MODS %%%%% - --["Bronco-OV-10A"] = {crates=true, troops=true}, - ["Hercules"] = {crates=true, troops=true}, - ["SK-60"] = {crates=true, troops=true}, - ["UH-60L"] = {crates=true, troops=true}, - --["T-45"] = {crates=true, troops=true}, + --%%%%% MODS %%%%% + --["Bronco-OV-10A"] = {crates=true, troops=true}, + ["Hercules"] = {crates=true, troops=true}, + ["SK-60"] = {crates=true, troops=true}, + ["UH-60L"] = {crates=true, troops=true}, + --["T-45"] = {crates=true, troops=true}, - --%%%%% CHOPPERS %%%%% - --["Ka-50"] = {crates=true, troops=false}, - --["Ka-50_3"] = {crates=true, troops=false}, - ["Mi-8MT"] = {crates=true, troops=true}, - ["Mi-24P"] = {crates=true, troops=true}, - --["SA342L"] = {crates=false, troops=true}, - --["SA342M"] = {crates=false, troops=true}, - --["SA342Mistral"] = {crates=false, troops=true}, - --["SA342Minigun"] = {crates=false, troops=true}, - ["UH-1H"] = {crates=true, troops=true}, - ["CH-47Fbl1"] = {crates=true, troops=true}, + --%%%%% CHOPPERS %%%%% + --["Ka-50"] = {crates=true, troops=false}, + --["Ka-50_3"] = {crates=true, troops=false}, + ["Mi-8MT"] = {crates=true, troops=true}, + ["Mi-24P"] = {crates=true, troops=true}, + --["SA342L"] = {crates=false, troops=true}, + --["SA342M"] = {crates=false, troops=true}, + --["SA342Mistral"] = {crates=false, troops=true}, + --["SA342Minigun"] = {crates=false, troops=true}, + ["UH-1H"] = {crates=true, troops=true}, + ["CH-47Fbl1"] = {crates=true, troops=true}, - --%%%%% AIRCRAFTS %%%%% - --["C-101EB"] = {crates=true, troops=true}, - --["C-101CC"] = {crates=true, troops=true}, - --["Christen Eagle II"] = {crates=true, troops=true}, - --["L-39C"] = {crates=true, troops=true}, - --["L-39ZA"] = {crates=true, troops=true}, - --["MB-339A"] = {crates=true, troops=true}, - --["MB-339APAN"] = {crates=true, troops=true}, - --["Mirage-F1B"] = {crates=true, troops=true}, - --["Mirage-F1BD"] = {crates=true, troops=true}, - --["Mirage-F1BE"] = {crates=true, troops=true}, - --["Mirage-F1BQ"] = {crates=true, troops=true}, - --["Mirage-F1DDA"] = {crates=true, troops=true}, - --["Su-25T"]= {crates=true, troops=false}, - --["Yak-52"] = {crates=true, troops=true}, + --%%%%% AIRCRAFTS %%%%% + --["C-101EB"] = {crates=true, troops=true}, + --["C-101CC"] = {crates=true, troops=true}, + --["Christen Eagle II"] = {crates=true, troops=true}, + --["L-39C"] = {crates=true, troops=true}, + --["L-39ZA"] = {crates=true, troops=true}, + --["MB-339A"] = {crates=true, troops=true}, + --["MB-339APAN"] = {crates=true, troops=true}, + --["Mirage-F1B"] = {crates=true, troops=true}, + --["Mirage-F1BD"] = {crates=true, troops=true}, + --["Mirage-F1BE"] = {crates=true, troops=true}, + --["Mirage-F1BQ"] = {crates=true, troops=true}, + --["Mirage-F1DDA"] = {crates=true, troops=true}, + --["Su-25T"]= {crates=true, troops=false}, + --["Yak-52"] = {crates=true, troops=true}, - --%%%%% WARBIRDS %%%%% - --["Bf-109K-4"] = {crates=true, troops=false}, - --["Fw 190A8"] = {crates=true, troops=false}, - --["FW-190D9"] = {crates=true, troops=false}, - --["I-16"] = {crates=true, troops=false}, - --["MosquitoFBMkVI"] = {crates=true, troops=true}, - --["P-47D-30"] = {crates=true, troops=false}, - --["P-47D-40"] = {crates=true, troops=false}, - --["P-51D"] = {crates=true, troops=false}, - --["P-51D-30-NA"] = {crates=true, troops=false}, - --["SpitfireLFMkIX"] = {crates=true, troops=false}, - --["SpitfireLFMkIXCW"] = {crates=true, troops=false}, - --["TF-51D"] = {crates=true, troops=true}, + --%%%%% WARBIRDS %%%%% + --["Bf-109K-4"] = {crates=true, troops=false}, + --["Fw 190A8"] = {crates=true, troops=false}, + --["FW-190D9"] = {crates=true, troops=false}, + --["I-16"] = {crates=true, troops=false}, + --["MosquitoFBMkVI"] = {crates=true, troops=true}, + --["P-47D-30"] = {crates=true, troops=false}, + --["P-47D-40"] = {crates=true, troops=false}, + --["P-51D"] = {crates=true, troops=false}, + --["P-51D-30-NA"] = {crates=true, troops=false}, + --["SpitfireLFMkIX"] = {crates=true, troops=false}, + --["SpitfireLFMkIXCW"] = {crates=true, troops=false}, + --["TF-51D"] = {crates=true, troops=true}, } -- ************** WEIGHT CALCULATIONS FOR INFANTRY GROUPS ****************** @@ -978,22 +978,22 @@ ctld.JTAC_WEIGHT = 15 -- kg -- You can also add an optional coalition side to limit the group to one side -- for the side - 2 is BLUE and 1 is RED ctld.loadableGroups = { - {name = ctld.i18n_translate("Standard Group"), inf = 6, mg = 2, at = 2 }, -- will make a loadable group with 6 infantry, 2 MGs and 2 anti-tank for both coalitions - {name = ctld.i18n_translate("Anti Air"), inf = 2, aa = 3 }, - {name = ctld.i18n_translate("Anti Tank"), inf = 2, at = 6 }, - {name = ctld.i18n_translate("Mortar Squad"), mortar = 6 }, - {name = ctld.i18n_translate("JTAC Group"), inf = 4, jtac = 1 }, -- will make a loadable group with 4 infantry and a JTAC soldier for both coalitions - {name = ctld.i18n_translate("Single JTAC"), jtac = 1 }, -- will make a loadable group witha single JTAC soldier for both coalitions - {name = ctld.i18n_translate("2x - Standard Groups"), inf = 12, mg = 4, at = 4 }, - {name = ctld.i18n_translate("2x - Anti Air"), inf = 4, aa = 6 }, - {name = ctld.i18n_translate("2x - Anti Tank"), inf = 4, at = 12 }, - {name = ctld.i18n_translate("2x - Standard Groups + 2x Mortar"), inf = 12, mg = 4, at = 4, mortar = 12 }, - {name = ctld.i18n_translate("3x - Standard Groups"), inf = 18, mg = 6, at = 6 }, - {name = ctld.i18n_translate("3x - Anti Air"), inf = 6, aa = 9 }, - {name = ctld.i18n_translate("3x - Anti Tank"), inf = 6, at = 18 }, - {name = ctld.i18n_translate("3x - Mortar Squad"), mortar = 18}, - {name = ctld.i18n_translate("5x - Mortar Squad"), mortar = 30}, - -- {name = ctld.i18n_translate("Mortar Squad Red"), inf = 2, mortar = 5, side =1 }, --would make a group loadable by RED only + {name = ctld.i18n_translate("Standard Group"), inf = 6, mg = 2, at = 2 }, -- will make a loadable group with 6 infantry, 2 MGs and 2 anti-tank for both coalitions + {name = ctld.i18n_translate("Anti Air"), inf = 2, aa = 3 }, + {name = ctld.i18n_translate("Anti Tank"), inf = 2, at = 6 }, + {name = ctld.i18n_translate("Mortar Squad"), mortar = 6 }, + {name = ctld.i18n_translate("JTAC Group"), inf = 4, jtac = 1 }, -- will make a loadable group with 4 infantry and a JTAC soldier for both coalitions + {name = ctld.i18n_translate("Single JTAC"), jtac = 1 }, -- will make a loadable group witha single JTAC soldier for both coalitions + {name = ctld.i18n_translate("2x - Standard Groups"), inf = 12, mg = 4, at = 4 }, + {name = ctld.i18n_translate("2x - Anti Air"), inf = 4, aa = 6 }, + {name = ctld.i18n_translate("2x - Anti Tank"), inf = 4, at = 12 }, + {name = ctld.i18n_translate("2x - Standard Groups + 2x Mortar"), inf = 12, mg = 4, at = 4, mortar = 12 }, + {name = ctld.i18n_translate("3x - Standard Groups"), inf = 18, mg = 6, at = 6 }, + {name = ctld.i18n_translate("3x - Anti Air"), inf = 6, aa = 9 }, + {name = ctld.i18n_translate("3x - Anti Tank"), inf = 6, at = 18 }, + {name = ctld.i18n_translate("3x - Mortar Squad"), mortar = 18}, + {name = ctld.i18n_translate("5x - Mortar Squad"), mortar = 30}, + -- {name = ctld.i18n_translate("Mortar Squad Red"), inf = 2, mortar = 5, side =1 }, --would make a group loadable by RED only } -- ************** SPAWNABLE CRATES ****************** @@ -1001,235 +1001,235 @@ ctld.loadableGroups = { -- when we unpack -- ctld.spawnableCrates = { - -- name of the sub menu on F10 for spawning crates - ["Combat Vehicles"] = { - --crates you can spawn - -- weight in KG - -- Desc is the description on the F10 MENU - -- unit is the model name of the unit to spawn - -- cratesRequired - if set requires that many crates of the same type within 100m of each other in order build the unit - -- side is optional but 2 is BLUE and 1 is RED + -- name of the sub menu on F10 for spawning crates + ["Combat Vehicles"] = { + --crates you can spawn + -- weight in KG + -- Desc is the description on the F10 MENU + -- unit is the model name of the unit to spawn + -- cratesRequired - if set requires that many crates of the same type within 100m of each other in order build the unit + -- side is optional but 2 is BLUE and 1 is RED - -- Some descriptions are filtered to determine if JTAC or not! + -- Some descriptions are filtered to determine if JTAC or not! - --- BLUE - { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types - { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, - { multiple = {1000.02, 1000.02}, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, - { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit="MaxxPro_MRAP", side = 2, cratesRequired = 2 }, - { multiple = {1000.03, 1000.03}, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, - { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit="LAV-25", side = 2, cratesRequired = 3 }, - { multiple = {1000.04, 1000.04, 1000.04}, desc = ctld.i18n_translate("Med Tank - LAV-25 - All crates"), side = 2 }, - { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit="M1A2C_SEP_V3", side = 2, cratesRequired = 4 }, - { multiple = {1000.05, 1000.05, 1000.05, 1000.05}, desc = ctld.i18n_translate("Heavy Tank - Abrams - All crates"), side = 2 }, + --- BLUE + { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types + { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, + { multiple = {1000.02, 1000.02}, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, + { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit="MaxxPro_MRAP", side = 2, cratesRequired = 2 }, + { multiple = {1000.03, 1000.03}, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, + { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit="LAV-25", side = 2, cratesRequired = 3 }, + { multiple = {1000.04, 1000.04, 1000.04}, desc = ctld.i18n_translate("Med Tank - LAV-25 - All crates"), side = 2 }, + { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit="M1A2C_SEP_V3", side = 2, cratesRequired = 4 }, + { multiple = {1000.05, 1000.05, 1000.05, 1000.05}, desc = ctld.i18n_translate("Heavy Tank - Abrams - All crates"), side = 2 }, - --- RED - { weight = 1000.11, desc = ctld.i18n_translate("BTR-D"), unit = "BTR_D", side = 1 }, - { weight = 1000.12, desc = ctld.i18n_translate("BRDM-2"), unit = "BRDM-2", side = 1 }, - -- need more redfor! - }, - ["Support"] = { - --- BLUE - { weight = 1001.01, desc = ctld.i18n_translate("Hummer - JTAC"), unit = "Hummer", side = 2, cratesRequired = 2 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled - { multiple = {1001.01, 1001.01}, desc = ctld.i18n_translate("Hummer - JTAC - All crates"), side = 2 }, - { weight = 1001.02, desc = ctld.i18n_translate("M-818 Ammo Truck"), unit = "M 818", side = 2, cratesRequired = 2 }, - { multiple = {1001.02, 1001.02}, desc = ctld.i18n_translate("M-818 Ammo Truck - All crates"), side = 2 }, - { weight = 1001.03, desc = ctld.i18n_translate("M-978 Tanker"), unit = "M978 HEMTT Tanker", side = 2, cratesRequired = 2 }, - { multiple = {1001.03, 1001.03}, desc = ctld.i18n_translate("M-978 Tanker - All crates"), side = 2 }, + --- RED + { weight = 1000.11, desc = ctld.i18n_translate("BTR-D"), unit = "BTR_D", side = 1 }, + { weight = 1000.12, desc = ctld.i18n_translate("BRDM-2"), unit = "BRDM-2", side = 1 }, + -- need more redfor! + }, + ["Support"] = { + --- BLUE + { weight = 1001.01, desc = ctld.i18n_translate("Hummer - JTAC"), unit = "Hummer", side = 2, cratesRequired = 2 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled + { multiple = {1001.01, 1001.01}, desc = ctld.i18n_translate("Hummer - JTAC - All crates"), side = 2 }, + { weight = 1001.02, desc = ctld.i18n_translate("M-818 Ammo Truck"), unit = "M 818", side = 2, cratesRequired = 2 }, + { multiple = {1001.02, 1001.02}, desc = ctld.i18n_translate("M-818 Ammo Truck - All crates"), side = 2 }, + { weight = 1001.03, desc = ctld.i18n_translate("M-978 Tanker"), unit = "M978 HEMTT Tanker", side = 2, cratesRequired = 2 }, + { multiple = {1001.03, 1001.03}, desc = ctld.i18n_translate("M-978 Tanker - All crates"), side = 2 }, - --- RED - { weight = 1001.11, desc = ctld.i18n_translate("SKP-11 - JTAC"), unit = "SKP-11", side = 1 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled - { weight = 1001.12, desc = ctld.i18n_translate("Ural-375 Ammo Truck"), unit = "Ural-375", side = 1, cratesRequired = 2 }, - { multiple = {1001.12, 1001.12}, desc = ctld.i18n_translate("Ural-375 Ammo Truck - All crates"), side = 1 }, - { weight = 1001.13, desc = ctld.i18n_translate("KAMAZ Ammo Truck"), unit = "KAMAZ Truck", side = 1, cratesRequired = 2 }, + --- RED + { weight = 1001.11, desc = ctld.i18n_translate("SKP-11 - JTAC"), unit = "SKP-11", side = 1 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled + { weight = 1001.12, desc = ctld.i18n_translate("Ural-375 Ammo Truck"), unit = "Ural-375", side = 1, cratesRequired = 2 }, + { multiple = {1001.12, 1001.12}, desc = ctld.i18n_translate("Ural-375 Ammo Truck - All crates"), side = 1 }, + { weight = 1001.13, desc = ctld.i18n_translate("KAMAZ Ammo Truck"), unit = "KAMAZ Truck", side = 1, cratesRequired = 2 }, - --- Both - { weight = 1001.21, desc = ctld.i18n_translate("EWR Radar"), unit="FPS-117", cratesRequired = 3 }, - { multiple = {1001.21, 1001.21, 1001.21}, desc = ctld.i18n_translate("EWR Radar - All crates") }, - { weight = 1001.22, desc = ctld.i18n_translate("FOB Crate - Small"), unit = "FOB-SMALL" }, -- Builds a FOB! - requires 3 * ctld.cratesRequiredForFOB + --- Both + { weight = 1001.21, desc = ctld.i18n_translate("EWR Radar"), unit="FPS-117", cratesRequired = 3 }, + { multiple = {1001.21, 1001.21, 1001.21}, desc = ctld.i18n_translate("EWR Radar - All crates") }, + { weight = 1001.22, desc = ctld.i18n_translate("FOB Crate - Small"), unit = "FOB-SMALL" }, -- Builds a FOB! - requires 3 * ctld.cratesRequiredForFOB - }, - ["Artillery"] = { - --- BLUE - { weight = 1002.01, desc = ctld.i18n_translate("MLRS"), unit = "MLRS", side=2, cratesRequired = 3 }, - { multiple = {1002.01, 1002.01, 1002.01}, desc = ctld.i18n_translate("MLRS - All crates"), side=2 }, - { weight = 1002.02, desc = ctld.i18n_translate("SpGH DANA"), unit = "SpGH_Dana", side=2, cratesRequired = 3 }, - { multiple = {1002.02, 1002.02, 1002.02}, desc = ctld.i18n_translate("SpGH DANA - All crates"), side=2 }, - { weight = 1002.03, desc = ctld.i18n_translate("T155 Firtina"), unit = "T155_Firtina", side=2, cratesRequired = 3 }, - { multiple = {1002.03, 1002.03, 1002.03}, desc = ctld.i18n_translate("T155 Firtina - All crates"), side=2 }, - { weight = 1002.04, desc = ctld.i18n_translate("Howitzer"), unit = "M-109", side=2, cratesRequired = 3 }, - { multiple = {1002.04, 1002.04, 1002.04}, desc = ctld.i18n_translate("Howitzer - All crates"), side=2 }, + }, + ["Artillery"] = { + --- BLUE + { weight = 1002.01, desc = ctld.i18n_translate("MLRS"), unit = "MLRS", side=2, cratesRequired = 3 }, + { multiple = {1002.01, 1002.01, 1002.01}, desc = ctld.i18n_translate("MLRS - All crates"), side=2 }, + { weight = 1002.02, desc = ctld.i18n_translate("SpGH DANA"), unit = "SpGH_Dana", side=2, cratesRequired = 3 }, + { multiple = {1002.02, 1002.02, 1002.02}, desc = ctld.i18n_translate("SpGH DANA - All crates"), side=2 }, + { weight = 1002.03, desc = ctld.i18n_translate("T155 Firtina"), unit = "T155_Firtina", side=2, cratesRequired = 3 }, + { multiple = {1002.03, 1002.03, 1002.03}, desc = ctld.i18n_translate("T155 Firtina - All crates"), side=2 }, + { weight = 1002.04, desc = ctld.i18n_translate("Howitzer"), unit = "M-109", side=2, cratesRequired = 3 }, + { multiple = {1002.04, 1002.04, 1002.04}, desc = ctld.i18n_translate("Howitzer - All crates"), side=2 }, - --- RED - { weight = 1002.11, desc = ctld.i18n_translate("SPH 2S19 Msta"), unit = "SAU Msta", side = 1, cratesRequired = 3 }, - { multiple = {1002.11, 1002.11, 1002.11}, desc = ctld.i18n_translate("SPH 2S19 Msta - All crates"), side=1 }, + --- RED + { weight = 1002.11, desc = ctld.i18n_translate("SPH 2S19 Msta"), unit = "SAU Msta", side = 1, cratesRequired = 3 }, + { multiple = {1002.11, 1002.11, 1002.11}, desc = ctld.i18n_translate("SPH 2S19 Msta - All crates"), side=1 }, - }, - ["SAM short range"] = { - --- BLUE - { weight = 1003.01, desc = ctld.i18n_translate("M1097 Avenger"), unit = "M1097 Avenger", side = 2, cratesRequired = 3 }, - { multiple = {1003.01, 1003.01, 1003.01}, desc = ctld.i18n_translate("M1097 Avenger - All crates"), side=2 }, - { weight = 1003.02, desc = ctld.i18n_translate("M48 Chaparral"), unit = "M48 Chaparral", side = 2, cratesRequired = 2 }, - { multiple = {1003.02, 1003.02}, desc = ctld.i18n_translate("M48 Chaparral - All crates"), side=2 }, - { weight = 1003.03, desc = ctld.i18n_translate("Roland ADS"), unit = "Roland ADS", side = 2, cratesRequired = 3 }, - { multiple = {1003.03, 1003.03, 1003.03}, desc = ctld.i18n_translate("Roland ADS - All crates"), side=2 }, - { weight = 1003.04, desc = ctld.i18n_translate("Gepard AAA"), unit = "Gepard", side = 2, cratesRequired = 3 }, - { multiple = {1003.04, 1003.04, 1003.04}, desc = ctld.i18n_translate("Gepard AAA - All crates"), side=2 }, - { weight = 1003.05, desc = ctld.i18n_translate("LPWS C-RAM"), unit = "HEMTT_C-RAM_Phalanx", side = 2, cratesRequired = 3 }, - { multiple = {1003.05, 1003.05, 1003.05}, desc = ctld.i18n_translate("LPWS C-RAM - All crates"), side=2 }, + }, + ["SAM short range"] = { + --- BLUE + { weight = 1003.01, desc = ctld.i18n_translate("M1097 Avenger"), unit = "M1097 Avenger", side = 2, cratesRequired = 3 }, + { multiple = {1003.01, 1003.01, 1003.01}, desc = ctld.i18n_translate("M1097 Avenger - All crates"), side=2 }, + { weight = 1003.02, desc = ctld.i18n_translate("M48 Chaparral"), unit = "M48 Chaparral", side = 2, cratesRequired = 2 }, + { multiple = {1003.02, 1003.02}, desc = ctld.i18n_translate("M48 Chaparral - All crates"), side=2 }, + { weight = 1003.03, desc = ctld.i18n_translate("Roland ADS"), unit = "Roland ADS", side = 2, cratesRequired = 3 }, + { multiple = {1003.03, 1003.03, 1003.03}, desc = ctld.i18n_translate("Roland ADS - All crates"), side=2 }, + { weight = 1003.04, desc = ctld.i18n_translate("Gepard AAA"), unit = "Gepard", side = 2, cratesRequired = 3 }, + { multiple = {1003.04, 1003.04, 1003.04}, desc = ctld.i18n_translate("Gepard AAA - All crates"), side=2 }, + { weight = 1003.05, desc = ctld.i18n_translate("LPWS C-RAM"), unit = "HEMTT_C-RAM_Phalanx", side = 2, cratesRequired = 3 }, + { multiple = {1003.05, 1003.05, 1003.05}, desc = ctld.i18n_translate("LPWS C-RAM - All crates"), side=2 }, - --- RED - { weight = 1003.11, desc = ctld.i18n_translate("9K33 Osa"), unit = "Osa 9A33 ln", side = 1, cratesRequired = 3 }, - { multiple = {1003.11, 1003.11, 1003.11}, desc = ctld.i18n_translate("9K33 Osa - All crates"), side=1 }, - { weight = 1003.12, desc = ctld.i18n_translate("9P31 Strela-1"), unit = "Strela-1 9P31", side = 1, cratesRequired = 3 }, - { multiple = {1003.12, 1003.12, 1003.12}, desc = ctld.i18n_translate("9P31 Strela-1 - All crates"), side=1 }, - { weight = 1003.13, desc = ctld.i18n_translate("9K35M Strela-10"), unit = "Strela-10M3", side = 1, cratesRequired = 3 }, - { multiple = {1003.13, 1003.13, 1003.13}, desc = ctld.i18n_translate("9K35M Strela-10 - All crates"), side=1 }, - { weight = 1003.14, desc = ctld.i18n_translate("9K331 Tor"), unit = "Tor 9A331", side = 1, cratesRequired = 3 }, - { multiple = {1003.14, 1003.14, 1003.14}, desc = ctld.i18n_translate("9K331 Tor - All crates"), side=1 }, - { weight = 1003.15, desc = ctld.i18n_translate("2K22 Tunguska"), unit = "2S6 Tunguska", side = 1, cratesRequired = 3 }, - { multiple = {1003.15, 1003.15, 1003.15}, desc = ctld.i18n_translate("2K22 Tunguska - All crates"), side=1 }, - }, - ["SAM mid range"] = { - --- BLUE - -- HAWK System - { weight = 1004.01, desc = ctld.i18n_translate("HAWK Launcher"), unit = "Hawk ln", side = 2}, - { weight = 1004.02, desc = ctld.i18n_translate("HAWK Search Radar"), unit = "Hawk sr", side = 2 }, - { weight = 1004.03, desc = ctld.i18n_translate("HAWK Track Radar"), unit = "Hawk tr", side = 2 }, - { weight = 1004.04, desc = ctld.i18n_translate("HAWK PCP"), unit = "Hawk pcp" , side = 2 }, - { weight = 1004.05, desc = ctld.i18n_translate("HAWK CWAR"), unit = "Hawk cwar" , side = 2 }, - { weight = 1004.06, desc = ctld.i18n_translate("HAWK Repair"), unit = "HAWK Repair" , side = 2 }, - { multiple = {1004.01, 1004.02, 1004.03}, desc = ctld.i18n_translate("HAWK - All crates"), side = 2 }, - -- End of HAWK + --- RED + { weight = 1003.11, desc = ctld.i18n_translate("9K33 Osa"), unit = "Osa 9A33 ln", side = 1, cratesRequired = 3 }, + { multiple = {1003.11, 1003.11, 1003.11}, desc = ctld.i18n_translate("9K33 Osa - All crates"), side=1 }, + { weight = 1003.12, desc = ctld.i18n_translate("9P31 Strela-1"), unit = "Strela-1 9P31", side = 1, cratesRequired = 3 }, + { multiple = {1003.12, 1003.12, 1003.12}, desc = ctld.i18n_translate("9P31 Strela-1 - All crates"), side=1 }, + { weight = 1003.13, desc = ctld.i18n_translate("9K35M Strela-10"), unit = "Strela-10M3", side = 1, cratesRequired = 3 }, + { multiple = {1003.13, 1003.13, 1003.13}, desc = ctld.i18n_translate("9K35M Strela-10 - All crates"), side=1 }, + { weight = 1003.14, desc = ctld.i18n_translate("9K331 Tor"), unit = "Tor 9A331", side = 1, cratesRequired = 3 }, + { multiple = {1003.14, 1003.14, 1003.14}, desc = ctld.i18n_translate("9K331 Tor - All crates"), side=1 }, + { weight = 1003.15, desc = ctld.i18n_translate("2K22 Tunguska"), unit = "2S6 Tunguska", side = 1, cratesRequired = 3 }, + { multiple = {1003.15, 1003.15, 1003.15}, desc = ctld.i18n_translate("2K22 Tunguska - All crates"), side=1 }, + }, + ["SAM mid range"] = { + --- BLUE + -- HAWK System + { weight = 1004.01, desc = ctld.i18n_translate("HAWK Launcher"), unit = "Hawk ln", side = 2}, + { weight = 1004.02, desc = ctld.i18n_translate("HAWK Search Radar"), unit = "Hawk sr", side = 2 }, + { weight = 1004.03, desc = ctld.i18n_translate("HAWK Track Radar"), unit = "Hawk tr", side = 2 }, + { weight = 1004.04, desc = ctld.i18n_translate("HAWK PCP"), unit = "Hawk pcp" , side = 2 }, + { weight = 1004.05, desc = ctld.i18n_translate("HAWK CWAR"), unit = "Hawk cwar" , side = 2 }, + { weight = 1004.06, desc = ctld.i18n_translate("HAWK Repair"), unit = "HAWK Repair" , side = 2 }, + { multiple = {1004.01, 1004.02, 1004.03}, desc = ctld.i18n_translate("HAWK - All crates"), side = 2 }, + -- End of HAWK - -- NASAMS Sysyem - { weight = 1004.11, desc = ctld.i18n_translate("NASAMS Launcher 120C"), unit = "NASAMS_LN_C", side = 2}, - { weight = 1004.12, desc = ctld.i18n_translate("NASAMS Search/Track Radar"), unit = "NASAMS_Radar_MPQ64F1", side = 2 }, - { weight = 1004.13, desc = ctld.i18n_translate("NASAMS Command Post"), unit = "NASAMS_Command_Post", side = 2 }, - { weight = 1004.14, desc = ctld.i18n_translate("NASAMS Repair"), unit = "NASAMS Repair", side = 2 }, - { multiple = {1004.11, 1004.12, 1004.13}, desc = ctld.i18n_translate("NASAMS - All crates"), side = 2 }, - -- End of NASAMS + -- NASAMS Sysyem + { weight = 1004.11, desc = ctld.i18n_translate("NASAMS Launcher 120C"), unit = "NASAMS_LN_C", side = 2}, + { weight = 1004.12, desc = ctld.i18n_translate("NASAMS Search/Track Radar"), unit = "NASAMS_Radar_MPQ64F1", side = 2 }, + { weight = 1004.13, desc = ctld.i18n_translate("NASAMS Command Post"), unit = "NASAMS_Command_Post", side = 2 }, + { weight = 1004.14, desc = ctld.i18n_translate("NASAMS Repair"), unit = "NASAMS Repair", side = 2 }, + { multiple = {1004.11, 1004.12, 1004.13}, desc = ctld.i18n_translate("NASAMS - All crates"), side = 2 }, + -- End of NASAMS - --- RED - -- KUB SYSTEM - { weight = 1004.21, desc = ctld.i18n_translate("KUB Launcher"), unit = "Kub 2P25 ln", side = 1}, - { weight = 1004.22, desc = ctld.i18n_translate("KUB Radar"), unit = "Kub 1S91 str", side = 1 }, - { weight = 1004.23, desc = ctld.i18n_translate("KUB Repair"), unit = "KUB Repair", side = 1}, - { multiple = {1004.21, 1004.22}, desc = ctld.i18n_translate("KUB - All crates"), side = 1 }, - -- End of KUB + --- RED + -- KUB SYSTEM + { weight = 1004.21, desc = ctld.i18n_translate("KUB Launcher"), unit = "Kub 2P25 ln", side = 1}, + { weight = 1004.22, desc = ctld.i18n_translate("KUB Radar"), unit = "Kub 1S91 str", side = 1 }, + { weight = 1004.23, desc = ctld.i18n_translate("KUB Repair"), unit = "KUB Repair", side = 1}, + { multiple = {1004.21, 1004.22}, desc = ctld.i18n_translate("KUB - All crates"), side = 1 }, + -- End of KUB - -- BUK System - { weight = 1004.31, desc = ctld.i18n_translate("BUK Launcher"), unit = "SA-11 Buk LN 9A310M1", side = 1}, - { weight = 1004.32, desc = ctld.i18n_translate("BUK Search Radar"), unit = "SA-11 Buk SR 9S18M1", side = 1}, - { weight = 1004.33, desc = ctld.i18n_translate("BUK CC Radar"), unit = "SA-11 Buk CC 9S470M1", side = 1}, - { weight = 1004.34, desc = ctld.i18n_translate("BUK Repair"), unit = "BUK Repair", side = 1}, - { multiple = {1004.31, 1004.32, 1004.33}, desc = ctld.i18n_translate("BUK - All crates"), side = 1 }, - -- END of BUK - }, - ["SAM long range"] = { - --- BLUE - -- Patriot System - { weight = 1005.01, desc = ctld.i18n_translate("Patriot Launcher"), unit = "Patriot ln", side = 2 }, - { weight = 1005.02, desc = ctld.i18n_translate("Patriot Radar"), unit = "Patriot str" , side = 2 }, - { weight = 1005.03, desc = ctld.i18n_translate("Patriot ECS"), unit = "Patriot ECS", side = 2 }, - -- { weight = 1005.04, desc = ctld.i18n_translate("Patriot ICC"), unit = "Patriot cp", side = 2 }, - -- { weight = 1005.05, desc = ctld.i18n_translate("Patriot EPP"), unit = "Patriot EPP", side = 2 }, - { weight = 1005.06, desc = ctld.i18n_translate("Patriot AMG (optional)"), unit = "Patriot AMG" , side = 2 }, - { weight = 1005.07, desc = ctld.i18n_translate("Patriot Repair"), unit = "Patriot Repair" , side = 2 }, - { multiple = {1005.01, 1005.02, 1005.03}, desc = ctld.i18n_translate("Patriot - All crates"), side = 2 }, - -- End of Patriot + -- BUK System + { weight = 1004.31, desc = ctld.i18n_translate("BUK Launcher"), unit = "SA-11 Buk LN 9A310M1", side = 1}, + { weight = 1004.32, desc = ctld.i18n_translate("BUK Search Radar"), unit = "SA-11 Buk SR 9S18M1", side = 1}, + { weight = 1004.33, desc = ctld.i18n_translate("BUK CC Radar"), unit = "SA-11 Buk CC 9S470M1", side = 1}, + { weight = 1004.34, desc = ctld.i18n_translate("BUK Repair"), unit = "BUK Repair", side = 1}, + { multiple = {1004.31, 1004.32, 1004.33}, desc = ctld.i18n_translate("BUK - All crates"), side = 1 }, + -- END of BUK + }, + ["SAM long range"] = { + --- BLUE + -- Patriot System + { weight = 1005.01, desc = ctld.i18n_translate("Patriot Launcher"), unit = "Patriot ln", side = 2 }, + { weight = 1005.02, desc = ctld.i18n_translate("Patriot Radar"), unit = "Patriot str" , side = 2 }, + { weight = 1005.03, desc = ctld.i18n_translate("Patriot ECS"), unit = "Patriot ECS", side = 2 }, + -- { weight = 1005.04, desc = ctld.i18n_translate("Patriot ICC"), unit = "Patriot cp", side = 2 }, + -- { weight = 1005.05, desc = ctld.i18n_translate("Patriot EPP"), unit = "Patriot EPP", side = 2 }, + { weight = 1005.06, desc = ctld.i18n_translate("Patriot AMG (optional)"), unit = "Patriot AMG" , side = 2 }, + { weight = 1005.07, desc = ctld.i18n_translate("Patriot Repair"), unit = "Patriot Repair" , side = 2 }, + { multiple = {1005.01, 1005.02, 1005.03}, desc = ctld.i18n_translate("Patriot - All crates"), side = 2 }, + -- End of Patriot - -- S-300 SYSTEM - { weight = 1005.11, desc = ctld.i18n_translate("S-300 Grumble TEL C"), unit = "S-300PS 5P85C ln", side = 1 }, - { weight = 1005.12, desc = ctld.i18n_translate("S-300 Grumble Flap Lid-A TR"), unit = "S-300PS 40B6M tr", side = 1 }, - { weight = 1005.13, desc = ctld.i18n_translate("S-300 Grumble Clam Shell SR"), unit = "S-300PS 40B6MD sr", side = 1 }, - { weight = 1005.14, desc = ctld.i18n_translate("S-300 Grumble Big Bird SR"), unit = "S-300PS 64H6E sr", side = 1 }, - { weight = 1005.15, desc = ctld.i18n_translate("S-300 Grumble C2"), unit = "S-300PS 54K6 cp", side = 1 }, - { weight = 1005.16, desc = ctld.i18n_translate("S-300 Repair"), unit = "S-300 Repair", side = 1 }, - { multiple = {1005.11, 1005.12, 1005.13, 1005.14, 1005.15}, desc = ctld.i18n_translate("Patriot - All crates"), side = 1 }, - -- End of S-300 - }, - ["Drone"] = { - --- BLUE MQ-9 Repear - { weight = 1006.01, desc = ctld.i18n_translate("MQ-9 Repear - JTAC"), unit = "MQ-9 Reaper", side = 2 }, - -- End of BLUE MQ-9 Repear + -- S-300 SYSTEM + { weight = 1005.11, desc = ctld.i18n_translate("S-300 Grumble TEL C"), unit = "S-300PS 5P85C ln", side = 1 }, + { weight = 1005.12, desc = ctld.i18n_translate("S-300 Grumble Flap Lid-A TR"), unit = "S-300PS 40B6M tr", side = 1 }, + { weight = 1005.13, desc = ctld.i18n_translate("S-300 Grumble Clam Shell SR"), unit = "S-300PS 40B6MD sr", side = 1 }, + { weight = 1005.14, desc = ctld.i18n_translate("S-300 Grumble Big Bird SR"), unit = "S-300PS 64H6E sr", side = 1 }, + { weight = 1005.15, desc = ctld.i18n_translate("S-300 Grumble C2"), unit = "S-300PS 54K6 cp", side = 1 }, + { weight = 1005.16, desc = ctld.i18n_translate("S-300 Repair"), unit = "S-300 Repair", side = 1 }, + { multiple = {1005.11, 1005.12, 1005.13, 1005.14, 1005.15}, desc = ctld.i18n_translate("Patriot - All crates"), side = 1 }, + -- End of S-300 + }, + ["Drone"] = { + --- BLUE MQ-9 Repear + { weight = 1006.01, desc = ctld.i18n_translate("MQ-9 Repear - JTAC"), unit = "MQ-9 Reaper", side = 2 }, + -- End of BLUE MQ-9 Repear - --- RED MQ-1A Predator - { weight = 1006.11, desc = ctld.i18n_translate("MQ-1A Predator - JTAC"), unit = "RQ-1A Predator", side = 1 }, - -- End of RED MQ-1A Predator - }, + --- RED MQ-1A Predator + { weight = 1006.11, desc = ctld.i18n_translate("MQ-1A Predator - JTAC"), unit = "RQ-1A Predator", side = 1 }, + -- End of RED MQ-1A Predator + }, } ctld.spawnableCratesModels = { - ["load"] = { - ["category"] = "Fortifications", - ["type"] = "Cargo04", - ["canCargo"] = false, - }, - ["sling"] = { - ["category"] = "Cargos", - ["shape_name"] = "bw_container_cargo", - ["type"] = "container_cargo", - ["canCargo"] = true - }, - ["dynamic"] = { - ["category"] = "Cargos", - ["type"] = "ammo_cargo", - ["canCargo"] = true - } + ["load"] = { + ["category"] = "Fortifications", + ["type"] = "Cargo04", + ["canCargo"] = false, + }, + ["sling"] = { + ["category"] = "Cargos", + ["shape_name"] = "bw_container_cargo", + ["type"] = "container_cargo", + ["canCargo"] = true + }, + ["dynamic"] = { + ["category"] = "Cargos", + ["type"] = "ammo_cargo", + ["canCargo"] = true + } } --[[ Placeholder for different type of cargo containers. Let's say pipes and trunks, fuel for FOB building - ["shape_name"] = "ab-212_cargo", - ["type"] = "uh1h_cargo" --new type for the container previously used + ["shape_name"] = "ab-212_cargo", + ["type"] = "uh1h_cargo" --new type for the container previously used - ["shape_name"] = "ammo_box_cargo", - ["type"] = "ammo_cargo", + ["shape_name"] = "ammo_box_cargo", + ["type"] = "ammo_cargo", - ["shape_name"] = "barrels_cargo", - ["type"] = "barrels_cargo", + ["shape_name"] = "barrels_cargo", + ["type"] = "barrels_cargo", - ["shape_name"] = "bw_container_cargo", - ["type"] = "container_cargo", + ["shape_name"] = "bw_container_cargo", + ["type"] = "container_cargo", - ["shape_name"] = "f_bar_cargo", - ["type"] = "f_bar_cargo", + ["shape_name"] = "f_bar_cargo", + ["type"] = "f_bar_cargo", - ["shape_name"] = "fueltank_cargo", - ["type"] = "fueltank_cargo", + ["shape_name"] = "fueltank_cargo", + ["type"] = "fueltank_cargo", - ["shape_name"] = "iso_container_cargo", - ["type"] = "iso_container", + ["shape_name"] = "iso_container_cargo", + ["type"] = "iso_container", - ["shape_name"] = "iso_container_small_cargo", - ["type"] = "iso_container_small", + ["shape_name"] = "iso_container_small_cargo", + ["type"] = "iso_container_small", - ["shape_name"] = "oiltank_cargo", - ["type"] = "oiltank_cargo", + ["shape_name"] = "oiltank_cargo", + ["type"] = "oiltank_cargo", - ["shape_name"] = "pipes_big_cargo", - ["type"] = "pipes_big_cargo", + ["shape_name"] = "pipes_big_cargo", + ["type"] = "pipes_big_cargo", - ["shape_name"] = "pipes_small_cargo", - ["type"] = "pipes_small_cargo", + ["shape_name"] = "pipes_small_cargo", + ["type"] = "pipes_small_cargo", - ["shape_name"] = "tetrapod_cargo", - ["type"] = "tetrapod_cargo", + ["shape_name"] = "tetrapod_cargo", + ["type"] = "tetrapod_cargo", - ["shape_name"] = "trunks_long_cargo", - ["type"] = "trunks_long_cargo", + ["shape_name"] = "trunks_long_cargo", + ["type"] = "trunks_long_cargo", - ["shape_name"] = "trunks_small_cargo", - ["type"] = "trunks_small_cargo", + ["shape_name"] = "trunks_small_cargo", + ["type"] = "trunks_small_cargo", ]]-- -- if the unit is on this list, it will be made into a JTAC when deployed ctld.jtacUnitTypes = { - "SKP", "Hummer", -- there are some wierd encoding issues so if you write SKP-11 it wont match as the - sign is encoded differently... - "MQ", "RQ" --"MQ-9 Repear", "RQ-1A Predator"} + "SKP", "Hummer", -- there are some wierd encoding issues so if you write SKP-11 it wont match as the - sign is encoded differently... + "MQ", "RQ" --"MQ-9 Repear", "RQ-1A Predator"} } -ctld.jtacDroneRadius = 1000 -- JTAC offset radius in meters for orbiting drones +ctld.jtacDroneRadius = 1000 -- JTAC offset radius in meters for orbiting drones ctld.jtacDroneAltitude = 7000 -- JTAC altitude in meters for orbiting drones -- *************************************************************** -- **************** Mission Editor Functions ********************* @@ -1254,39 +1254,39 @@ ctld.jtacDroneAltitude = 7000 -- JTAC altitude in meters for orbiting drones -- Spawns 1 machine gun, 2 anti tank, 3 anti air, 4 standard soldiers and 5 mortars -- function ctld.spawnGroupAtTrigger(_groupSide, _number, _triggerName, _searchRadius) - local _spawnTrigger = trigger.misc.getZone(_triggerName) -- trigger to use as reference position + local _spawnTrigger = trigger.misc.getZone(_triggerName) -- trigger to use as reference position - if _spawnTrigger == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find trigger called %1",_triggerName), 10) - return - end + if _spawnTrigger == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find trigger called %1",_triggerName), 10) + return + end - local _country - if _groupSide == "red" then - _groupSide = 1 - _country = 0 - else - _groupSide = 2 - _country = 2 - end + local _country + if _groupSide == "red" then + _groupSide = 1 + _country = 0 + else + _groupSide = 2 + _country = 2 + end - if _searchRadius < 0 then - _searchRadius = 0 - end + if _searchRadius < 0 then + _searchRadius = 0 + end - local _pos2 = { x = _spawnTrigger.point.x, y = _spawnTrigger.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + local _pos2 = { x = _spawnTrigger.point.x, y = _spawnTrigger.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - local _groupDetails = ctld.generateTroopTypes(_groupSide, _number, _country) + local _groupDetails = ctld.generateTroopTypes(_groupSide, _number, _country) - local _droppedTroops = ctld.spawnDroppedGroup(_pos3, _groupDetails, false, _searchRadius); + local _droppedTroops = ctld.spawnDroppedGroup(_pos3, _groupDetails, false, _searchRadius); - if _groupSide == 1 then - table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) - else - table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) - end + if _groupSide == 1 then + table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) + else + table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) + end end @@ -1309,28 +1309,28 @@ end -- Spawns 1 machine gun, 2 anti tank, 3 anti air, 4 standard soldiers and 5 mortars function ctld.spawnGroupAtPoint(_groupSide, _number, _point, _searchRadius) - local _country - if _groupSide == "red" then - _groupSide = 1 - _country = 0 - else - _groupSide = 2 - _country = 2 - end + local _country + if _groupSide == "red" then + _groupSide = 1 + _country = 0 + else + _groupSide = 2 + _country = 2 + end - if _searchRadius < 0 then - _searchRadius = 0 - end + if _searchRadius < 0 then + _searchRadius = 0 + end - local _groupDetails = ctld.generateTroopTypes(_groupSide, _number, _country) + local _groupDetails = ctld.generateTroopTypes(_groupSide, _number, _country) - local _droppedTroops = ctld.spawnDroppedGroup(_point, _groupDetails, false, _searchRadius); + local _droppedTroops = ctld.spawnDroppedGroup(_point, _groupDetails, false, _searchRadius); - if _groupSide == 1 then - table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) - else - table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) - end + if _groupSide == 1 then + table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) + else + table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) + end end @@ -1338,15 +1338,15 @@ end -- replaces any troops currently on board function ctld.preLoadTransport(_unitName, _number, _troops) - local _unit = ctld.getTransportUnit(_unitName) + local _unit = ctld.getTransportUnit(_unitName) - if _unit ~= nil then + if _unit ~= nil then - -- will replace any units currently on board - -- if not ctld.troopsOnboard(_unit,_troops) then - ctld.loadTroops(_unit, _troops, _number) - -- end - end + -- will replace any units currently on board + -- if not ctld.troopsOnboard(_unit,_troops) then + ctld.loadTroops(_unit, _troops, _number) + -- end + end end @@ -1357,50 +1357,50 @@ end -- This will now work for Mission Editor and Spawned Crates -- e.g. ctld.cratesInZone("DropZone1", 5) function ctld.cratesInZone(_zone, _flagNumber) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end - - local _zonePos = mist.utils.zoneToVec3(_zone) - - --ignore side, if crate has been used its discounted from the count - local _crateTables = { ctld.spawnedCratesRED, ctld.spawnedCratesBLUE, ctld.missionEditorCargoCrates } - - local _crateCount = 0 - - for _, _crates in pairs(_crateTables) do - - for _crateName, _dontUse in pairs(_crates) do - - --get crate - local _crate = ctld.getCrateObject(_crateName) - - --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude - if _crate ~= nil and _crate:getLife() > 0 - and (ctld.inAir(_crate) == false) then - - local _dist = ctld.getDistance(_crate:getPoint(), _zonePos) - - if _dist <= _triggerZone.radius then - _crateCount = _crateCount + 1 - end - end + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return end - end - --set flag stuff - trigger.action.setUserFlag(_flagNumber, _crateCount) + local _zonePos = mist.utils.zoneToVec3(_zone) - -- env.info("FLAG ".._flagNumber.." crates ".._crateCount) + --ignore side, if crate has been used its discounted from the count + local _crateTables = { ctld.spawnedCratesRED, ctld.spawnedCratesBLUE, ctld.missionEditorCargoCrates } - --retrigger in 5 seconds - timer.scheduleFunction(function(_args) + local _crateCount = 0 - ctld.cratesInZone(_args[1], _args[2]) - end, { _zone, _flagNumber }, timer.getTime() + 5) + for _, _crates in pairs(_crateTables) do + + for _crateName, _dontUse in pairs(_crates) do + + --get crate + local _crate = ctld.getCrateObject(_crateName) + + --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude + if _crate ~= nil and _crate:getLife() > 0 + and (ctld.inAir(_crate) == false) then + + local _dist = ctld.getDistance(_crate:getPoint(), _zonePos) + + if _dist <= _triggerZone.radius then + _crateCount = _crateCount + 1 + end + end + end + end + + --set flag stuff + trigger.action.setUserFlag(_flagNumber, _crateCount) + + -- env.info("FLAG ".._flagNumber.." crates ".._crateCount) + + --retrigger in 5 seconds + timer.scheduleFunction(function(_args) + + ctld.cratesInZone(_args[1], _args[2]) + end, { _zone, _flagNumber }, timer.getTime() + 5) end -- Creates an extraction zone @@ -1419,45 +1419,45 @@ end -- -- function ctld.createExtractZone(_zone, _flagNumber, _smoke) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end - - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - - trigger.action.setUserFlag(_flagNumber, 0) --start at 0 - - local _details = { point = _pos3, name = _zone, smoke = _smoke, flag = _flagNumber, radius = _triggerZone.radius} - - ctld.extractZones[_zone.."-".._flagNumber] = _details - - if _smoke ~= nil and _smoke > -1 then - - local _smokeFunction - - _smokeFunction = function(_args) - - local _extractDetails = ctld.extractZones[_zone.."-".._flagNumber] - -- check zone is still active - if _extractDetails == nil then - -- stop refreshing smoke, zone is done + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) return - end - - - trigger.action.smoke(_args.point, _args.smoke) - --refresh in 5 minutes - timer.scheduleFunction(_smokeFunction, _args, timer.getTime() + 300) end - --run local function - _smokeFunction(_details) - end + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + + trigger.action.setUserFlag(_flagNumber, 0) --start at 0 + + local _details = { point = _pos3, name = _zone, smoke = _smoke, flag = _flagNumber, radius = _triggerZone.radius} + + ctld.extractZones[_zone.."-".._flagNumber] = _details + + if _smoke ~= nil and _smoke > -1 then + + local _smokeFunction + + _smokeFunction = function(_args) + + local _extractDetails = ctld.extractZones[_zone.."-".._flagNumber] + -- check zone is still active + if _extractDetails == nil then + -- stop refreshing smoke, zone is done + return + end + + + trigger.action.smoke(_args.point, _args.smoke) + --refresh in 5 minutes + timer.scheduleFunction(_smokeFunction, _args, timer.getTime() + 300) + end + + --run local function + _smokeFunction(_details) + end end @@ -1474,13 +1474,13 @@ end -- function ctld.removeExtractZone(_zone,_flagNumber) - local _extractDetails = ctld.extractZones[_zone.."-".._flagNumber] + local _extractDetails = ctld.extractZones[_zone.."-".._flagNumber] - if _extractDetails ~= nil then - --remove zone - ctld.extractZones[_zone.."-".._flagNumber] = nil + if _extractDetails ~= nil then + --remove zone + ctld.extractZones[_zone.."-".._flagNumber] = nil - end + end end -- CONTINUOUS TRIGGER FUNCTION @@ -1491,43 +1491,43 @@ end -- Use: ctld.countDroppedGroupsInZone("Zone Name", flagBlue, flagRed) function ctld.countDroppedGroupsInZone(_zone, _blueFlag, _redFlag) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end - - local _zonePos = mist.utils.zoneToVec3(_zone) - - local _redCount = 0; - local _blueCount = 0; - - local _allGroups = {ctld.droppedTroopsRED,ctld.droppedTroopsBLUE,ctld.droppedVehiclesRED,ctld.droppedVehiclesBLUE} - for _, _extractGroups in pairs(_allGroups) do - for _,_groupName in pairs(_extractGroups) do - local _groupUnits = ctld.getGroup(_groupName) - - if #_groupUnits > 0 then - local _zonePos = mist.utils.zoneToVec3(_zone) - local _dist = ctld.getDistance(_groupUnits[1]:getPoint(), _zonePos) - - if _dist <= _triggerZone.radius then - - if (_groupUnits[1]:getCoalition() == 1) then - _redCount = _redCount + 1; - else - _blueCount = _blueCount + 1; - end - end - end + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return end - end - --set flag stuff - trigger.action.setUserFlag(_blueFlag, _blueCount) - trigger.action.setUserFlag(_redFlag, _redCount) - -- env.info("Groups in zone ".._blueCount.." ".._redCount) + local _zonePos = mist.utils.zoneToVec3(_zone) + + local _redCount = 0; + local _blueCount = 0; + + local _allGroups = {ctld.droppedTroopsRED,ctld.droppedTroopsBLUE,ctld.droppedVehiclesRED,ctld.droppedVehiclesBLUE} + for _, _extractGroups in pairs(_allGroups) do + for _,_groupName in pairs(_extractGroups) do + local _groupUnits = ctld.getGroup(_groupName) + + if #_groupUnits > 0 then + local _zonePos = mist.utils.zoneToVec3(_zone) + local _dist = ctld.getDistance(_groupUnits[1]:getPoint(), _zonePos) + + if _dist <= _triggerZone.radius then + + if (_groupUnits[1]:getCoalition() == 1) then + _redCount = _redCount + 1; + else + _blueCount = _blueCount + 1; + end + end + end + end + end + --set flag stuff + trigger.action.setUserFlag(_blueFlag, _blueCount) + trigger.action.setUserFlag(_redFlag, _redCount) + + -- env.info("Groups in zone ".._blueCount.." ".._redCount) end @@ -1538,54 +1538,54 @@ end -- Use: ctld.countDroppedUnitsInZone("Zone Name", flagBlue, flagRed) function ctld.countDroppedUnitsInZone(_zone, _blueFlag, _redFlag) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end - - local _zonePos = mist.utils.zoneToVec3(_zone) - - local _redCount = 0; - local _blueCount = 0; - - local _allGroups = {ctld.droppedTroopsRED,ctld.droppedTroopsBLUE,ctld.droppedVehiclesRED,ctld.droppedVehiclesBLUE} - - for _, _extractGroups in pairs(_allGroups) do - for _,_groupName in pairs(_extractGroups) do - local _groupUnits = ctld.getGroup(_groupName) - - if #_groupUnits > 0 then - - local _zonePos = mist.utils.zoneToVec3(_zone) - for _,_unit in pairs(_groupUnits) do - local _dist = ctld.getDistance(_unit:getPoint(), _zonePos) - - if _dist <= _triggerZone.radius then - - if (_unit:getCoalition() == 1) then - _redCount = _redCount + 1; - else - _blueCount = _blueCount + 1; - end - end - end - end + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return + end + + local _zonePos = mist.utils.zoneToVec3(_zone) + + local _redCount = 0; + local _blueCount = 0; + + local _allGroups = {ctld.droppedTroopsRED,ctld.droppedTroopsBLUE,ctld.droppedVehiclesRED,ctld.droppedVehiclesBLUE} + + for _, _extractGroups in pairs(_allGroups) do + for _,_groupName in pairs(_extractGroups) do + local _groupUnits = ctld.getGroup(_groupName) + + if #_groupUnits > 0 then + + local _zonePos = mist.utils.zoneToVec3(_zone) + for _,_unit in pairs(_groupUnits) do + local _dist = ctld.getDistance(_unit:getPoint(), _zonePos) + + if _dist <= _triggerZone.radius then + + if (_unit:getCoalition() == 1) then + _redCount = _redCount + 1; + else + _blueCount = _blueCount + 1; + end + end + end + end + end end - end - --set flag stuff - trigger.action.setUserFlag(_blueFlag, _blueCount) - trigger.action.setUserFlag(_redFlag, _redCount) + --set flag stuff + trigger.action.setUserFlag(_blueFlag, _blueCount) + trigger.action.setUserFlag(_redFlag, _redCount) - -- env.info("Units in zone ".._blueCount.." ".._redCount) + -- env.info("Units in zone ".._blueCount.." ".._redCount) end --*************************************************************** function ctld.getNextDynamicLogisticUnitIndex() - ctld.dynamicLogisticUnitsIndex = ctld.dynamicLogisticUnitsIndex + 1 - return ctld.dynamicLogisticUnitsIndex + ctld.dynamicLogisticUnitsIndex = ctld.dynamicLogisticUnitsIndex + 1 + return ctld.dynamicLogisticUnitsIndex end -- Creates a radio beacon on a random UHF - VHF and HF/FM frequency for homing @@ -1596,26 +1596,26 @@ end -- e.g. ctld.createRadioBeaconAtZone("beaconZoneBlue","blue", 20) will create a beacon at trigger zone "beaconZoneBlue" for the Blue side -- that will last 20 minutes function ctld.createRadioBeaconAtZone(_zone, _coalition, _batteryLife, _name) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return + end - local _zonePos = mist.utils.zoneToVec3(_zone) + local _zonePos = mist.utils.zoneToVec3(_zone) - ctld.beaconCount = ctld.beaconCount + 1 + ctld.beaconCount = ctld.beaconCount + 1 - if _name == nil or _name == "" then - _name = "Beacon #" .. ctld.beaconCount - end + if _name == nil or _name == "" then + _name = "Beacon #" .. ctld.beaconCount + end - if _coalition == "red" then - ctld.createRadioBeacon(_zonePos, 1, 0, _name, _batteryLife) --1440 - else - ctld.createRadioBeacon(_zonePos, 2, 2, _name, _batteryLife) --1440 - end + if _coalition == "red" then + ctld.createRadioBeacon(_zonePos, 1, 0, _name, _batteryLife) --1440 + else + ctld.createRadioBeacon(_zonePos, 2, 2, _name, _batteryLife) --1440 + end end @@ -1625,52 +1625,52 @@ end -- This is enable pickzone3 to be used as a pickup zone for the team set function ctld.activatePickupZone(_zoneName) - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_triggerZone) + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_triggerZone) + + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point + end - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point end - end - - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone or ship called %1", _zoneName), 10) - end - - for _, _zoneDetails in pairs(ctld.pickupZones) do - - if _zoneName == _zoneDetails[1] then - - --smoke could get messy if designer keeps calling this on an active zone, check its not active first - if _zoneDetails[4] == 1 then - -- they might have a continuous trigger so i've hidden the warning - return - end - - _zoneDetails[4] = 1 --activate zone - - if ctld.disableAllSmoke == true then --smoke disabled - return - end - - if _zoneDetails[2] >= 0 then - - -- Trigger smoke marker - -- This will cause an overlapping smoke marker on next refreshsmoke call - -- but will only happen once - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - - trigger.action.smoke(_pos3, _zoneDetails[2]) - end + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone or ship called %1", _zoneName), 10) + end + + for _, _zoneDetails in pairs(ctld.pickupZones) do + + if _zoneName == _zoneDetails[1] then + + --smoke could get messy if designer keeps calling this on an active zone, check its not active first + if _zoneDetails[4] == 1 then + -- they might have a continuous trigger so i've hidden the warning + return + end + + _zoneDetails[4] = 1 --activate zone + + if ctld.disableAllSmoke == true then --smoke disabled + return + end + + if _zoneDetails[2] >= 0 then + + -- Trigger smoke marker + -- This will cause an overlapping smoke marker on next refreshsmoke call + -- but will only happen once + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + + trigger.action.smoke(_pos3, _zoneDetails[2]) + end + end end - end end @@ -1682,61 +1682,61 @@ end -- once they are destroyed function ctld.deactivatePickupZone(_zoneName) - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_triggerZone) + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_triggerZone) + + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point + end - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point end - end - - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) - return - end - - for _, _zoneDetails in pairs(ctld.pickupZones) do - - if _zoneName == _zoneDetails[1] then - -- i'd just ignore it if its already been deactivated - _zoneDetails[4] = 0 --deactivate zone + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) + return + end + + for _, _zoneDetails in pairs(ctld.pickupZones) do + + if _zoneName == _zoneDetails[1] then + -- i'd just ignore it if its already been deactivated + _zoneDetails[4] = 0 --deactivate zone + end end - end end -- Change the remaining groups currently available for pickup at a zone -- e.g. ctld.changeRemainingGroupsForPickupZone("pickup1", 5) -- adds 5 groups -- ctld.changeRemainingGroupsForPickupZone("pickup1", -3) -- remove 3 groups function ctld.changeRemainingGroupsForPickupZone(_zoneName, _amount) - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_triggerZone) + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_triggerZone) + + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point + end - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point end - end - - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) - return - end - - for _, _zoneDetails in pairs(ctld.pickupZones) do - - if _zoneName == _zoneDetails[1] then - ctld.updateZoneCounter(_zoneName, _amount) + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) + return + end + + for _, _zoneDetails in pairs(ctld.pickupZones) do + + if _zoneName == _zoneDetails[1] then + ctld.updateZoneCounter(_zoneName, _amount) + end end - end end @@ -1747,68 +1747,68 @@ end -- This means that troops dropped within the radius of the zone will head to the center -- of the zone instead of searching for troops function ctld.activateWaypointZone(_zoneName) - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) - return - end - - for _, _zoneDetails in pairs(ctld.wpZones) do - - if _zoneName == _zoneDetails[1] then - - --smoke could get messy if designer keeps calling this on an active zone, check its not active first - if _zoneDetails[3] == 1 then - -- they might have a continuous trigger so i've hidden the warning return - end - - _zoneDetails[3] = 1 --activate zone - - if ctld.disableAllSmoke == true then --smoke disabled - return - end - - if _zoneDetails[2] >= 0 then - - -- Trigger smoke marker - -- This will cause an overlapping smoke marker on next refreshsmoke call - -- but will only happen once - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - - trigger.action.smoke(_pos3, _zoneDetails[2]) - end end - end + + for _, _zoneDetails in pairs(ctld.wpZones) do + + if _zoneName == _zoneDetails[1] then + + --smoke could get messy if designer keeps calling this on an active zone, check its not active first + if _zoneDetails[3] == 1 then + -- they might have a continuous trigger so i've hidden the warning + return + end + + _zoneDetails[3] = 1 --activate zone + + if ctld.disableAllSmoke == true then --smoke disabled + return + end + + if _zoneDetails[2] >= 0 then + + -- Trigger smoke marker + -- This will cause an overlapping smoke marker on next refreshsmoke call + -- but will only happen once + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + + trigger.action.smoke(_pos3, _zoneDetails[2]) + end + end + end end -- Deactivates a Waypoint zone -- Deactivates a Waypoint zone when called from a trigger -- EG: ctld.deactivateWaypointZone("wpzone3") --- This disables wpzone3 so that troops dropped in this zone will search for troops as normal +-- This disables wpzone3 so that troops dropped in this zone will search for troops as normal -- These functions can be called by triggers function ctld.deactivateWaypointZone(_zoneName) - local _triggerZone = trigger.misc.getZone(_zoneName) + local _triggerZone = trigger.misc.getZone(_zoneName) - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) - return - end - - for _, _zoneDetails in pairs(ctld.pickupZones) do - - if _zoneName == _zoneDetails[1] then - - _zoneDetails[3] = 0 --deactivate zone + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) + return + end + + for _, _zoneDetails in pairs(ctld.pickupZones) do + + if _zoneName == _zoneDetails[1] then + + _zoneDetails[3] = 0 --deactivate zone + end end - end end -- Continuous Trigger Function @@ -1817,30 +1817,30 @@ end -- The enemy must have Line or Sight to the unit to be detected function ctld.unloadInProximityToEnemy(_unitName,_distance) - local _unit = ctld.getTransportUnit(_unitName) + local _unit = ctld.getTransportUnit(_unitName) - if _unit ~= nil and _unit:getPlayerName() == nil then + if _unit ~= nil and _unit:getPlayerName() == nil then - -- no player name means AI! - -- the findNearest visible enemy you'd want to modify as it'll find enemies quite far away - -- limited by ctld.JTAC_maxDistance - local _nearestEnemy = ctld.findNearestVisibleEnemy(_unit,"all",_distance) + -- no player name means AI! + -- the findNearest visible enemy you'd want to modify as it'll find enemies quite far away + -- limited by ctld.JTAC_maxDistance + local _nearestEnemy = ctld.findNearestVisibleEnemy(_unit,"all",_distance) - if _nearestEnemy ~= nil then + if _nearestEnemy ~= nil then - if ctld.troopsOnboard(_unit, true) then - ctld.deployTroops(_unit, true) - return true - end + if ctld.troopsOnboard(_unit, true) then + ctld.deployTroops(_unit, true) + return true + end - if ctld.unitCanCarryVehicles(_unit) and ctld.troopsOnboard(_unit, false) then - ctld.deployTroops(_unit, false) - return true - end + if ctld.unitCanCarryVehicles(_unit) and ctld.troopsOnboard(_unit, false) then + ctld.deployTroops(_unit, false) + return true + end + end end - end - return false + return false end @@ -1850,42 +1850,42 @@ end -- when this function is called function ctld.unloadTransport(_unitName) - local _unit = ctld.getTransportUnit(_unitName) + local _unit = ctld.getTransportUnit(_unitName) - if _unit ~= nil then + if _unit ~= nil then - if ctld.troopsOnboard(_unit, true) then - ctld.unloadTroops({_unitName,true}) + if ctld.troopsOnboard(_unit, true) then + ctld.unloadTroops({_unitName,true}) + end + + if ctld.unitCanCarryVehicles(_unit) and ctld.troopsOnboard(_unit, false) then + ctld.unloadTroops({_unitName,false}) + end end - if ctld.unitCanCarryVehicles(_unit) and ctld.troopsOnboard(_unit, false) then - ctld.unloadTroops({_unitName,false}) - end - end - end -- Loads Troops and Vehicles from a zone or picks up nearby troops or vehicles function ctld.loadTransport(_unitName) - local _unit = ctld.getTransportUnit(_unitName) + local _unit = ctld.getTransportUnit(_unitName) - if _unit ~= nil then + if _unit ~= nil then - ctld.loadTroopsFromZone({ _unitName, true,"",true }) + ctld.loadTroopsFromZone({ _unitName, true,"",true }) + + if ctld.unitCanCarryVehicles(_unit) then + ctld.loadTroopsFromZone({ _unitName, false,"",true }) + end - if ctld.unitCanCarryVehicles(_unit) then - ctld.loadTroopsFromZone({ _unitName, false,"",true }) end - end - end -- adds a callback that will be called for many actions ingame function ctld.addCallback(_callback) - table.insert(ctld.callbacks,_callback) + table.insert(ctld.callbacks,_callback) end @@ -1896,38 +1896,38 @@ end -- e.g. ctld.spawnCrateAtZone("blue", 505,"triggerzone1") -- spawn a tow humvee at triggerzone1 for blue side -- function ctld.spawnCrateAtZone(_side, _weight,_zone) - local _spawnTrigger = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _spawnTrigger = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _spawnTrigger == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end + if _spawnTrigger == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return + end - local _crateType = ctld.crateLookupTable[tostring(_weight)] + local _crateType = ctld.crateLookupTable[tostring(_weight)] - if _crateType == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find crate with weight %1", _weight), 10) - return - end + if _crateType == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find crate with weight %1", _weight), 10) + return + end - local _country - if _side == "red" then - _side = 1 - _country = 0 - else - _side = 2 - _country = 2 - end + local _country + if _side == "red" then + _side = 1 + _country = 0 + else + _side = 2 + _country = 2 + end - local _pos2 = { x = _spawnTrigger.point.x, y = _spawnTrigger.point.z } - local _alt = land.getHeight(_pos2) - local _point = { x = _pos2.x, y = _alt, z = _pos2.y } + local _pos2 = { x = _spawnTrigger.point.x, y = _spawnTrigger.point.z } + local _alt = land.getHeight(_pos2) + local _point = { x = _pos2.x, y = _alt, z = _pos2.y } - local _unitId = ctld.getNextUnitId() + local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", _crateType.desc, _unitId) + local _name = string.format("%s #%i", _crateType.desc, _unitId) - ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side) + ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side) end @@ -1942,250 +1942,250 @@ end function ctld.spawnCrateAtPoint(_side, _weight, _point,_hdg) - local _crateType = ctld.crateLookupTable[tostring(_weight)] + local _crateType = ctld.crateLookupTable[tostring(_weight)] - if _crateType == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find crate with weight %1", _weight), 10) - return - end + if _crateType == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find crate with weight %1", _weight), 10) + return + end - local _country - if _side == "red" then - _side = 1 - _country = 0 - else - _side = 2 - _country = 2 - end + local _country + if _side == "red" then + _side = 1 + _country = 0 + else + _side = 2 + _country = 2 + end - local _unitId = ctld.getNextUnitId() + local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", _crateType.desc, _unitId) + local _name = string.format("%s #%i", _crateType.desc, _unitId) - ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side, _hdg) + ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side, _hdg) end -- *************************************************************** --- Repack vehicules crates functions +-- Repack vehicules crates functions -- *************************************************************** ctld.repackRequestsStack = {} -- table to store the repack request function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) - if _radius == nil then - _radius = ctld.maximumDistanceRepackableUnitsSearch - end - - local unit = ctld.getTransportUnit(_PlayerTransportUnitName) - if unit == nil then - return - end - - local unitsNamesList = ctld.getNearbyUnits(unit:getPoint(), _radius, unit:getCoalition()) - local repackableUnits = {} - for i=1, #unitsNamesList do - local unitObject = Unit.getByName(unitsNamesList[i]) - local repackableUnit = ctld.isRepackableUnit(unitsNamesList[i]) - if repackableUnit then - repackableUnit["repackableUnitGroupID"] = unitObject:getGroup():getID() - table.insert(repackableUnits, mist.utils.deepCopy(repackableUnit)) + if _radius == nil then + _radius = ctld.maximumDistanceRepackableUnitsSearch end - end - return repackableUnits + + local unit = ctld.getTransportUnit(_PlayerTransportUnitName) + if unit == nil then + return + end + + local unitsNamesList = ctld.getNearbyUnits(unit:getPoint(), _radius, unit:getCoalition()) + local repackableUnits = {} + for i=1, #unitsNamesList do + local unitObject = Unit.getByName(unitsNamesList[i]) + local repackableUnit = ctld.isRepackableUnit(unitsNamesList[i]) + if repackableUnit then + repackableUnit["repackableUnitGroupID"] = unitObject:getGroup():getID() + table.insert(repackableUnits, mist.utils.deepCopy(repackableUnit)) + end + end + return repackableUnits end -- *************************************************************** function ctld.getNearbyUnits(_point, _radius, _coalition) - if _coalition == nil then - _coalition = 4 -- all coalitions - end - local _units = {} - local _unitList = mist.DBs.unitsByName - for k, _unit in pairs(mist.DBs.unitsByName) do - local u = Unit.getByName(k) - if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then - --local _dist = ctld.getDistance(u:getPoint(), _point) - local _dist = mist.utils.get2DDist(u:getPoint(), _point) - if _dist <= _radius then - table.insert(_units, k) -- insert nearby unitName - end + if _coalition == nil then + _coalition = 4 -- all coalitions end - end - return _units + local _units = {} + local _unitList = mist.DBs.unitsByName + for k, _unit in pairs(mist.DBs.unitsByName) do + local u = Unit.getByName(k) + if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then + --local _dist = ctld.getDistance(u:getPoint(), _point) + local _dist = mist.utils.get2DDist(u:getPoint(), _point) + if _dist <= _radius then + table.insert(_units, k) -- insert nearby unitName + end + end + end + return _units end -- *************************************************************** function ctld.isRepackableUnit(_unitName) - local unitObject = Unit.getByName(_unitName) - local unitType = unitObject:getTypeName() - for k,v in pairs(ctld.spawnableCrates) do - for i=1, #ctld.spawnableCrates[k] do - if _unitName then - if ctld.spawnableCrates[k][i].unit == unitType then - local repackableUnit = mist.utils.deepCopy(ctld.spawnableCrates[k][i]) - repackableUnit["repackableUnitName"] = _unitName - return repackableUnit + local unitObject = Unit.getByName(_unitName) + local unitType = unitObject:getTypeName() + for k,v in pairs(ctld.spawnableCrates) do + for i=1, #ctld.spawnableCrates[k] do + if _unitName then + if ctld.spawnableCrates[k][i].unit == unitType then + local repackableUnit = mist.utils.deepCopy(ctld.spawnableCrates[k][i]) + repackableUnit["repackableUnitName"] = _unitName + return repackableUnit + end + end end - end end - end - return nil + return nil end -- *************************************************************** function ctld.getCrateDesc(_crateWeight) - for k,v in pairs(ctld.spawnableCrates) do - for i=1, #ctld.spawnableCrates[k] do - if _crateWeight then - if ctld.spawnableCrates[k][i].weight == _crateWeight then - return ctld.spawnableCrates[k][i] + for k,v in pairs(ctld.spawnableCrates) do + for i=1, #ctld.spawnableCrates[k] do + if _crateWeight then + if ctld.spawnableCrates[k][i].weight == _crateWeight then + return ctld.spawnableCrates[k][i] + end + end end - end end - end - return nil + return nil end -- *************************************************************** -function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request - ctld.repackRequestsStack[#ctld.repackRequestsStack+1] = _params - ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) +function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request + ctld.repackRequestsStack[#ctld.repackRequestsStack+1] = _params + ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) end -- *************************************************************** -function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request - trigger.action.outText("Repacking vehicle...", 10) - if t == nil then - t = timer.getTime() - end - ctld.logTrace("FG_ ctld.repackVehicle = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) - for ii, v in ipairs(ctld.repackRequestsStack) do - local repackableUnitName = v[1].repackableUnitName - local crateWeight = v[1].weight - local repackableUnit = Unit.getByName(repackableUnitName) - - local playerUnitName = v[2] - local PlayerTransportUnit = Unit.getByName(playerUnitName) - local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) - local spawnRefPoint = PlayerTransportUnit:getPoint() - local refCountry = PlayerTransportUnit:getCountry() - - if repackableUnit then - if repackableUnit:isExist() then - --ici calculer le heading des spwans à effectuer - for i=1, v[1].cratesRequired do - local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} - -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) - local _unitId = ctld.getNextUnitId() - local _name = string.format("%s_%i", v[1].desc, _unitId) - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) - end +function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request + trigger.action.outText("Repacking vehicle...", 10) + if t == nil then + t = timer.getTime() + end + ctld.logTrace("FG_ ctld.repackVehicle = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) + for ii, v in ipairs(ctld.repackRequestsStack) do + local repackableUnitName = v[1].repackableUnitName + local crateWeight = v[1].weight + local repackableUnit = Unit.getByName(repackableUnitName) - if ctld.isUnitInALogisticZone(repackableUnitName) == nil then - ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle + local playerUnitName = v[2] + local PlayerTransportUnit = Unit.getByName(playerUnitName) + local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) + local spawnRefPoint = PlayerTransportUnit:getPoint() + local refCountry = PlayerTransportUnit:getCountry() + + if repackableUnit then + if repackableUnit:isExist() then + --ici calculer le heading des spwans à effectuer + for i=1, v[1].cratesRequired do + local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} + -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s_%i", v[1].desc, _unitId) + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) + end + + if ctld.isUnitInALogisticZone(repackableUnitName) == nil then + ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle + end + repackableUnit:destroy() -- destroy repacked unit + end + ctld.repackRequestsStack[ii] = nil end - repackableUnit:destroy() -- destroy repacked unit - end - ctld.repackRequestsStack[ii] = nil - end - ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units + ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units end - if ctld.enableRepackingVehicles == true then - return t + 3 -- reschedule the function in 3 seconds - else - return nil --stop scheduling - end + if ctld.enableRepackingVehicles == true then + return t + 3 -- reschedule the function in 3 seconds + else + return nil --stop scheduling + end end --[[ *************************************************************** function ctld.repackVehicle_old(_params) - local repackableUnit = _params[1] - local PlayerTransportUnitName = _params[2] - local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) - ctld.logTrace(" ctld.repackVehicle.repackableUnit = %s", ctld.p(mist.utils.tableShow(repackableUnit))) - local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) - local spawnRefPoint = PlayerTransportUnit:getPoint() - local refCountry = PlayerTransportUnit:getCountry() - if repackableUnit then - --ici calculer le heading des spwan à effectuer - for i=1, repackableUnit.cratesRequired do - --local _point = mist.utils.makeVec3GL(spawnRefPoint.x+(i*5), spawnRefPoint.z, spawnRefPoint.y) - local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} - -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) - local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", repackableUnit.desc, _unitId) - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) + local repackableUnit = _params[1] + local PlayerTransportUnitName = _params[2] + local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) + ctld.logTrace(" ctld.repackVehicle.repackableUnit = %s", ctld.p(mist.utils.tableShow(repackableUnit))) + local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) + local spawnRefPoint = PlayerTransportUnit:getPoint() + local refCountry = PlayerTransportUnit:getCountry() + if repackableUnit then + --ici calculer le heading des spwan à effectuer + for i=1, repackableUnit.cratesRequired do + --local _point = mist.utils.makeVec3GL(spawnRefPoint.x+(i*5), spawnRefPoint.z, spawnRefPoint.y) + local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} + -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s #%i", repackableUnit.desc, _unitId) + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) + end + + ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle + repackableUnit.vehicleId:destroy() -- destroy repacked unit + return end - - ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle - repackableUnit.vehicleId:destroy() -- destroy repacked unit - return - end end ]]-- -- *************************************************************** function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit to be able to repack the vehicle - local dynamicLogisticUnitName = "%dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) - ctld.logisticUnits[#ctld.logisticUnits+1] = dynamicLogisticUnitName - local LogUnit = { - ["category"] = "Fortifications", - ["shape_name"] = "H-Windsock_RW", - ["type"] = "Windsock", - ["y"] = _point.z, - ["x"] = _point.x, - ["name"] = dynamicLogisticUnitName, - ["canCargo"] = false, - ["heading"] = 0, - } - LogUnit["country"] = _country - mist.dynAddStatic(LogUnit) - return StaticObject.getByName(LogUnit["name"]) + local dynamicLogisticUnitName = "%dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) + ctld.logisticUnits[#ctld.logisticUnits+1] = dynamicLogisticUnitName + local LogUnit = { + ["category"] = "Fortifications", + ["shape_name"] = "H-Windsock_RW", + ["type"] = "Windsock", + ["y"] = _point.z, + ["x"] = _point.x, + ["name"] = dynamicLogisticUnitName, + ["canCargo"] = false, + ["heading"] = 0, + } + LogUnit["country"] = _country + mist.dynAddStatic(LogUnit) + return StaticObject.getByName(LogUnit["name"]) end -- *************************************************************** -function ctld.updateDynamicLogisticUnitsZones() -- remove Dynamic Logistic Units if no statics units (crates) are in the zone - local _units = {} - for i, logUnit in ipairs(ctld.logisticUnits) do - if string.sub(logUnit, 1, 17) == "%dynLogisticName_" then -- check if the unit is a dynamic logistic unit - local unitsInLogisticUnitZone = ctld.getUnitsInLogisticZone(logUnit) - if #unitsInLogisticUnitZone == 0 then - local _logUnit = StaticObject.getByName(logUnit) - if _logUnit then - _logUnit:destroy() -- destroy the dynamic Logistic unit object from map - ctld.logisticUnits[i] = nil -- remove the dynamic Logistic unit from the list +function ctld.updateDynamicLogisticUnitsZones() -- remove Dynamic Logistic Units if no statics units (crates) are in the zone + local _units = {} + for i, logUnit in ipairs(ctld.logisticUnits) do + if string.sub(logUnit, 1, 17) == "%dynLogisticName_" then -- check if the unit is a dynamic logistic unit + local unitsInLogisticUnitZone = ctld.getUnitsInLogisticZone(logUnit) + if #unitsInLogisticUnitZone == 0 then + local _logUnit = StaticObject.getByName(logUnit) + if _logUnit then + _logUnit:destroy() -- destroy the dynamic Logistic unit object from map + ctld.logisticUnits[i] = nil -- remove the dynamic Logistic unit from the list + end + end end - end end - end - return 5 -- reschedule the function in 5 second + return 5 -- reschedule the function in 5 second end -- *************************************************************** function ctld.getUnitsInLogisticZone(_logisticUnitName, _coalition) - local _unit = StaticObject.getByName(_logisticUnitName) - if _unit then - local _point = _unit:getPoint() - local _unitList = ctld.getNearbyUnits(_point, ctld.maximumDistanceLogistic, _coalition) - return _unitList - end - return {} + local _unit = StaticObject.getByName(_logisticUnitName) + if _unit then + local _point = _unit:getPoint() + local _unitList = ctld.getNearbyUnits(_point, ctld.maximumDistanceLogistic, _coalition) + return _unitList + end + return {} end -- *************************************************************** function ctld.isUnitInNamedLogisticZone(_unitName, _logisticUnitName) -- check if a unit is in the named logistic zone - trigger.action.outText('ctld.isUnitInNamedLogisticZone._logisticUnitName = '..tostring(_logisticUnitName), 10) - local _unit = Unit.getByName(_unitName) - if _unit == nil then - return false - end - local unitPoint = _unit:getPoint() - if StaticObject.getByName(_logisticUnitName) then - local logisticUnitPoint = StaticObject.getByName(_logisticUnitName):getPoint() - local _dist = ctld.getDistance(unitPoint, logisticUnitPoint) - if _dist <= ctld.maximumDistanceLogistic then - return true + trigger.action.outText('ctld.isUnitInNamedLogisticZone._logisticUnitName = '..tostring(_logisticUnitName), 10) + local _unit = Unit.getByName(_unitName) + if _unit == nil then + return false end - end - return false + local unitPoint = _unit:getPoint() + if StaticObject.getByName(_logisticUnitName) then + local logisticUnitPoint = StaticObject.getByName(_logisticUnitName):getPoint() + local _dist = ctld.getDistance(unitPoint, logisticUnitPoint) + if _dist <= ctld.maximumDistanceLogistic then + return true + end + end + return false end -- *************************************************************** function ctld.isUnitInALogisticZone(_unitName) -- check if a unit is in a logistic zone if true then return the logisticUnitName of the zone - for i, logUnit in ipairs(ctld.logisticUnits) do - if ctld.isUnitInNamedLogisticZone(_unitName, logUnit) then - return logUnit + for i, logUnit in ipairs(ctld.logisticUnits) do + if ctld.isUnitInNamedLogisticZone(_unitName, logUnit) then + return logUnit + end end - end - return nil + return nil end -- *************************************************************** @@ -2198,73 +2198,73 @@ end -- If a component does not require a crate, it can be specified via the entry "NoCrate" set to true ctld.AASystemTemplate = { - { - name = "HAWK AA System", - count = 5, - parts = { - {name = "Hawk ln", desc = "HAWK Launcher", launcher = true}, - {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}, + { + name = "HAWK AA System", + count = 5, + parts = { + {name = "Hawk ln", desc = "HAWK Launcher", launcher = true}, + {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", }, - repair = "HAWK Repair", - }, - { - name = "Patriot AA System", - count = 4, - parts = { - {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", amount = 2}, - --{name = "Patriot cp", desc = "Patriot ICC", NoCrate = true}, - --{name = "Patriot EPP", desc = "Patriot EPP", NoCrate = true}, - {name = "Patriot AMG", desc = "Patriot AMG DL relay", NoCrate = true}, + { + name = "Patriot AA System", + count = 4, + parts = { + {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", amount = 2}, + --{name = "Patriot cp", desc = "Patriot ICC", NoCrate = true}, + --{name = "Patriot EPP", desc = "Patriot EPP", NoCrate = true}, + {name = "Patriot AMG", desc = "Patriot AMG DL relay", NoCrate = true}, + }, + repair = "Patriot Repair", }, - repair = "Patriot Repair", - }, - { - name = "NASAMS AA System", - count = 3, - parts = { - {name = "NASAMS_LN_C", desc = "NASAMS Launcher 120C", launcher = true}, - {name = "NASAMS_Radar_MPQ64F1", desc = "NASAMS Search/Track Radar"}, - {name = "NASAMS_Command_Post", desc = "NASAMS Command Post"}, + { + name = "NASAMS AA System", + count = 3, + parts = { + {name = "NASAMS_LN_C", desc = "NASAMS Launcher 120C", launcher = true}, + {name = "NASAMS_Radar_MPQ64F1", desc = "NASAMS Search/Track Radar"}, + {name = "NASAMS_Command_Post", desc = "NASAMS Command Post"}, + }, + repair = "NASAMS Repair", }, - repair = "NASAMS Repair", - }, { - name = "BUK AA System", - count = 3, - parts = { - {name = "SA-11 Buk LN 9A310M1", desc = "BUK Launcher" , launcher = true}, - {name = "SA-11 Buk CC 9S470M1", desc = "BUK CC Radar"}, - {name = "SA-11 Buk SR 9S18M1", desc = "BUK Search Radar"}, + name = "BUK AA System", + count = 3, + parts = { + {name = "SA-11 Buk LN 9A310M1", desc = "BUK Launcher" , launcher = true}, + {name = "SA-11 Buk CC 9S470M1", desc = "BUK CC Radar"}, + {name = "SA-11 Buk SR 9S18M1", desc = "BUK Search Radar"}, + }, + repair = "BUK Repair", }, - repair = "BUK Repair", - }, - { - name = "KUB AA System", - count = 2, - parts = { - {name = "Kub 2P25 ln", desc = "KUB Launcher", launcher = true}, - {name = "Kub 1S91 str", desc = "KUB Radar"}, + { + name = "KUB AA System", + count = 2, + parts = { + {name = "Kub 2P25 ln", desc = "KUB Launcher", launcher = true}, + {name = "Kub 1S91 str", desc = "KUB Radar"}, + }, + repair = "KUB Repair", }, - repair = "KUB Repair", - }, - { - name = "S-300 AA System", - count = 6, - parts = { - { desc = "S-300 Grumble TEL C", name = "S-300PS 5P85C ln", launcher = true, amount = 1 }, - { desc = "S-300 Grumble TEL D", name = "S-300PS 5P85D ln", NoCrate = true, amount = 2 }, - { desc = "S-300 Grumble Flap Lid-A TR", name = "S-300PS 40B6M tr"}, - { desc = "S-300 Grumble Clam Shell SR", name = "S-300PS 40B6MD sr"}, - { desc = "S-300 Grumble Big Bird SR", name = "S-300PS 64H6E sr"}, - { desc = "S-300 Grumble C2", name = "S-300PS 54K6 cp"}, + { + name = "S-300 AA System", + count = 6, + parts = { + { desc = "S-300 Grumble TEL C", name = "S-300PS 5P85C ln", launcher = true, amount = 1 }, + { desc = "S-300 Grumble TEL D", name = "S-300PS 5P85D ln", NoCrate = true, amount = 2 }, + { desc = "S-300 Grumble Flap Lid-A TR", name = "S-300PS 40B6M tr"}, + { desc = "S-300 Grumble Clam Shell SR", name = "S-300PS 40B6MD sr"}, + { desc = "S-300 Grumble Big Bird SR", name = "S-300PS 64H6E sr"}, + { desc = "S-300 Grumble C2", name = "S-300PS 54K6 cp"}, + }, + repair = "S-300 Repair", }, - repair = "S-300 Repair", - }, } @@ -2280,718 +2280,718 @@ ctld.crateMove = {} --- print an object for a debugging log function ctld.p(o, level) - local MAX_LEVEL = 20 - if level == nil then level = 0 end - if level > MAX_LEVEL then - ctld.logError("max depth reached in ctld.p : "..tostring(MAX_LEVEL)) - return "" - end - local text = "" - if (type(o) == "table") then - text = "\n" - for key,value in pairs(o) do - for i=0, level do - text = text .. " " - end - text = text .. ".".. key.."="..ctld.p(value, level+1) .. "\n" + local MAX_LEVEL = 20 + if level == nil then level = 0 end + if level > MAX_LEVEL then + ctld.logError("max depth reached in ctld.p : "..tostring(MAX_LEVEL)) + return "" end - elseif (type(o) == "function") then - text = "[function]" - elseif (type(o) == "boolean") then - if o == true then - text = "[true]" + local text = "" + if (type(o) == "table") then + text = "\n" + for key,value in pairs(o) do + for i=0, level do + text = text .. " " + end + text = text .. ".".. key.."="..ctld.p(value, level+1) .. "\n" + end + elseif (type(o) == "function") then + text = "[function]" + elseif (type(o) == "boolean") then + if o == true then + text = "[true]" + else + text = "[false]" + end else - text = "[false]" + if o == nil then + text = "[nil]" + else + text = tostring(o) + end end - else - if o == nil then - text = "[nil]" - else - text = tostring(o) - end - end - return text + return text end function ctld.formatText(text, ...) - if not text then - return "" - end - if type(text) ~= 'string' then - text = ctld.p(text) - else - local args = ... - if args and args.n and args.n > 0 then - local pArgs = {} - for i=1,args.n do - pArgs[i] = ctld.p(args[i]) - end - text = text:format(unpack(pArgs)) - end + if not text then + return "" + end + if type(text) ~= 'string' then + text = ctld.p(text) + else + local args = ... + if args and args.n and args.n > 0 then + local pArgs = {} + for i=1,args.n do + pArgs[i] = ctld.p(args[i]) + end + text = text:format(unpack(pArgs)) + end + end + local fName = nil + local cLine = nil + if debug and debug.getinfo then + local dInfo = debug.getinfo(3) + fName = dInfo.name + cLine = dInfo.currentline + end + if fName and cLine then + return fName .. '|' .. cLine .. ': ' .. text + elseif cLine then + return cLine .. ': ' .. text + else + return ' ' .. text end - local fName = nil - local cLine = nil - if debug and debug.getinfo then - local dInfo = debug.getinfo(3) - fName = dInfo.name - cLine = dInfo.currentline - end - if fName and cLine then - return fName .. '|' .. cLine .. ': ' .. text - elseif cLine then - return cLine .. ': ' .. text - else - return ' ' .. text - end end function ctld.logError(message, ...) - message = ctld.formatText(message, arg) - env.info(" E - " .. ctld.Id .. message) + message = ctld.formatText(message, arg) + env.info(" E - " .. ctld.Id .. message) end function ctld.logWarning(message, ...) - message = ctld.formatText(message, arg) - env.info(" W - " .. ctld.Id .. message) + message = ctld.formatText(message, arg) + env.info(" W - " .. ctld.Id .. message) end function ctld.logInfo(message, ...) - message = ctld.formatText(message, arg) - env.info(" I - " .. ctld.Id .. message) + message = ctld.formatText(message, arg) + env.info(" I - " .. ctld.Id .. message) end function ctld.logDebug(message, ...) - if message and ctld.Debug then - message = ctld.formatText(message, arg) - env.info(" D - " .. ctld.Id .. message) - end + if message and ctld.Debug then + message = ctld.formatText(message, arg) + env.info(" D - " .. ctld.Id .. message) + end end function ctld.logTrace(message, ...) - if message and ctld.Trace then - message = ctld.formatText(message, arg) - env.info(" T - " .. ctld.Id .. message) - end + if message and ctld.Trace then + message = ctld.formatText(message, arg) + env.info(" T - " .. ctld.Id .. message) + end end ctld.nextUnitId = 1; ctld.getNextUnitId = function() - ctld.nextUnitId = ctld.nextUnitId + 1 + ctld.nextUnitId = ctld.nextUnitId + 1 - return ctld.nextUnitId + return ctld.nextUnitId end ctld.nextGroupId = 1; ctld.getNextGroupId = function() - ctld.nextGroupId = ctld.nextGroupId + 1 + ctld.nextGroupId = ctld.nextGroupId + 1 - return ctld.nextGroupId + return ctld.nextGroupId end function ctld.getTransportUnit(_unitName) - if _unitName == nil then + if _unitName == nil then + return nil + end + + local transportUnitObject = Unit.getByName(_unitName) + + if transportUnitObject ~= nil and transportUnitObject:isActive() and transportUnitObject:getLife() > 0 then + return transportUnitObject + end + return nil - end - - local transportUnitObject = Unit.getByName(_unitName) - - if transportUnitObject ~= nil and transportUnitObject:isActive() and transportUnitObject:getLife() > 0 then - return transportUnitObject - end - - return nil end function ctld.spawnCrateStatic(_country, _unitId, _point, _name, _weight, _side, _hdg, _model_type) - local _crate - local _spawnedCrate + local _crate + local _spawnedCrate - local hdg = _hdg or 0 + local hdg = _hdg or 0 - if ctld.staticBugWorkaround and ctld.slingLoad == false then - local _groupId = ctld.getNextGroupId() - local _groupName = "Crate Group #".._groupId + if ctld.staticBugWorkaround and ctld.slingLoad == false then + local _groupId = ctld.getNextGroupId() + local _groupName = "Crate Group #".._groupId - local _group = { - ["visible"] = false, - -- ["groupId"] = _groupId, - ["hidden"] = false, - ["units"] = {}, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = _groupName, - ["task"] = {}, - } + local _group = { + ["visible"] = false, + -- ["groupId"] = _groupId, + ["hidden"] = false, + ["units"] = {}, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = _groupName, + ["task"] = {}, + } - _group.units[1] = ctld.createUnit(_point.x , _point.z , hdg, {type="UAZ-469",name=_name,unitId=_unitId}) + _group.units[1] = ctld.createUnit(_point.x , _point.z , hdg, {type="UAZ-469",name=_name,unitId=_unitId}) - --switch to MIST - _group.category = Group.Category.GROUND; - _group.country = _country; + --switch to MIST + _group.category = Group.Category.GROUND; + _group.country = _country; - local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) + local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) - -- Turn off AI - trigger.action.setGroupAIOff(_spawnedGroup) + -- Turn off AI + trigger.action.setGroupAIOff(_spawnedGroup) - _spawnedCrate = Unit.getByName(_name) - else - if _model_type ~= nil then - _crate = mist.utils.deepCopy(ctld.spawnableCratesModels[_model_type]) - elseif ctld.slingLoad then - _crate = mist.utils.deepCopy(ctld.spawnableCratesModels["sling"]) + _spawnedCrate = Unit.getByName(_name) else - _crate = mist.utils.deepCopy(ctld.spawnableCratesModels["load"]) + if _model_type ~= nil then + _crate = mist.utils.deepCopy(ctld.spawnableCratesModels[_model_type]) + elseif ctld.slingLoad then + _crate = mist.utils.deepCopy(ctld.spawnableCratesModels["sling"]) + else + _crate = mist.utils.deepCopy(ctld.spawnableCratesModels["load"]) + end + + _crate["y"] = _point.z + _crate["x"] = _point.x + _crate["mass"] = _weight + _crate["name"] = _name + _crate["heading"] = hdg + _crate["country"] = _country + + mist.dynAddStatic(_crate) + + _spawnedCrate = StaticObject.getByName(_crate["name"]) end - _crate["y"] = _point.z - _crate["x"] = _point.x - _crate["mass"] = _weight - _crate["name"] = _name - _crate["heading"] = hdg - _crate["country"] = _country - mist.dynAddStatic(_crate) + local _crateType = ctld.crateLookupTable[tostring(_weight)] - _spawnedCrate = StaticObject.getByName(_crate["name"]) - end + if _side == 1 then + ctld.spawnedCratesRED[_name] =_crateType + else + ctld.spawnedCratesBLUE[_name] = _crateType + end - - local _crateType = ctld.crateLookupTable[tostring(_weight)] - - if _side == 1 then - ctld.spawnedCratesRED[_name] =_crateType - else - ctld.spawnedCratesBLUE[_name] = _crateType - end - - return _spawnedCrate + return _spawnedCrate end function ctld.spawnFOBCrateStatic(_country, _unitId, _point, _name) - local _crate = { - ["category"] = "Fortifications", - ["shape_name"] = "konteiner_red1", - ["type"] = "Container red 1", - -- ["unitId"] = _unitId, - ["y"] = _point.z, - ["x"] = _point.x, - ["name"] = _name, - ["canCargo"] = false, - ["heading"] = 0, - } + local _crate = { + ["category"] = "Fortifications", + ["shape_name"] = "konteiner_red1", + ["type"] = "Container red 1", + -- ["unitId"] = _unitId, + ["y"] = _point.z, + ["x"] = _point.x, + ["name"] = _name, + ["canCargo"] = false, + ["heading"] = 0, + } - _crate["country"] = _country + _crate["country"] = _country - mist.dynAddStatic(_crate) + mist.dynAddStatic(_crate) - local _spawnedCrate = StaticObject.getByName(_crate["name"]) - --local _spawnedCrate = coalition.addStaticObject(_country, _crate) + local _spawnedCrate = StaticObject.getByName(_crate["name"]) + --local _spawnedCrate = coalition.addStaticObject(_country, _crate) - return _spawnedCrate + return _spawnedCrate end function ctld.spawnFOB(_country, _unitId, _point, _name) - local _crate = { - ["category"] = "Fortifications", - ["type"] = "outpost", - -- ["unitId"] = _unitId, - ["y"] = _point.z, - ["x"] = _point.x, - ["name"] = _name, - ["canCargo"] = false, - ["heading"] = 0, - } + local _crate = { + ["category"] = "Fortifications", + ["type"] = "outpost", + -- ["unitId"] = _unitId, + ["y"] = _point.z, + ["x"] = _point.x, + ["name"] = _name, + ["canCargo"] = false, + ["heading"] = 0, + } - _crate["country"] = _country - mist.dynAddStatic(_crate) - local _spawnedCrate = StaticObject.getByName(_crate["name"]) - --local _spawnedCrate = coalition.addStaticObject(_country, _crate) + _crate["country"] = _country + mist.dynAddStatic(_crate) + local _spawnedCrate = StaticObject.getByName(_crate["name"]) + --local _spawnedCrate = coalition.addStaticObject(_country, _crate) - local _id = ctld.getNextUnitId() - local _tower = { - ["type"] = "house2arm", - -- ["unitId"] = _id, - ["rate"] = 100, - ["y"] = _point.z + -36.57142857, - ["x"] = _point.x + 14.85714286, - ["name"] = "FOB Watchtower #" .. _id, - ["category"] = "Fortifications", - ["canCargo"] = false, - ["heading"] = 0, - } - --coalition.addStaticObject(_country, _tower) - _tower["country"] = _country + local _id = ctld.getNextUnitId() + local _tower = { + ["type"] = "house2arm", + -- ["unitId"] = _id, + ["rate"] = 100, + ["y"] = _point.z + -36.57142857, + ["x"] = _point.x + 14.85714286, + ["name"] = "FOB Watchtower #" .. _id, + ["category"] = "Fortifications", + ["canCargo"] = false, + ["heading"] = 0, + } + --coalition.addStaticObject(_country, _tower) + _tower["country"] = _country - mist.dynAddStatic(_tower) + mist.dynAddStatic(_tower) - return _spawnedCrate + return _spawnedCrate end function ctld.spawnCrate(_arguments, bypassCrateWaitTime) - local _status, _err = pcall(function(_args) + local _status, _err = pcall(function(_args) - -- use the cargo weight to guess the type of unit as no way to add description :( - local _crateType = ctld.crateLookupTable[tostring(_args[2])] + -- use the cargo weight to guess the type of unit as no way to add description :( + local _crateType = ctld.crateLookupTable[tostring(_args[2])] - local _heli = ctld.getTransportUnit(_args[1]) - if not _heli then - return - end - - -- check crate spam - if not(bypassCrateWaitTime) and _heli:getPlayerName() ~= nil and ctld.crateWait[_heli:getPlayerName()] and ctld.crateWait[_heli:getPlayerName()] > timer.getTime() then - ctld.displayMessageToGroup(_heli,ctld.i18n_translate("Sorry you must wait %1 seconds before you can get another crate", (ctld.crateWait[_heli:getPlayerName()] - timer.getTime())), 20) - return - end - - if _crateType and _crateType.multiple then - for _, weight in pairs(_crateType.multiple) do - local _aCrateType = ctld.crateLookupTable[tostring(weight)] - if _aCrateType then - ctld.spawnCrate({_args[1], _aCrateType.weight}, true) + local _heli = ctld.getTransportUnit(_args[1]) + if not _heli then + return end - end - return - end - if _crateType ~= nil and _heli ~= nil and ctld.inAir(_heli) == false then + -- check crate spam + if not(bypassCrateWaitTime) and _heli:getPlayerName() ~= nil and ctld.crateWait[_heli:getPlayerName()] and ctld.crateWait[_heli:getPlayerName()] > timer.getTime() then + ctld.displayMessageToGroup(_heli,ctld.i18n_translate("Sorry you must wait %1 seconds before you can get another crate", (ctld.crateWait[_heli:getPlayerName()] - timer.getTime())), 20) + return + end - if ctld.inLogisticsZone(_heli) == false then + if _crateType and _crateType.multiple then + for _, weight in pairs(_crateType.multiple) do + local _aCrateType = ctld.crateLookupTable[tostring(weight)] + if _aCrateType then + ctld.spawnCrate({_args[1], _aCrateType.weight}, true) + end + end + return + end - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not close enough to friendly logistics to get a crate!"), 10) + if _crateType ~= nil and _heli ~= nil and ctld.inAir(_heli) == false then - return - end + if ctld.inLogisticsZone(_heli) == false then - if ctld.isJTACUnitType(_crateType.unit) then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not close enough to friendly logistics to get a crate!"), 10) - local _limitHit = false + return + end - if _heli:getCoalition() == 1 then + if ctld.isJTACUnitType(_crateType.unit) then + + local _limitHit = false + + if _heli:getCoalition() == 1 then + + if ctld.JTAC_LIMIT_RED == 0 then + _limitHit = true + else + ctld.JTAC_LIMIT_RED = ctld.JTAC_LIMIT_RED - 1 + end + else + if ctld.JTAC_LIMIT_BLUE == 0 then + _limitHit = true + else + ctld.JTAC_LIMIT_BLUE = ctld.JTAC_LIMIT_BLUE - 1 + end + end + + if _limitHit then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No more JTAC Crates Left!"), 10) + return + end + end + + if _heli:getPlayerName() ~= nil then + ctld.crateWait[_heli:getPlayerName()] = timer.getTime() + ctld.crateWaitTime + end + + local _heli = ctld.getTransportUnit(_args[1]) + + local _model_type = nil + + local _point = ctld.getPointAt12Oclock(_heli, 30) + local _position = "12" + + if ctld.unitDynamicCargoCapable(_heli) then + _model_type = "dynamic" + _point = ctld.getPointAt6Oclock(_heli, 15) + _position = "6" + end + + local _unitId = ctld.getNextUnitId() + + local _side = _heli:getCoalition() + + local _name = string.format("%s #%i", _crateType.desc, _unitId) + + ctld.spawnCrateStatic(_heli:getCountry(), _unitId, _point, _name, _crateType.weight, _side, 0, _model_type) + + -- add to move table + ctld.crateMove[_name] = _name + + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock ", _crateType.desc, _crateType.weight, _position), 20) - if ctld.JTAC_LIMIT_RED == 0 then - _limitHit = true - else - ctld.JTAC_LIMIT_RED = ctld.JTAC_LIMIT_RED - 1 - end else - if ctld.JTAC_LIMIT_BLUE == 0 then - _limitHit = true - else - ctld.JTAC_LIMIT_BLUE = ctld.JTAC_LIMIT_BLUE - 1 - end + env.info("Couldn't find crate item to spawn") end + end, _arguments) - if _limitHit then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No more JTAC Crates Left!"), 10) - return - end - end - - if _heli:getPlayerName() ~= nil then - ctld.crateWait[_heli:getPlayerName()] = timer.getTime() + ctld.crateWaitTime - end - - local _heli = ctld.getTransportUnit(_args[1]) - - local _model_type = nil - - local _point = ctld.getPointAt12Oclock(_heli, 30) - local _position = "12" - - if ctld.unitDynamicCargoCapable(_heli) then - _model_type = "dynamic" - _point = ctld.getPointAt6Oclock(_heli, 15) - _position = "6" - end - - local _unitId = ctld.getNextUnitId() - - local _side = _heli:getCoalition() - - local _name = string.format("%s #%i", _crateType.desc, _unitId) - - ctld.spawnCrateStatic(_heli:getCountry(), _unitId, _point, _name, _crateType.weight, _side, 0, _model_type) - - -- add to move table - ctld.crateMove[_name] = _name - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock ", _crateType.desc, _crateType.weight, _position), 20) - - else - env.info("Couldn't find crate item to spawn") + if (not _status) then + env.error(string.format("CTLD ERROR: %s", _err)) end - end, _arguments) - - if (not _status) then - env.error(string.format("CTLD ERROR: %s", _err)) - end end --*************************************************************** ctld.randomCrateSpacing = 12 -- meters function ctld.getPointAt12Oclock(_unit, _offset) - return ctld.getPointAtDirection(_unit, _offset, 0) + return ctld.getPointAtDirection(_unit, _offset, 0) end function ctld.getPointAt6Oclock(_unit, _offset) - return ctld.getPointAtDirection(_unit, _offset, math.pi) + return ctld.getPointAtDirection(_unit, _offset, math.pi) end function ctld.getPointAtDirection(_unit, _offset, _directionInRadian) - ctld.logTrace("_offset = %s", ctld.p(_offset)) - local _randomOffsetX = math.random(ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing - local _randomOffsetZ = math.random(0, ctld.randomCrateSpacing) - ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) - ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) - local _position = _unit:getPosition() - --local _angle = math.atan2(_position.x.z, _position.x.x) + _directionInRadian - local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian - local _xOffset = math.cos(_angle) * _offset + _randomOffsetX - local _zOffset = math.sin(_angle) * _offset + _randomOffsetZ + ctld.logTrace("_offset = %s", ctld.p(_offset)) + local _randomOffsetX = math.random(ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing + local _randomOffsetZ = math.random(0, ctld.randomCrateSpacing) + ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) + ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) + local _position = _unit:getPosition() + --local _angle = math.atan2(_position.x.z, _position.x.x) + _directionInRadian + local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian + local _xOffset = math.cos(_angle) * _offset + _randomOffsetX + local _zOffset = math.sin(_angle) * _offset + _randomOffsetZ - local _point = _unit:getPoint() - return { x = _point.x + _xOffset, z = _point.z + _zOffset, y = _point.y } + local _point = _unit:getPoint() + return { x = _point.x + _xOffset, z = _point.z + _zOffset, y = _point.y } end function ctld.troopsOnboard(_heli, _troops) - if ctld.inTransitTroops[_heli:getName()] ~= nil then + if ctld.inTransitTroops[_heli:getName()] ~= nil then - local _onboard = ctld.inTransitTroops[_heli:getName()] + local _onboard = ctld.inTransitTroops[_heli:getName()] - if _troops then + if _troops then + + if _onboard.troops ~= nil and _onboard.troops.units ~= nil and #_onboard.troops.units > 0 then + return true + else + return false + end + else + + if _onboard.vehicles ~= nil and _onboard.vehicles.units ~= nil and #_onboard.vehicles.units > 0 then + return true + else + return false + end + end - if _onboard.troops ~= nil and _onboard.troops.units ~= nil and #_onboard.troops.units > 0 then - return true - else - return false - end else - - if _onboard.vehicles ~= nil and _onboard.vehicles.units ~= nil and #_onboard.vehicles.units > 0 then - return true - else return false - end end - - else - return false - end end -- if its dropped by AI then there is no player name so return the type of unit function ctld.getPlayerNameOrType(_heli) - if _heli:getPlayerName() == nil then + if _heli:getPlayerName() == nil then - return _heli:getTypeName() - else - return _heli:getPlayerName() - end + return _heli:getTypeName() + else + return _heli:getPlayerName() + end end function ctld.inExtractZone(_heli) - local _heliPoint = _heli:getPoint() + local _heliPoint = _heli:getPoint() - for _, _zoneDetails in pairs(ctld.extractZones) do + for _, _zoneDetails in pairs(ctld.extractZones) do - --get distance to center - local _dist = ctld.getDistance(_heliPoint, _zoneDetails.point) + --get distance to center + local _dist = ctld.getDistance(_heliPoint, _zoneDetails.point) - if _dist <= _zoneDetails.radius then - return _zoneDetails + if _dist <= _zoneDetails.radius then + return _zoneDetails + end end - end - return false + return false end -- safe to fast rope if speed is less than 0.5 Meters per second function ctld.safeToFastRope(_heli) - if ctld.enableFastRopeInsertion == false then - return false - end + if ctld.enableFastRopeInsertion == false then + return false + end - --landed or speed is less than 8 km/h and height is less than fast rope height - if (ctld.inAir(_heli) == false or (ctld.heightDiff(_heli) <= ctld.fastRopeMaximumHeight + 3.0 and mist.vec.mag(_heli:getVelocity()) < 2.2)) then - return true - end + --landed or speed is less than 8 km/h and height is less than fast rope height + if (ctld.inAir(_heli) == false or (ctld.heightDiff(_heli) <= ctld.fastRopeMaximumHeight + 3.0 and mist.vec.mag(_heli:getVelocity()) < 2.2)) then + return true + end end function ctld.metersToFeet(_meters) - local _feet = _meters * 3.2808399 + local _feet = _meters * 3.2808399 - return mist.utils.round(_feet) + return mist.utils.round(_feet) end function ctld.inAir(_heli) - if _heli:inAir() == false then - return false - end + if _heli:inAir() == false then + return false + end - -- less than 5 cm/s a second so landed - -- BUT AI can hold a perfect hover so ignore AI - if mist.vec.mag(_heli:getVelocity()) < 0.05 and _heli:getPlayerName() ~= nil then - return false - end - return true + -- less than 5 cm/s a second so landed + -- BUT AI can hold a perfect hover so ignore AI + if mist.vec.mag(_heli:getVelocity()) < 0.05 and _heli:getPlayerName() ~= nil then + return false + end + return true end function ctld.deployTroops(_heli, _troops) - local _onboard = ctld.inTransitTroops[_heli:getName()] + local _onboard = ctld.inTransitTroops[_heli:getName()] - -- deloy troops - if _troops then - if _onboard.troops ~= nil and #_onboard.troops.units > 0 then - if ctld.inAir(_heli) == false or ctld.safeToFastRope(_heli) then + -- deloy troops + if _troops then + if _onboard.troops ~= nil and #_onboard.troops.units > 0 then + if ctld.inAir(_heli) == false or ctld.safeToFastRope(_heli) then - -- check we're not in extract zone - local _extractZone = ctld.inExtractZone(_heli) + -- check we're not in extract zone + local _extractZone = ctld.inExtractZone(_heli) - if _extractZone == false then + if _extractZone == false then - local _droppedTroops = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.troops, false) - if _onboard.troops.jtac or _droppedTroops:getName():lower():find("jtac") then - local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) - table.insert(ctld.jtacGeneratedLaserCodes, _code) - ctld.JTACStart(_droppedTroops:getName(), _code) - end + local _droppedTroops = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.troops, false) + if _onboard.troops.jtac or _droppedTroops:getName():lower():find("jtac") then + local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) + table.insert(ctld.jtacGeneratedLaserCodes, _code) + ctld.JTACStart(_droppedTroops:getName(), _code) + end - if _heli:getCoalition() == 1 then + if _heli:getCoalition() == 1 then - table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) - else + table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) + else - table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) - end + table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) + end - ctld.inTransitTroops[_heli:getName()].troops = nil - ctld.adaptWeightToCargo(_heli:getName()) + ctld.inTransitTroops[_heli:getName()].troops = nil + ctld.adaptWeightToCargo(_heli:getName()) - if ctld.inAir(_heli) then - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 fast-ropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - else - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - end + if ctld.inAir(_heli) then + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 fast-ropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + else + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + end - ctld.processCallback({unit = _heli, unloaded = _droppedTroops, action = "dropped_troops"}) + ctld.processCallback({unit = _heli, unloaded = _droppedTroops, action = "dropped_troops"}) - else - --extract zone! - local _droppedCount = trigger.misc.getUserFlag(_extractZone.flag) + else + --extract zone! + local _droppedCount = trigger.misc.getUserFlag(_extractZone.flag) - _droppedCount = (#_onboard.troops.units) + _droppedCount + _droppedCount = (#_onboard.troops.units) + _droppedCount - trigger.action.setUserFlag(_extractZone.flag, _droppedCount) + trigger.action.setUserFlag(_extractZone.flag, _droppedCount) - ctld.inTransitTroops[_heli:getName()].troops = nil - ctld.adaptWeightToCargo(_heli:getName()) + ctld.inTransitTroops[_heli:getName()].troops = nil + ctld.adaptWeightToCargo(_heli:getName()) - if ctld.inAir(_heli) then - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 fast-ropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), _heli:getTypeName(), _extractZone.name), 10) - else - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), _heli:getTypeName(), _extractZone.name), 10) - end - end - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Too high or too fast to drop troops into combat! Hover below %1 feet or land.", ctld.metersToFeet(ctld.fastRopeMaximumHeight)), 10) - end - end - - else - if ctld.inAir(_heli) == false then - if _onboard.vehicles ~= nil and #_onboard.vehicles.units > 0 then - - local _droppedVehicles = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.vehicles, true) - - if _heli:getCoalition() == 1 then - - table.insert(ctld.droppedVehiclesRED, _droppedVehicles:getName()) - else - - table.insert(ctld.droppedVehiclesBLUE, _droppedVehicles:getName()) + if ctld.inAir(_heli) then + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 fast-ropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), _heli:getTypeName(), _extractZone.name), 10) + else + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), _heli:getTypeName(), _extractZone.name), 10) + end + end + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Too high or too fast to drop troops into combat! Hover below %1 feet or land.", ctld.metersToFeet(ctld.fastRopeMaximumHeight)), 10) + end end - ctld.inTransitTroops[_heli:getName()].vehicles = nil - ctld.adaptWeightToCargo(_heli:getName()) + else + if ctld.inAir(_heli) == false then + if _onboard.vehicles ~= nil and #_onboard.vehicles.units > 0 then - ctld.processCallback({unit = _heli, unloaded = _droppedVehicles, action = "dropped_vehicles"}) + local _droppedVehicles = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.vehicles, true) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped vehicles from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - end + if _heli:getCoalition() == 1 then + + table.insert(ctld.droppedVehiclesRED, _droppedVehicles:getName()) + else + + table.insert(ctld.droppedVehiclesBLUE, _droppedVehicles:getName()) + end + + ctld.inTransitTroops[_heli:getName()].vehicles = nil + ctld.adaptWeightToCargo(_heli:getName()) + + ctld.processCallback({unit = _heli, unloaded = _droppedVehicles, action = "dropped_vehicles"}) + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped vehicles from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + end + end end - end end function ctld.insertIntoTroopsArray(_troopType,_count,_troopArray,_troopName) - for _i = 1, _count do - local _unitId = ctld.getNextUnitId() - table.insert(_troopArray, { type = _troopType, unitId = _unitId, name = string.format("Dropped %s #%i", _troopName or _troopType, _unitId) }) - end + for _i = 1, _count do + local _unitId = ctld.getNextUnitId() + table.insert(_troopArray, { type = _troopType, unitId = _unitId, name = string.format("Dropped %s #%i", _troopName or _troopType, _unitId) }) + end - return _troopArray + return _troopArray end function ctld.generateTroopTypes(_side, _countOrTemplate, _country) - local _troops = {} - local _weight = 0 - local _hasJTAC = false - - local function getSoldiersWeight(count, additionalWeight) + local _troops = {} local _weight = 0 - for i = 1, count do - local _soldierWeight = math.random(90, 120) * ctld.SOLDIER_WEIGHT / 100 - _weight = _weight + _soldierWeight + ctld.KIT_WEIGHT + additionalWeight - end - return _weight - end + local _hasJTAC = false - if type(_countOrTemplate) == "table" then - - if _countOrTemplate.aa then - if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier stinger",_countOrTemplate.aa,_troops) - else - _troops = ctld.insertIntoTroopsArray("SA-18 Igla manpad",_countOrTemplate.aa,_troops) - end - _weight = _weight + getSoldiersWeight(_countOrTemplate.aa, ctld.MANPAD_WEIGHT) - end - - if _countOrTemplate.inf then - if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.inf,_troops) - else - _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.inf,_troops) - end - _weight = _weight + getSoldiersWeight(_countOrTemplate.inf, ctld.RIFLE_WEIGHT) - end - - if _countOrTemplate.mg then - if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M249",_countOrTemplate.mg,_troops) - else - _troops = ctld.insertIntoTroopsArray("Paratrooper AKS-74",_countOrTemplate.mg,_troops) - end - _weight = _weight + getSoldiersWeight(_countOrTemplate.mg, ctld.MG_WEIGHT) - end - - if _countOrTemplate.at then - _troops = ctld.insertIntoTroopsArray("Paratrooper RPG-16",_countOrTemplate.at,_troops) - _weight = _weight + getSoldiersWeight(_countOrTemplate.at, ctld.RPG_WEIGHT) - end - - if _countOrTemplate.mortar then - _troops = ctld.insertIntoTroopsArray("2B11 mortar",_countOrTemplate.mortar,_troops) - _weight = _weight + getSoldiersWeight(_countOrTemplate.mortar, ctld.MORTAR_WEIGHT) - end - - if _countOrTemplate.jtac then - if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.jtac,_troops, "JTAC") - else - _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.jtac,_troops, "JTAC") - end - _hasJTAC = true - _weight = _weight + getSoldiersWeight(_countOrTemplate.jtac, ctld.JTAC_WEIGHT + ctld.RIFLE_WEIGHT) - end - - else - for _i = 1, _countOrTemplate do - - local _unitType = "Infantry AK" - - if _side == 2 then - if _i <=2 then - _unitType = "Soldier M249" - _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) - elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then - _unitType = "Paratrooper RPG-16" - _weight = _weight + getSoldiersWeight(1, ctld.RPG_WEIGHT) - elseif ctld.spawnStinger and _i > 4 and _i <= 5 then - _unitType = "Soldier stinger" - _weight = _weight + getSoldiersWeight(1, ctld.MANPAD_WEIGHT) - else - _unitType = "Soldier M4 GRG" - _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) + local function getSoldiersWeight(count, additionalWeight) + local _weight = 0 + for i = 1, count do + local _soldierWeight = math.random(90, 120) * ctld.SOLDIER_WEIGHT / 100 + _weight = _weight + _soldierWeight + ctld.KIT_WEIGHT + additionalWeight end - else - if _i <=2 then - _unitType = "Paratrooper AKS-74" - _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) - elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then - _unitType = "Paratrooper RPG-16" - _weight = _weight + getSoldiersWeight(1, ctld.RPG_WEIGHT) - elseif ctld.spawnStinger and _i > 4 and _i <= 5 then - _unitType = "SA-18 Igla manpad" - _weight = _weight + getSoldiersWeight(1, ctld.MANPAD_WEIGHT) - else - _unitType = "Infantry AK" - _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) - end - end - - local _unitId = ctld.getNextUnitId() - - _troops[_i] = { type = _unitType, unitId = _unitId, name = string.format("Dropped %s #%i", _unitType, _unitId) } + return _weight end - end - local _groupId = ctld.getNextGroupId() - local _groupName = "Dropped Group" - if _hasJTAC then - _groupName = "Dropped JTAC Group" - end - local _details = { units = _troops, groupId = _groupId, groupName = string.format("%s %i", _groupName, _groupId), side = _side, country = _country, weight = _weight, jtac = _hasJTAC } + if type(_countOrTemplate) == "table" then - return _details + if _countOrTemplate.aa then + if _side == 2 then + _troops = ctld.insertIntoTroopsArray("Soldier stinger",_countOrTemplate.aa,_troops) + else + _troops = ctld.insertIntoTroopsArray("SA-18 Igla manpad",_countOrTemplate.aa,_troops) + end + _weight = _weight + getSoldiersWeight(_countOrTemplate.aa, ctld.MANPAD_WEIGHT) + end + + if _countOrTemplate.inf then + if _side == 2 then + _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.inf,_troops) + else + _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.inf,_troops) + end + _weight = _weight + getSoldiersWeight(_countOrTemplate.inf, ctld.RIFLE_WEIGHT) + end + + if _countOrTemplate.mg then + if _side == 2 then + _troops = ctld.insertIntoTroopsArray("Soldier M249",_countOrTemplate.mg,_troops) + else + _troops = ctld.insertIntoTroopsArray("Paratrooper AKS-74",_countOrTemplate.mg,_troops) + end + _weight = _weight + getSoldiersWeight(_countOrTemplate.mg, ctld.MG_WEIGHT) + end + + if _countOrTemplate.at then + _troops = ctld.insertIntoTroopsArray("Paratrooper RPG-16",_countOrTemplate.at,_troops) + _weight = _weight + getSoldiersWeight(_countOrTemplate.at, ctld.RPG_WEIGHT) + end + + if _countOrTemplate.mortar then + _troops = ctld.insertIntoTroopsArray("2B11 mortar",_countOrTemplate.mortar,_troops) + _weight = _weight + getSoldiersWeight(_countOrTemplate.mortar, ctld.MORTAR_WEIGHT) + end + + if _countOrTemplate.jtac then + if _side == 2 then + _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.jtac,_troops, "JTAC") + else + _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.jtac,_troops, "JTAC") + end + _hasJTAC = true + _weight = _weight + getSoldiersWeight(_countOrTemplate.jtac, ctld.JTAC_WEIGHT + ctld.RIFLE_WEIGHT) + end + + else + for _i = 1, _countOrTemplate do + + local _unitType = "Infantry AK" + + if _side == 2 then + if _i <=2 then + _unitType = "Soldier M249" + _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) + elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then + _unitType = "Paratrooper RPG-16" + _weight = _weight + getSoldiersWeight(1, ctld.RPG_WEIGHT) + elseif ctld.spawnStinger and _i > 4 and _i <= 5 then + _unitType = "Soldier stinger" + _weight = _weight + getSoldiersWeight(1, ctld.MANPAD_WEIGHT) + else + _unitType = "Soldier M4 GRG" + _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) + end + else + if _i <=2 then + _unitType = "Paratrooper AKS-74" + _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) + elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then + _unitType = "Paratrooper RPG-16" + _weight = _weight + getSoldiersWeight(1, ctld.RPG_WEIGHT) + elseif ctld.spawnStinger and _i > 4 and _i <= 5 then + _unitType = "SA-18 Igla manpad" + _weight = _weight + getSoldiersWeight(1, ctld.MANPAD_WEIGHT) + else + _unitType = "Infantry AK" + _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) + end + end + + local _unitId = ctld.getNextUnitId() + + _troops[_i] = { type = _unitType, unitId = _unitId, name = string.format("Dropped %s #%i", _unitType, _unitId) } + end + end + + local _groupId = ctld.getNextGroupId() + local _groupName = "Dropped Group" + if _hasJTAC then + _groupName = "Dropped JTAC Group" + end + local _details = { units = _troops, groupId = _groupId, groupName = string.format("%s %i", _groupName, _groupId), side = _side, country = _country, weight = _weight, jtac = _hasJTAC } + + return _details end --Special F10 function for players for troops function ctld.unloadExtractTroops(_args) - local _heli = ctld.getTransportUnit(_args[1]) + local _heli = ctld.getTransportUnit(_args[1]) - if _heli == nil then - return false - end - - - local _extract = nil - if not ctld.inAir(_heli) then - if _heli:getCoalition() == 1 then - _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) - else - _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) + if _heli == nil then + return false end - end - if _extract ~= nil and not ctld.troopsOnboard(_heli, true) then - -- search for nearest troops to pickup - return ctld.extractTroops({_heli:getName(), true}) - else - return ctld.unloadTroops({_heli:getName(),true,true}) - end + local _extract = nil + if not ctld.inAir(_heli) then + if _heli:getCoalition() == 1 then + _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) + else + _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) + end + + end + + if _extract ~= nil and not ctld.troopsOnboard(_heli, true) then + -- search for nearest troops to pickup + return ctld.extractTroops({_heli:getName(), true}) + else + return ctld.unloadTroops({_heli:getName(),true,true}) + end end @@ -2999,1253 +2999,1253 @@ end -- load troops onto vehicle function ctld.loadTroops(_heli, _troops, _numberOrTemplate) - -- load troops + vehicles if c130 or herc - -- "M1045 HMMWV TOW" - -- "M1043 HMMWV Armament" - local _onboard = ctld.inTransitTroops[_heli:getName()] + -- load troops + vehicles if c130 or herc + -- "M1045 HMMWV TOW" + -- "M1043 HMMWV Armament" + local _onboard = ctld.inTransitTroops[_heli:getName()] - --number doesnt apply to vehicles - if _numberOrTemplate == nil or (type(_numberOrTemplate) ~= "table" and type(_numberOrTemplate) ~= "number") then - _numberOrTemplate = ctld.getTransportLimit(_heli:getTypeName()) - end + --number doesnt apply to vehicles + if _numberOrTemplate == nil or (type(_numberOrTemplate) ~= "table" and type(_numberOrTemplate) ~= "number") then + _numberOrTemplate = ctld.getTransportLimit(_heli:getTypeName()) + end - if _onboard == nil then - _onboard = { troops = {}, vehicles = {} } - end + if _onboard == nil then + _onboard = { troops = {}, vehicles = {} } + end - local _list - if _heli:getCoalition() == 1 then - _list = ctld.vehiclesForTransportRED - else - _list = ctld.vehiclesForTransportBLUE - end + local _list + if _heli:getCoalition() == 1 then + _list = ctld.vehiclesForTransportRED + else + _list = ctld.vehiclesForTransportBLUE + end - if _troops then - _onboard.troops = ctld.generateTroopTypes(_heli:getCoalition(), _numberOrTemplate, _heli:getCountry()) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded troops into %2", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + if _troops then + _onboard.troops = ctld.generateTroopTypes(_heli:getCoalition(), _numberOrTemplate, _heli:getCountry()) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded troops into %2", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - ctld.processCallback({unit = _heli, onboard = _onboard.troops, action = "load_troops"}) - else + ctld.processCallback({unit = _heli, onboard = _onboard.troops, action = "load_troops"}) + else - _onboard.vehicles = ctld.generateVehiclesForTransport(_heli:getCoalition(), _heli:getCountry()) + _onboard.vehicles = ctld.generateVehiclesForTransport(_heli:getCoalition(), _heli:getCountry()) - local _count = #_list + local _count = #_list - ctld.processCallback({unit = _heli, onboard = _onboard.vehicles, action = "load_vehicles"}) + ctld.processCallback({unit = _heli, onboard = _onboard.vehicles, action = "load_vehicles"}) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded %2 vehicles into %3", ctld.getPlayerNameOrType(_heli), _count, _heli:getTypeName()), 10) - end + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded %2 vehicles into %3", ctld.getPlayerNameOrType(_heli), _count, _heli:getTypeName()), 10) + end - ctld.inTransitTroops[_heli:getName()] = _onboard - ctld.adaptWeightToCargo(_heli:getName()) + ctld.inTransitTroops[_heli:getName()] = _onboard + ctld.adaptWeightToCargo(_heli:getName()) end function ctld.generateVehiclesForTransport(_side, _country) - local _vehicles = {} - local _list - if _side == 1 then - _list = ctld.vehiclesForTransportRED - else - _list = ctld.vehiclesForTransportBLUE - end + local _vehicles = {} + local _list + if _side == 1 then + _list = ctld.vehiclesForTransportRED + else + _list = ctld.vehiclesForTransportBLUE + end - for _i, _type in ipairs(_list) do + for _i, _type in ipairs(_list) do - local _unitId = ctld.getNextUnitId() - local _weight = ctld.vehiclesWeight[_type] or 2500 - _vehicles[_i] = { type = _type, unitId = _unitId, name = string.format("Dropped %s #%i", _type, _unitId), weight = _weight } - end + local _unitId = ctld.getNextUnitId() + local _weight = ctld.vehiclesWeight[_type] or 2500 + _vehicles[_i] = { type = _type, unitId = _unitId, name = string.format("Dropped %s #%i", _type, _unitId), weight = _weight } + end - local _groupId = ctld.getNextGroupId() - local _details = { units = _vehicles, groupId = _groupId, groupName = string.format("Dropped Group %i", _groupId), side = _side, country = _country } + local _groupId = ctld.getNextGroupId() + local _details = { units = _vehicles, groupId = _groupId, groupName = string.format("Dropped Group %i", _groupId), side = _side, country = _country } - return _details + return _details end function ctld.loadUnloadFOBCrate(_args) - local _heli = ctld.getTransportUnit(_args[1]) - local _troops = _args[2] + local _heli = ctld.getTransportUnit(_args[1]) + local _troops = _args[2] - if _heli == nil then - return - end + if _heli == nil then + return + end - if ctld.inAir(_heli) == true then - return - end + if ctld.inAir(_heli) == true then + return + end - local _side = _heli:getCoalition() - - local _inZone = ctld.inLogisticsZone(_heli) - local _crateOnboard = ctld.inTransitFOBCrates[_heli:getName()] ~= nil - - if _inZone == false and _crateOnboard == true then - - ctld.inTransitFOBCrates[_heli:getName()] = nil - - local _position = _heli:getPosition() - - --try to spawn at 6 oclock to us - local _angle = math.atan2(_position.x.z, _position.x.x) - local _xOffset = math.cos(_angle) * -60 - local _yOffset = math.sin(_angle) * -60 - - local _point = _heli:getPoint() - local _side = _heli:getCoalition() - local _unitId = ctld.getNextUnitId() + local _inZone = ctld.inLogisticsZone(_heli) + local _crateOnboard = ctld.inTransitFOBCrates[_heli:getName()] ~= nil - local _name = string.format("FOB Crate #%i", _unitId) + if _inZone == false and _crateOnboard == true then - local _spawnedCrate = ctld.spawnFOBCrateStatic(_heli:getCountry(), ctld.getNextUnitId(), { x = _point.x + _xOffset, z = _point.z + _yOffset }, _name) + ctld.inTransitFOBCrates[_heli:getName()] = nil - if _side == 1 then - ctld.droppedFOBCratesRED[_name] = _name - else - ctld.droppedFOBCratesBLUE[_name] = _name - end + local _position = _heli:getPosition() - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 delivered a FOB Crate", ctld.getPlayerNameOrType(_heli)), 10) + --try to spawn at 6 oclock to us + local _angle = math.atan2(_position.x.z, _position.x.x) + local _xOffset = math.cos(_angle) * -60 + local _yOffset = math.sin(_angle) * -60 - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Delivered FOB Crate 60m at 6'oclock to you"), 10) + local _point = _heli:getPoint() - elseif _inZone == true and _crateOnboard == true then + local _side = _heli:getCoalition() - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate dropped back to base"), 10) + local _unitId = ctld.getNextUnitId() - ctld.inTransitFOBCrates[_heli:getName()] = nil + local _name = string.format("FOB Crate #%i", _unitId) - elseif _inZone == true and _crateOnboard == false then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) + local _spawnedCrate = ctld.spawnFOBCrateStatic(_heli:getCountry(), ctld.getNextUnitId(), { x = _point.x + _xOffset, z = _point.z + _yOffset }, _name) - ctld.inTransitFOBCrates[_heli:getName()] = true + if _side == 1 then + ctld.droppedFOBCratesRED[_name] = _name + else + ctld.droppedFOBCratesBLUE[_name] = _name + end - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 delivered a FOB Crate", ctld.getPlayerNameOrType(_heli)), 10) - else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Delivered FOB Crate 60m at 6'oclock to you"), 10) - -- nearest Crate - local _crates = ctld.getCratesAndDistance(_heli) - local _nearestCrate = ctld.getClosestCrate(_heli, _crates, "FOB") + elseif _inZone == true and _crateOnboard == true then - if _nearestCrate ~= nil and _nearestCrate.dist < 150 then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate dropped back to base"), 10) - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) - ctld.inTransitFOBCrates[_heli:getName()] = true + ctld.inTransitFOBCrates[_heli:getName()] = nil - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) + elseif _inZone == true and _crateOnboard == false then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) - if _side == 1 then - ctld.droppedFOBCratesRED[_nearestCrate.crateUnit:getName()] = nil - else - ctld.droppedFOBCratesBLUE[_nearestCrate.crateUnit:getName()] = nil - end + ctld.inTransitFOBCrates[_heli:getName()] = true - --remove - _nearestCrate.crateUnit:destroy() + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("There are no friendly logistic units nearby to load a FOB crate from!"), 10) + + -- nearest Crate + local _crates = ctld.getCratesAndDistance(_heli) + local _nearestCrate = ctld.getClosestCrate(_heli, _crates, "FOB") + + if _nearestCrate ~= nil and _nearestCrate.dist < 150 then + + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) + ctld.inTransitFOBCrates[_heli:getName()] = true + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) + + if _side == 1 then + ctld.droppedFOBCratesRED[_nearestCrate.crateUnit:getName()] = nil + else + ctld.droppedFOBCratesBLUE[_nearestCrate.crateUnit:getName()] = nil + end + + --remove + _nearestCrate.crateUnit:destroy() + + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("There are no friendly logistic units nearby to load a FOB crate from!"), 10) + end end - end end function ctld.loadTroopsFromZone(_args) - local _heli = ctld.getTransportUnit(_args[1]) - local _troops = _args[2] - local _groupTemplate = _args[3] or "" - local _allowExtract = _args[4] + local _heli = ctld.getTransportUnit(_args[1]) + local _troops = _args[2] + local _groupTemplate = _args[3] or "" + local _allowExtract = _args[4] - if _heli == nil then - return false - end - - local _zone = ctld.inPickupZone(_heli) - - if ctld.troopsOnboard(_heli, _troops) then - - if _troops then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) + if _heli == nil then + return false end - return false - end + local _zone = ctld.inPickupZone(_heli) - local _extract + if ctld.troopsOnboard(_heli, _troops) then - if _allowExtract then - -- first check for extractable troops regardless of if we're in a zone or not - if _troops then - if _heli:getCoalition() == 1 then - _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) - else - _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) - end - else + if _troops then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) + end - if _heli:getCoalition() == 1 then - _extract = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) - else - _extract = ctld.findNearestGroup(_heli, ctld.droppedVehiclesBLUE) - end - end - end - - if _extract ~= nil then - -- search for nearest troops to pickup - return ctld.extractTroops({_heli:getName(), _troops}) - elseif _zone.inZone == true then - - if _zone.limit - 1 >= 0 then - -- decrease zone counter by 1 - ctld.updateZoneCounter(_zone.index, -1) - - ctld.loadTroops(_heli, _troops,_groupTemplate) - - return true - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("This area has no more reinforcements available!"), 20) - - return false + return false end - else + local _extract + if _allowExtract then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone and no one is nearby to extract"), 10) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone"), 10) + -- first check for extractable troops regardless of if we're in a zone or not + if _troops then + if _heli:getCoalition() == 1 then + _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) + else + _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) + end + else + + if _heli:getCoalition() == 1 then + _extract = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) + else + _extract = ctld.findNearestGroup(_heli, ctld.droppedVehiclesBLUE) + end + end end - return false - end + if _extract ~= nil then + -- search for nearest troops to pickup + return ctld.extractTroops({_heli:getName(), _troops}) + elseif _zone.inZone == true then + + if _zone.limit - 1 >= 0 then + -- decrease zone counter by 1 + ctld.updateZoneCounter(_zone.index, -1) + + ctld.loadTroops(_heli, _troops,_groupTemplate) + + return true + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("This area has no more reinforcements available!"), 20) + + return false + end + + else + if _allowExtract then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone and no one is nearby to extract"), 10) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone"), 10) + end + + return false + end end function ctld.unloadTroops(_args) - local _heli = ctld.getTransportUnit(_args[1]) - local _troops = _args[2] + local _heli = ctld.getTransportUnit(_args[1]) + local _troops = _args[2] - if _heli == nil then - return false - end - - local _zone = ctld.inPickupZone(_heli) - if not ctld.troopsOnboard(_heli, _troops) then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No one to unload"), 10) - - return false - else - - -- troops must be onboard to get here - if _zone.inZone == true then - - if _troops then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped troops back to base"), 20) - - ctld.processCallback({unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].troops, action = "unload_troops_zone"}) - - ctld.inTransitTroops[_heli:getName()].troops = nil - - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped vehicles back to base"), 20) - - ctld.processCallback({unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].vehicles, action = "unload_vehicles_zone"}) - - ctld.inTransitTroops[_heli:getName()].vehicles = nil - end - - ctld.adaptWeightToCargo(_heli:getName()) - - -- increase zone counter by 1 - ctld.updateZoneCounter(_zone.index, 1) - - return true - - elseif ctld.troopsOnboard(_heli, _troops) then - - return ctld.deployTroops(_heli, _troops) + if _heli == nil then + return false + end + + local _zone = ctld.inPickupZone(_heli) + if not ctld.troopsOnboard(_heli, _troops) then + + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No one to unload"), 10) + + return false + else + + -- troops must be onboard to get here + if _zone.inZone == true then + + if _troops then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped troops back to base"), 20) + + ctld.processCallback({unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].troops, action = "unload_troops_zone"}) + + ctld.inTransitTroops[_heli:getName()].troops = nil + + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped vehicles back to base"), 20) + + ctld.processCallback({unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].vehicles, action = "unload_vehicles_zone"}) + + ctld.inTransitTroops[_heli:getName()].vehicles = nil + end + + ctld.adaptWeightToCargo(_heli:getName()) + + -- increase zone counter by 1 + ctld.updateZoneCounter(_zone.index, 1) + + return true + + elseif ctld.troopsOnboard(_heli, _troops) then + + return ctld.deployTroops(_heli, _troops) + end end - end end function ctld.extractTroops(_args) - local _heli = ctld.getTransportUnit(_args[1]) - local _troops = _args[2] + local _heli = ctld.getTransportUnit(_args[1]) + local _troops = _args[2] - if _heli == nil then - return false - end + if _heli == nil then + return false + end - if ctld.inAir(_heli) then - return false - end + if ctld.inAir(_heli) then + return false + end + + if ctld.troopsOnboard(_heli, _troops) then + if _troops then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) + end + + return false + end + + local _onboard = ctld.inTransitTroops[_heli:getName()] + + if _onboard == nil then + _onboard = { troops = nil, vehicles = nil } + end + + local _extracted = false - if ctld.troopsOnboard(_heli, _troops) then if _troops then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) - end - return false - end + local _extractTroops - local _onboard = ctld.inTransitTroops[_heli:getName()] - - if _onboard == nil then - _onboard = { troops = nil, vehicles = nil } - end - - local _extracted = false - - if _troops then - - local _extractTroops - - if _heli:getCoalition() == 1 then - _extractTroops = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) - else - _extractTroops = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) - end + if _heli:getCoalition() == 1 then + _extractTroops = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) + else + _extractTroops = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) + end - if _extractTroops ~= nil then + if _extractTroops ~= nil then - local _limit = ctld.getTransportLimit(_heli:getTypeName()) + local _limit = ctld.getTransportLimit(_heli:getTypeName()) - local _size = #_extractTroops.group:getUnits() + local _size = #_extractTroops.group:getUnits() - if _limit < #_extractTroops.group:getUnits() then + if _limit < #_extractTroops.group:getUnits() then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3", _size, _limit, _heli:getTypeName()), 20) + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3", _size, _limit, _heli:getTypeName()), 20) - return - end + return + end - _onboard.troops = _extractTroops.details - _onboard.troops.weight = #_extractTroops.group:getUnits() * 130 -- default to 130kg per soldier + _onboard.troops = _extractTroops.details + _onboard.troops.weight = #_extractTroops.group:getUnits() * 130 -- default to 130kg per soldier - if _extractTroops.group:getName():lower():find("jtac") then - _onboard.troops.jtac = true - end + if _extractTroops.group:getName():lower():find("jtac") then + _onboard.troops.jtac = true + end - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 extracted troops in %2 from combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 extracted troops in %2 from combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - if _heli:getCoalition() == 1 then - ctld.droppedTroopsRED[_extractTroops.group:getName()] = nil - else - ctld.droppedTroopsBLUE[_extractTroops.group:getName()] = nil - end + if _heli:getCoalition() == 1 then + ctld.droppedTroopsRED[_extractTroops.group:getName()] = nil + else + ctld.droppedTroopsBLUE[_extractTroops.group:getName()] = nil + end - ctld.processCallback({unit = _heli, extracted = _extractTroops, action = "extract_troops"}) + ctld.processCallback({unit = _heli, extracted = _extractTroops, action = "extract_troops"}) - --remove - _extractTroops.group:destroy() + --remove + _extractTroops.group:destroy() - _extracted = true - else - _onboard.troops = nil - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable troops nearby!"), 20) - end - - else - - local _extractVehicles - - - if _heli:getCoalition() == 1 then - - _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) - else - - _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesBLUE) - end - - if _extractVehicles ~= nil then - _onboard.vehicles = _extractVehicles.details - - if _heli:getCoalition() == 1 then - - ctld.droppedVehiclesRED[_extractVehicles.group:getName()] = nil - else - - ctld.droppedVehiclesBLUE[_extractVehicles.group:getName()] = nil - end - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 extracted vehicles in %2 from combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - - ctld.processCallback({unit = _heli, extracted = _extractVehicles, action = "extract_vehicles"}) - --remove - _extractVehicles.group:destroy() - _extracted = true + _extracted = true + else + _onboard.troops = nil + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable troops nearby!"), 20) + end else - _onboard.vehicles = nil - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable vehicles nearby!"), 20) + + local _extractVehicles + + + if _heli:getCoalition() == 1 then + + _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) + else + + _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesBLUE) + end + + if _extractVehicles ~= nil then + _onboard.vehicles = _extractVehicles.details + + if _heli:getCoalition() == 1 then + + ctld.droppedVehiclesRED[_extractVehicles.group:getName()] = nil + else + + ctld.droppedVehiclesBLUE[_extractVehicles.group:getName()] = nil + end + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 extracted vehicles in %2 from combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + + ctld.processCallback({unit = _heli, extracted = _extractVehicles, action = "extract_vehicles"}) + --remove + _extractVehicles.group:destroy() + _extracted = true + + else + _onboard.vehicles = nil + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable vehicles nearby!"), 20) + end end - end - ctld.inTransitTroops[_heli:getName()] = _onboard - ctld.adaptWeightToCargo(_heli:getName()) + ctld.inTransitTroops[_heli:getName()] = _onboard + ctld.adaptWeightToCargo(_heli:getName()) - return _extracted + return _extracted end function ctld.checkTroopStatus(_args) - local _unitName = _args[1] - --list onboard troops, if c130 - local _heli = ctld.getTransportUnit(_unitName) + local _unitName = _args[1] + --list onboard troops, if c130 + local _heli = ctld.getTransportUnit(_unitName) - if _heli == nil then - return - end + if _heli == nil then + return + end - local _, _message = ctld.getWeightOfCargo(_unitName) - if _message and _message ~= "" then - ctld.displayMessageToGroup(_heli, _message, 10) - end + local _, _message = ctld.getWeightOfCargo(_unitName) + if _message and _message ~= "" then + ctld.displayMessageToGroup(_heli, _message, 10) + end end -- Removes troops from transport when it dies function ctld.checkTransportStatus() - timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 3) + timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 3) - for _, _name in ipairs(ctld.transportPilotNames) do + for _, _name in ipairs(ctld.transportPilotNames) do - local _transUnit = ctld.getTransportUnit(_name) + local _transUnit = ctld.getTransportUnit(_name) - if _transUnit == nil then - --env.info("CTLD Transport Unit Dead event") - ctld.inTransitTroops[_name] = nil - ctld.inTransitFOBCrates[_name] = nil - ctld.inTransitSlingLoadCrates[_name] = nil + if _transUnit == nil then + --env.info("CTLD Transport Unit Dead event") + ctld.inTransitTroops[_name] = nil + ctld.inTransitFOBCrates[_name] = nil + ctld.inTransitSlingLoadCrates[_name] = nil + end end - end end function ctld.adaptWeightToCargo(unitName) - local _weight = ctld.getWeightOfCargo(unitName) - trigger.action.setUnitInternalCargo(unitName, _weight) + local _weight = ctld.getWeightOfCargo(unitName) + trigger.action.setUnitInternalCargo(unitName, _weight) end function ctld.getWeightOfCargo(unitName) - local FOB_CRATE_WEIGHT = 800 - local _weight = 0 - local _description = "" + local FOB_CRATE_WEIGHT = 800 + local _weight = 0 + local _description = "" - ctld.inTransitSlingLoadCrates[unitName] = ctld.inTransitSlingLoadCrates[unitName] or {} + ctld.inTransitSlingLoadCrates[unitName] = ctld.inTransitSlingLoadCrates[unitName] or {} - -- add troops weight - if ctld.inTransitTroops[unitName] then - local _inTransit = ctld.inTransitTroops[unitName] - if _inTransit then - local _troops = _inTransit.troops - if _troops and _troops.units then - _description = _description .. ctld.i18n_translate("%1 troops onboard (%2 kg)\n", #_troops.units, _troops.weight) - _weight = _weight + _troops.weight - end - local _vehicles = _inTransit.vehicles - if _vehicles and _vehicles.units then - for _, _unit in pairs(_vehicles.units) do - _weight = _weight + _unit.weight + -- add troops weight + if ctld.inTransitTroops[unitName] then + local _inTransit = ctld.inTransitTroops[unitName] + if _inTransit then + local _troops = _inTransit.troops + if _troops and _troops.units then + _description = _description .. ctld.i18n_translate("%1 troops onboard (%2 kg)\n", #_troops.units, _troops.weight) + _weight = _weight + _troops.weight + end + local _vehicles = _inTransit.vehicles + if _vehicles and _vehicles.units then + for _, _unit in pairs(_vehicles.units) do + _weight = _weight + _unit.weight + end + _description = _description .. ctld.i18n_translate("%1 vehicles onboard (%2)\n", #_vehicles.units, _weight) + end end - _description = _description .. ctld.i18n_translate("%1 vehicles onboard (%2)\n", #_vehicles.units, _weight) - end end - end - -- add FOB crates weight - if ctld.inTransitFOBCrates[unitName] then - _weight = _weight + FOB_CRATE_WEIGHT - _description = _description .. ctld.i18n_translate("1 FOB Crate oboard (%1 kg)\n", FOB_CRATE_WEIGHT) - end - - -- add simulated slingload crates weight - for i = 1, #ctld.inTransitSlingLoadCrates[unitName] do - local _crate = ctld.inTransitSlingLoadCrates[unitName][i] - if _crate and _crate.simulatedSlingload then - _weight = _weight + _crate.weight - _description = _description .. ctld.i18n_translate("%1 crate onboard (%2 kg)\n", _crate.desc, _crate.weight) + -- add FOB crates weight + if ctld.inTransitFOBCrates[unitName] then + _weight = _weight + FOB_CRATE_WEIGHT + _description = _description .. ctld.i18n_translate("1 FOB Crate oboard (%1 kg)\n", FOB_CRATE_WEIGHT) end - end - if _description ~= "" then - _description = _description .. ctld.i18n_translate("Total weight of cargo : %1 kg\n", _weight) - else - _description = ctld.i18n_translate("No cargo.") - end - return _weight, _description + -- add simulated slingload crates weight + for i = 1, #ctld.inTransitSlingLoadCrates[unitName] do + local _crate = ctld.inTransitSlingLoadCrates[unitName][i] + if _crate and _crate.simulatedSlingload then + _weight = _weight + _crate.weight + _description = _description .. ctld.i18n_translate("%1 crate onboard (%2 kg)\n", _crate.desc, _crate.weight) + end + end + if _description ~= "" then + _description = _description .. ctld.i18n_translate("Total weight of cargo : %1 kg\n", _weight) + else + _description = ctld.i18n_translate("No cargo.") + end + + return _weight, _description end function ctld.checkHoverStatus() - timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1.0) + timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1.0) - local _status, _result = pcall(function() + local _status, _result = pcall(function() - for _, _name in ipairs(ctld.transportPilotNames) do + for _, _name in ipairs(ctld.transportPilotNames) do - local _reset = true - local _transUnit = ctld.getTransportUnit(_name) - local _transUnitTypeName = _transUnit and _transUnit:getTypeName() - local _cargoCapacity = ctld.internalCargoLimits[_transUnitTypeName] or 1 - ctld.inTransitSlingLoadCrates[_name] = ctld.inTransitSlingLoadCrates[_name] or {} + local _reset = true + local _transUnit = ctld.getTransportUnit(_name) + local _transUnitTypeName = _transUnit and _transUnit:getTypeName() + local _cargoCapacity = ctld.internalCargoLimits[_transUnitTypeName] or 1 + ctld.inTransitSlingLoadCrates[_name] = ctld.inTransitSlingLoadCrates[_name] or {} - --only check transports that are hovering and not planes - if _transUnit ~= nil and #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity and ctld.inAir(_transUnit) and ctld.unitCanCarryVehicles(_transUnit) == false and not ctld.unitDynamicCargoCapable(_transUnit) then + --only check transports that are hovering and not planes + if _transUnit ~= nil and #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity and ctld.inAir(_transUnit) and ctld.unitCanCarryVehicles(_transUnit) == false and not ctld.unitDynamicCargoCapable(_transUnit) then - local _crates = ctld.getCratesAndDistance(_transUnit) + local _crates = ctld.getCratesAndDistance(_transUnit) - for _, _crate in pairs(_crates) do - local _crateUnitName = _crate.crateUnit:getName() - if _crate.dist < ctld.maxDistanceFromCrate and _crate.details.unit ~= "FOB" then + for _, _crate in pairs(_crates) do + local _crateUnitName = _crate.crateUnit:getName() + if _crate.dist < ctld.maxDistanceFromCrate and _crate.details.unit ~= "FOB" then - --check height! - local _height = _transUnit:getPoint().y - _crate.crateUnit:getPoint().y - if _height > ctld.minimumHoverHeight and _height <= ctld.maximumHoverHeight then + --check height! + local _height = _transUnit:getPoint().y - _crate.crateUnit:getPoint().y + if _height > ctld.minimumHoverHeight and _height <= ctld.maximumHoverHeight then - local _time = ctld.hoverStatus[_name] + local _time = ctld.hoverStatus[_name] - if _time == nil then - ctld.hoverStatus[_name] = ctld.hoverTime - _time = ctld.hoverTime - else - _time = ctld.hoverStatus[_name] - 1 - ctld.hoverStatus[_name] = _time - end + if _time == nil then + ctld.hoverStatus[_name] = ctld.hoverTime + _time = ctld.hoverTime + else + _time = ctld.hoverStatus[_name] - 1 + ctld.hoverStatus[_name] = _time + end - if _time > 0 then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!", _crate.details.desc, _time), 10,true) - else - ctld.hoverStatus[_name] = nil - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10,true) + if _time > 0 then + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!", _crate.details.desc, _time), 10,true) + else + ctld.hoverStatus[_name] = nil + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10,true) - --crates been moved once! - ctld.crateMove[_crateUnitName] = nil + --crates been moved once! + ctld.crateMove[_crateUnitName] = nil - if _transUnit:getCoalition() == 1 then - ctld.spawnedCratesRED[_crateUnitName] = nil - else - ctld.spawnedCratesBLUE[_crateUnitName] = nil + if _transUnit:getCoalition() == 1 then + ctld.spawnedCratesRED[_crateUnitName] = nil + else + ctld.spawnedCratesBLUE[_crateUnitName] = nil + end + + _crate.crateUnit:destroy() + + local _copiedCrate = mist.utils.deepCopy(_crate.details) + _copiedCrate.simulatedSlingload = true + table.insert(ctld.inTransitSlingLoadCrates[_name], _copiedCrate) + ctld.adaptWeightToCargo(_name) + end + + _reset = false + + break + elseif _height <= ctld.minimumHoverHeight then + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Too low to hook %1 crate.\n\nHold hover for %2 seconds", _crate.details.desc, ctld.hoverTime), 5,true) + break + else + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Too high to hook %1 crate.\n\nHold hover for %2 seconds", _crate.details.desc, ctld.hoverTime), 5,true) + break + end + end end - - _crate.crateUnit:destroy() - - local _copiedCrate = mist.utils.deepCopy(_crate.details) - _copiedCrate.simulatedSlingload = true - table.insert(ctld.inTransitSlingLoadCrates[_name], _copiedCrate) - ctld.adaptWeightToCargo(_name) - end - - _reset = false - - break - elseif _height <= ctld.minimumHoverHeight then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Too low to hook %1 crate.\n\nHold hover for %2 seconds", _crate.details.desc, ctld.hoverTime), 5,true) - break - else - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Too high to hook %1 crate.\n\nHold hover for %2 seconds", _crate.details.desc, ctld.hoverTime), 5,true) - break end - end + + if _reset then + ctld.hoverStatus[_name] = nil + end end - end + end) - if _reset then - ctld.hoverStatus[_name] = nil - end + if (not _status) then + env.error(string.format("CTLD ERROR: %s", _result)) end - end) - - if (not _status) then - env.error(string.format("CTLD ERROR: %s", _result)) - end end function ctld.loadNearbyCrate(_name) - local _transUnit = ctld.getTransportUnit(_name) + local _transUnit = ctld.getTransportUnit(_name) - if _transUnit ~= nil then + if _transUnit ~= nil then - local _cargoCapacity = ctld.internalCargoLimits[_transUnit:getTypeName()] or 1 - ctld.inTransitSlingLoadCrates[_name] = ctld.inTransitSlingLoadCrates[_name] or {} + local _cargoCapacity = ctld.internalCargoLimits[_transUnit:getTypeName()] or 1 + ctld.inTransitSlingLoadCrates[_name] = ctld.inTransitSlingLoadCrates[_name] or {} - if ctld.inAir(_transUnit) then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("You must land before you can load a crate!"), 10,true) - return - end - - local _crates = ctld.getCratesAndDistance(_transUnit) - local loaded = false - for _, _crate in pairs(_crates) do - - if _crate.dist < 50.0 then - if #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10) - - if _transUnit:getCoalition() == 1 then - ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil - else - ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil - end - - ctld.crateMove[_crate.crateUnit:getName()] = nil - - _crate.crateUnit:destroy() - - local _copiedCrate = mist.utils.deepCopy(_crate.details) - _copiedCrate.simulatedSlingload = true - table.insert(ctld.inTransitSlingLoadCrates[_name], _copiedCrate) - loaded = true - ctld.adaptWeightToCargo(_name) - else - -- Max crates onboard - local outputMsg = ctld.i18n_translate("Maximum number of crates are on board!") - for i = 1, _cargoCapacity do - outputMsg = outputMsg .. "\n" .. ctld.inTransitSlingLoadCrates[_name][i].desc - end - ctld.displayMessageToGroup(_transUnit, outputMsg, 10,true) - return + if ctld.inAir(_transUnit) then + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("You must land before you can load a crate!"), 10,true) + return + end + + local _crates = ctld.getCratesAndDistance(_transUnit) + local loaded = false + for _, _crate in pairs(_crates) do + + if _crate.dist < 50.0 then + if #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity then + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10) + + if _transUnit:getCoalition() == 1 then + ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil + else + ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil + end + + ctld.crateMove[_crate.crateUnit:getName()] = nil + + _crate.crateUnit:destroy() + + local _copiedCrate = mist.utils.deepCopy(_crate.details) + _copiedCrate.simulatedSlingload = true + table.insert(ctld.inTransitSlingLoadCrates[_name], _copiedCrate) + loaded = true + ctld.adaptWeightToCargo(_name) + else + -- Max crates onboard + local outputMsg = ctld.i18n_translate("Maximum number of crates are on board!") + for i = 1, _cargoCapacity do + outputMsg = outputMsg .. "\n" .. ctld.inTransitSlingLoadCrates[_name][i].desc + end + ctld.displayMessageToGroup(_transUnit, outputMsg, 10,true) + return + end + end + end + if not loaded then + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("No Crates within 50m to load!"), 10,true) end - end end - if not loaded then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("No Crates within 50m to load!"), 10,true) - end - end end --check each minute if the beacons' batteries have failed, and stop them accordingly --there's no more need to actually refresh the beacons, since we set "loop" to true. function ctld.refreshRadioBeacons() - timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 60) + timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 60) - for _index, _beaconDetails in ipairs(ctld.deployedRadioBeacons) do + for _index, _beaconDetails in ipairs(ctld.deployedRadioBeacons) do - if ctld.updateRadioBeacon(_beaconDetails) == false then + if ctld.updateRadioBeacon(_beaconDetails) == false then - --search used frequencies + remove, add back to unused + --search used frequencies + remove, add back to unused - for _i, _freq in ipairs(ctld.usedUHFFrequencies) do - if _freq == _beaconDetails.uhf then + for _i, _freq in ipairs(ctld.usedUHFFrequencies) do + if _freq == _beaconDetails.uhf then - table.insert(ctld.freeUHFFrequencies, _freq) - table.remove(ctld.usedUHFFrequencies, _i) + table.insert(ctld.freeUHFFrequencies, _freq) + table.remove(ctld.usedUHFFrequencies, _i) + end + end + + for _i, _freq in ipairs(ctld.usedVHFFrequencies) do + if _freq == _beaconDetails.vhf then + + table.insert(ctld.freeVHFFrequencies, _freq) + table.remove(ctld.usedVHFFrequencies, _i) + end + end + + for _i, _freq in ipairs(ctld.usedFMFrequencies) do + if _freq == _beaconDetails.fm then + + table.insert(ctld.freeFMFrequencies, _freq) + table.remove(ctld.usedFMFrequencies, _i) + end + end + + --clean up beacon table + table.remove(ctld.deployedRadioBeacons, _index) end - end - - for _i, _freq in ipairs(ctld.usedVHFFrequencies) do - if _freq == _beaconDetails.vhf then - - table.insert(ctld.freeVHFFrequencies, _freq) - table.remove(ctld.usedVHFFrequencies, _i) - end - end - - for _i, _freq in ipairs(ctld.usedFMFrequencies) do - if _freq == _beaconDetails.fm then - - table.insert(ctld.freeFMFrequencies, _freq) - table.remove(ctld.usedFMFrequencies, _i) - end - end - - --clean up beacon table - table.remove(ctld.deployedRadioBeacons, _index) end - end end function ctld.getClockDirection(_heli, _crate) - -- Source: Helicopter Script - Thanks! + -- Source: Helicopter Script - Thanks! - local _position = _crate:getPosition().p -- get position of crate - local _playerPosition = _heli:getPosition().p -- get position of helicopter - local _relativePosition = mist.vec.sub(_position, _playerPosition) + local _position = _crate:getPosition().p -- get position of crate + local _playerPosition = _heli:getPosition().p -- get position of helicopter + local _relativePosition = mist.vec.sub(_position, _playerPosition) - local _playerHeading = mist.getHeading(_heli) -- the rest of the code determines the 'o'clock' bearing of the missile relative to the helicopter + local _playerHeading = mist.getHeading(_heli) -- the rest of the code determines the 'o'clock' bearing of the missile relative to the helicopter - local _headingVector = { x = math.cos(_playerHeading), y = 0, z = math.sin(_playerHeading) } + local _headingVector = { x = math.cos(_playerHeading), y = 0, z = math.sin(_playerHeading) } - local _headingVectorPerpendicular = { x = math.cos(_playerHeading + math.pi / 2), y = 0, z = math.sin(_playerHeading + math.pi / 2) } + local _headingVectorPerpendicular = { x = math.cos(_playerHeading + math.pi / 2), y = 0, z = math.sin(_playerHeading + math.pi / 2) } - local _forwardDistance = mist.vec.dp(_relativePosition, _headingVector) + local _forwardDistance = mist.vec.dp(_relativePosition, _headingVector) - local _rightDistance = mist.vec.dp(_relativePosition, _headingVectorPerpendicular) + local _rightDistance = mist.vec.dp(_relativePosition, _headingVectorPerpendicular) - local _angle = math.atan2(_rightDistance, _forwardDistance) * 180 / math.pi + local _angle = math.atan2(_rightDistance, _forwardDistance) * 180 / math.pi - if _angle < 0 then - _angle = 360 + _angle - end - _angle = math.floor(_angle * 12 / 360 + 0.5) - if _angle == 0 then - _angle = 12 - end + if _angle < 0 then + _angle = 360 + _angle + end + _angle = math.floor(_angle * 12 / 360 + 0.5) + if _angle == 0 then + _angle = 12 + end - return _angle + return _angle end function ctld.getCompassBearing(_ref, _unitPos) - _ref = mist.utils.makeVec3(_ref, 0) -- turn it into Vec3 if it is not already. - _unitPos = mist.utils.makeVec3(_unitPos, 0) -- turn it into Vec3 if it is not already. + _ref = mist.utils.makeVec3(_ref, 0) -- turn it into Vec3 if it is not already. + _unitPos = mist.utils.makeVec3(_unitPos, 0) -- turn it into Vec3 if it is not already. - local _vec = { x = _unitPos.x - _ref.x, y = _unitPos.y - _ref.y, z = _unitPos.z - _ref.z } + local _vec = { x = _unitPos.x - _ref.x, y = _unitPos.y - _ref.y, z = _unitPos.z - _ref.z } - local _dir = mist.utils.getDir(_vec, _ref) + local _dir = mist.utils.getDir(_vec, _ref) - local _bearing = mist.utils.round(mist.utils.toDegree(_dir), 0) + local _bearing = mist.utils.round(mist.utils.toDegree(_dir), 0) - return _bearing + return _bearing end function ctld.listNearbyCrates(_args) - local _message = "" + local _message = "" - local _heli = ctld.getTransportUnit(_args[1]) + local _heli = ctld.getTransportUnit(_args[1]) - if _heli == nil then + if _heli == nil then - return -- no heli! - end - - local _crates = ctld.getCratesAndDistance(_heli) - - --sort - local _sort = function( a,b ) return a.dist < b.dist end - table.sort(_crates,_sort) - - for _, _crate in pairs(_crates) do - - if _crate.dist < 1000 and _crate.details.unit ~= "FOB" then - _message = ctld.i18n_translate("%1\n%2 crate - kg %3 - %4 m - %5 o'clock", _message, _crate.details.desc, _crate.details.weight, _crate.dist, ctld.getClockDirection(_heli, _crate.crateUnit)) - end - end - - - local _fobMsg = "" - for _, _fobCrate in pairs(_crates) do - - if _fobCrate.dist < 1000 and _fobCrate.details.unit == "FOB" then - _fobMsg = _fobMsg .. ctld.i18n_translate("FOB Crate - %1 m - %2 o'clock\n", _fobCrate.dist, ctld.getClockDirection(_heli, _fobCrate.crateUnit)) - end - end - - local _txt = ctld.i18n_translate("No Nearby Crates") - if _message ~= "" or _fobMsg ~= "" then - - _txt = "" - - if _message ~= "" then - _txt = ctld.i18n_translate("Nearby Crates:\n%1", _message) + return -- no heli! end - if _fobMsg ~= "" then + local _crates = ctld.getCratesAndDistance(_heli) - if _txt ~= "" then - _txt = _txt .. "\n\n" - end + --sort + local _sort = function( a,b ) return a.dist < b.dist end + table.sort(_crates,_sort) - _txt = _txt .. ctld.i18n_translate("Nearby FOB Crates (Not Slingloadable):\n%1", _fobMsg) + for _, _crate in pairs(_crates) do + + if _crate.dist < 1000 and _crate.details.unit ~= "FOB" then + _message = ctld.i18n_translate("%1\n%2 crate - kg %3 - %4 m - %5 o'clock", _message, _crate.details.desc, _crate.details.weight, _crate.dist, ctld.getClockDirection(_heli, _crate.crateUnit)) + end end - end - ctld.displayMessageToGroup(_heli, _txt, 20) + + + local _fobMsg = "" + for _, _fobCrate in pairs(_crates) do + + if _fobCrate.dist < 1000 and _fobCrate.details.unit == "FOB" then + _fobMsg = _fobMsg .. ctld.i18n_translate("FOB Crate - %1 m - %2 o'clock\n", _fobCrate.dist, ctld.getClockDirection(_heli, _fobCrate.crateUnit)) + end + end + + local _txt = ctld.i18n_translate("No Nearby Crates") + if _message ~= "" or _fobMsg ~= "" then + + _txt = "" + + if _message ~= "" then + _txt = ctld.i18n_translate("Nearby Crates:\n%1", _message) + end + + if _fobMsg ~= "" then + + if _txt ~= "" then + _txt = _txt .. "\n\n" + end + + _txt = _txt .. ctld.i18n_translate("Nearby FOB Crates (Not Slingloadable):\n%1", _fobMsg) + end + end + ctld.displayMessageToGroup(_heli, _txt, 20) end function ctld.listFOBS(_args) - local _msg = ctld.i18n_translate("FOB Positions:") + local _msg = ctld.i18n_translate("FOB Positions:") - local _heli = ctld.getTransportUnit(_args[1]) + local _heli = ctld.getTransportUnit(_args[1]) - if _heli == nil then + if _heli == nil then - return -- no heli! - end - - -- get fob positions - local _fobs = ctld.getSpawnedFobs(_heli) - - if _fobs and #_fobs > 0 then - -- now check spawned fobs - for _, _fob in ipairs(_fobs) do - _msg = ctld.i18n_translate("%1\nFOB @ %2", _msg, ctld.getFOBPositionString(_fob)) + return -- no heli! end - else - _msg = ctld.i18n_translate("Sorry, there are no active FOBs!") - end - ctld.displayMessageToGroup(_heli, _msg, 20) + + -- get fob positions + local _fobs = ctld.getSpawnedFobs(_heli) + + if _fobs and #_fobs > 0 then + -- now check spawned fobs + for _, _fob in ipairs(_fobs) do + _msg = ctld.i18n_translate("%1\nFOB @ %2", _msg, ctld.getFOBPositionString(_fob)) + end + else + _msg = ctld.i18n_translate("Sorry, there are no active FOBs!") + end + ctld.displayMessageToGroup(_heli, _msg, 20) end function ctld.getFOBPositionString(_fob) - local _lat, _lon = coord.LOtoLL(_fob:getPosition().p) + local _lat, _lon = coord.LOtoLL(_fob:getPosition().p) - local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) + local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) - -- local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_fob:getPosition().p)), 5) + -- local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_fob:getPosition().p)), 5) - local _message = _latLngStr + local _message = _latLngStr - local _beaconInfo = ctld.fobBeacons[_fob:getName()] + local _beaconInfo = ctld.fobBeacons[_fob:getName()] - if _beaconInfo ~= nil then - _message = string.format("%s - %.2f KHz ", _message, _beaconInfo.vhf / 1000) - _message = string.format("%s - %.2f MHz ", _message, _beaconInfo.uhf / 1000000) - _message = string.format("%s - %.2f MHz ", _message, _beaconInfo.fm / 1000000) - end + if _beaconInfo ~= nil then + _message = string.format("%s - %.2f KHz ", _message, _beaconInfo.vhf / 1000) + _message = string.format("%s - %.2f MHz ", _message, _beaconInfo.uhf / 1000000) + _message = string.format("%s - %.2f MHz ", _message, _beaconInfo.fm / 1000000) + end - return _message + return _message end function ctld.displayMessageToGroup(_unit, _text, _time,_clear) - local _groupId = ctld.getGroupId(_unit) - if _groupId then - if _clear == true then - trigger.action.outTextForGroup(_groupId, _text, _time,_clear) - else - trigger.action.outTextForGroup(_groupId, _text, _time) + local _groupId = ctld.getGroupId(_unit) + if _groupId then + if _clear == true then + trigger.action.outTextForGroup(_groupId, _text, _time,_clear) + else + trigger.action.outTextForGroup(_groupId, _text, _time) + end end - end end function ctld.heightDiff(_unit) - local _point = _unit:getPoint() + local _point = _unit:getPoint() - -- env.info("heightunit " .. _point.y) - --env.info("heightland " .. land.getHeight({ x = _point.x, y = _point.z })) + -- env.info("heightunit " .. _point.y) + --env.info("heightland " .. land.getHeight({ x = _point.x, y = _point.z })) - return _point.y - land.getHeight({ x = _point.x, y = _point.z }) + return _point.y - land.getHeight({ x = _point.x, y = _point.z }) end --includes fob crates! function ctld.getCratesAndDistance(_heli) - local _crates = {} + local _crates = {} - local _allCrates - if _heli:getCoalition() == 1 then - _allCrates = ctld.spawnedCratesRED - else - _allCrates = ctld.spawnedCratesBLUE - end - - for _crateName, _details in pairs(_allCrates) do - - --get crate - local _crate = ctld.getCrateObject(_crateName) - - --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude - if _crate ~= nil and _crate:getLife() > 0 - and (ctld.inAir(_crate) == false) then - - local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) - - local _crateDetails = { crateUnit = _crate, dist = _dist, details = _details } - - table.insert(_crates, _crateDetails) + local _allCrates + if _heli:getCoalition() == 1 then + _allCrates = ctld.spawnedCratesRED + else + _allCrates = ctld.spawnedCratesBLUE end - end - local _fobCrates - if _heli:getCoalition() == 1 then - _fobCrates = ctld.droppedFOBCratesRED - else - _fobCrates = ctld.droppedFOBCratesBLUE - end + for _crateName, _details in pairs(_allCrates) do - for _crateName, _details in pairs(_fobCrates) do + --get crate + local _crate = ctld.getCrateObject(_crateName) - --get crate - local _crate = ctld.getCrateObject(_crateName) + --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude + if _crate ~= nil and _crate:getLife() > 0 + and (ctld.inAir(_crate) == false) then - if _crate ~= nil and _crate:getLife() > 0 then + local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) - local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) + local _crateDetails = { crateUnit = _crate, dist = _dist, details = _details } - local _crateDetails = { crateUnit = _crate, dist = _dist, details = { unit = "FOB" }, } - - table.insert(_crates, _crateDetails) + table.insert(_crates, _crateDetails) + end end - end - return _crates + local _fobCrates + if _heli:getCoalition() == 1 then + _fobCrates = ctld.droppedFOBCratesRED + else + _fobCrates = ctld.droppedFOBCratesBLUE + end + + for _crateName, _details in pairs(_fobCrates) do + + --get crate + local _crate = ctld.getCrateObject(_crateName) + + if _crate ~= nil and _crate:getLife() > 0 then + + local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) + + local _crateDetails = { crateUnit = _crate, dist = _dist, details = { unit = "FOB" }, } + + table.insert(_crates, _crateDetails) + end + end + + return _crates end function ctld.getClosestCrate(_heli, _crates, _type) - local _closetCrate = nil - local _shortestDistance = -1 - local _distance = 0 - local _minimumDistance = 5 -- prevents dynamic cargo crates from unpacking while in cargo hold + local _closetCrate = nil + local _shortestDistance = -1 + local _distance = 0 + local _minimumDistance = 5 -- prevents dynamic cargo crates from unpacking while in cargo hold - for _, _crate in pairs(_crates) do + for _, _crate in pairs(_crates) do - if (_crate.details.unit == _type or _type == nil) then - _distance = _crate.dist + if (_crate.details.unit == _type or _type == nil) then + _distance = _crate.dist - if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) and _distance > _minimumDistance then - _shortestDistance = _distance - _closetCrate = _crate - end + if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) and _distance > _minimumDistance then + _shortestDistance = _distance + _closetCrate = _crate + end + end end - end - return _closetCrate + return _closetCrate end function ctld.findNearestAASystem(_heli,_aaSystem) - local _closestHawkGroup = nil - local _shortestDistance = -1 - local _distance = 0 + local _closestHawkGroup = nil + local _shortestDistance = -1 + local _distance = 0 - for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do + for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do - local _hawkGroup = Group.getByName(_groupName) + local _hawkGroup = Group.getByName(_groupName) - -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) - if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() and _hawkDetails[1].system.name == _aaSystem.name then + -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) + if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() and _hawkDetails[1].system.name == _aaSystem.name then - local _units = _hawkGroup:getUnits() + local _units = _hawkGroup:getUnits() - for _, _leader in pairs(_units) do + for _, _leader in pairs(_units) do - if _leader ~= nil and _leader:getLife() > 0 then + if _leader ~= nil and _leader:getLife() > 0 then - _distance = ctld.getDistance(_leader:getPoint(), _heli:getPoint()) + _distance = ctld.getDistance(_leader:getPoint(), _heli:getPoint()) - if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then - _shortestDistance = _distance - _closestHawkGroup = _hawkGroup - end + if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then + _shortestDistance = _distance + _closestHawkGroup = _hawkGroup + end - break + break + end + end end - end end - end - if _closestHawkGroup ~= nil then + if _closestHawkGroup ~= nil then - return { group = _closestHawkGroup, dist = _shortestDistance } - end - return nil + return { group = _closestHawkGroup, dist = _shortestDistance } + end + return nil end function ctld.getCrateObject(_name) - local _crate + local _crate - if ctld.staticBugWorkaround then - _crate = Unit.getByName(_name) - else - _crate = StaticObject.getByName(_name) - end - return _crate + if ctld.staticBugWorkaround then + _crate = Unit.getByName(_name) + else + _crate = StaticObject.getByName(_name) + end + return _crate end function ctld.unpackCrates(_arguments) - local _status, _err = pcall(function(_args) + local _status, _err = pcall(function(_args) - local _heli = ctld.getTransportUnit(_args[1]) + local _heli = ctld.getTransportUnit(_args[1]) - if _heli ~= nil and ctld.inAir(_heli) == false then + if _heli ~= nil and ctld.inAir(_heli) == false then - local _crates = ctld.getCratesAndDistance(_heli) - local _crate = ctld.getClosestCrate(_heli, _crates) + local _crates = ctld.getCratesAndDistance(_heli) + local _crate = ctld.getClosestCrate(_heli, _crates) - 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.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) - return - end + return + end - if _crate ~= nil and _crate.dist < 750 - and (_crate.details.unit == "FOB" or _crate.details.unit == "FOB-SMALL") then + if _crate ~= nil and _crate.dist < 750 + and (_crate.details.unit == "FOB" or _crate.details.unit == "FOB-SMALL") then - ctld.unpackFOBCrates(_crates, _heli) + ctld.unpackFOBCrates(_crates, _heli) - return + return - elseif _crate ~= nil and _crate.dist < 200 then + elseif _crate ~= nil and _crate.dist < 200 then - if ctld.forceCrateToBeMoved and ctld.crateMove[_crate.crateUnit:getName()] and not ctld.unitDynamicCargoCapable(_heli) then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Sorry you must move this crate before you unpack it!"), 20) - return + if ctld.forceCrateToBeMoved and ctld.crateMove[_crate.crateUnit:getName()] and not ctld.unitDynamicCargoCapable(_heli) then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Sorry you must move this crate before you unpack it!"), 20) + return + end + + + local _aaTemplate = ctld.getAATemplate(_crate.details.unit) + + if _aaTemplate then + + if _crate.details.unit == _aaTemplate.repair then + ctld.repairAASystem(_heli, _crate,_aaTemplate) + else + ctld.unpackAASystem(_heli, _crate, _crates,_aaTemplate) + end + + return -- stop processing + -- is multi crate? + elseif _crate.details.cratesRequired ~= nil and _crate.details.cratesRequired > 1 then + -- multicrate + + ctld.unpackMultiCrate(_heli, _crate, _crates) + + return + + else + -- single crate + local _cratePoint = _crate.crateUnit:getPoint() + local _crateName = _crate.crateUnit:getName() + local _crateHdg = mist.getHeading(_crate.crateUnit, true) + + --remove crate + -- if ctld.slingLoad == false then + _crate.crateUnit:destroy() + -- end + + local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }, { _crateHdg }) + + if _heli:getCoalition() == 1 then + ctld.spawnedCratesRED[_crateName] = nil + else + ctld.spawnedCratesBLUE[_crateName] = nil + end + + ctld.processCallback({unit = _heli, crate = _crate , spawnedGroup = _spawnedGroups, action = "unpack"}) + + if _crate.details.unit == "1L13 EWR" then + ctld.addEWRTask(_spawnedGroups) + + -- env.info("Added EWR") + end + + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed %2 to the field", ctld.getPlayerNameOrType(_heli), _crate.details.desc), 10) + + if ctld.isJTACUnitType(_crate.details.unit) and ctld.JTAC_dropEnabled then + + local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) + --put to the end + table.insert(ctld.jtacGeneratedLaserCodes, _code) + + ctld.JTACStart(_spawnedGroups:getName(), _code) --(_jtacGroupName, _laserCode, _smoke, _lock, _colour) + end + end + + else + + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No friendly crates close enough to unpack, or crate too close to aircraft."), 20) + end end + end, _arguments) - - local _aaTemplate = ctld.getAATemplate(_crate.details.unit) - - if _aaTemplate then - - if _crate.details.unit == _aaTemplate.repair then - ctld.repairAASystem(_heli, _crate,_aaTemplate) - else - ctld.unpackAASystem(_heli, _crate, _crates,_aaTemplate) - end - - return -- stop processing - -- is multi crate? - elseif _crate.details.cratesRequired ~= nil and _crate.details.cratesRequired > 1 then - -- multicrate - - ctld.unpackMultiCrate(_heli, _crate, _crates) - - return - - else - -- single crate - local _cratePoint = _crate.crateUnit:getPoint() - local _crateName = _crate.crateUnit:getName() - local _crateHdg = mist.getHeading(_crate.crateUnit, true) - - --remove crate - -- if ctld.slingLoad == false then - _crate.crateUnit:destroy() - -- end - - local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }, { _crateHdg }) - - if _heli:getCoalition() == 1 then - ctld.spawnedCratesRED[_crateName] = nil - else - ctld.spawnedCratesBLUE[_crateName] = nil - end - - ctld.processCallback({unit = _heli, crate = _crate , spawnedGroup = _spawnedGroups, action = "unpack"}) - - if _crate.details.unit == "1L13 EWR" then - ctld.addEWRTask(_spawnedGroups) - - -- env.info("Added EWR") - end - - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed %2 to the field", ctld.getPlayerNameOrType(_heli), _crate.details.desc), 10) - - if ctld.isJTACUnitType(_crate.details.unit) and ctld.JTAC_dropEnabled then - - local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) - --put to the end - table.insert(ctld.jtacGeneratedLaserCodes, _code) - - ctld.JTACStart(_spawnedGroups:getName(), _code) --(_jtacGroupName, _laserCode, _smoke, _lock, _colour) - end - end - - else - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No friendly crates close enough to unpack, or crate too close to aircraft."), 20) - end + if (not _status) then + env.error(string.format("CTLD ERROR: %s", _err)) end - end, _arguments) - - if (not _status) then - env.error(string.format("CTLD ERROR: %s", _err)) - end end -- builds a fob! function ctld.unpackFOBCrates(_crates, _heli) - if ctld.inLogisticsZone(_heli) == true then + if ctld.inLogisticsZone(_heli) == true then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) - return - end - - -- unpack multi crate - local _nearbyMultiCrates = {} - - local _bigFobCrates = 0 - local _smallFobCrates = 0 - local _totalCrates = 0 - - for _, _nearbyCrate in pairs(_crates) do - - if _nearbyCrate.dist < 750 then - - if _nearbyCrate.details.unit == "FOB" then - _bigFobCrates = _bigFobCrates + 1 - table.insert(_nearbyMultiCrates, _nearbyCrate) - elseif _nearbyCrate.details.unit == "FOB-SMALL" then - _smallFobCrates = _smallFobCrates + 1 - table.insert(_nearbyMultiCrates, _nearbyCrate) - end - - --catch divide by 0 - if _smallFobCrates > 0 then - _totalCrates = _bigFobCrates + (_smallFobCrates/3.0) - else - _totalCrates = _bigFobCrates - end - - if _totalCrates >= ctld.cratesRequiredForFOB then - break - end - end - end - - --- check crate count - if _totalCrates >= ctld.cratesRequiredForFOB then - - -- destroy crates - - local _points = {} - - for _, _crate in pairs(_nearbyMultiCrates) do - - if _heli:getCoalition() == 1 then - ctld.droppedFOBCratesRED[_crate.crateUnit:getName()] = nil - ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil - else - ctld.droppedFOBCratesBLUE[_crate.crateUnit:getName()] = nil - ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil - end - - table.insert(_points, _crate.crateUnit:getPoint()) - - --destroy - _crate.crateUnit:destroy() + return end - local _centroid = ctld.getCentroid(_points) + -- unpack multi crate + local _nearbyMultiCrates = {} - timer.scheduleFunction(function(_args) + local _bigFobCrates = 0 + local _smallFobCrates = 0 + local _totalCrates = 0 - local _unitId = ctld.getNextUnitId() - local _name = "Deployed FOB #" .. _unitId + for _, _nearbyCrate in pairs(_crates) do - local _fob = ctld.spawnFOB(_args[2], _unitId, _args[1], _name) + if _nearbyCrate.dist < 750 then - --make it able to deploy crates - table.insert(ctld.logisticUnits, _fob:getName()) + if _nearbyCrate.details.unit == "FOB" then + _bigFobCrates = _bigFobCrates + 1 + table.insert(_nearbyMultiCrates, _nearbyCrate) + elseif _nearbyCrate.details.unit == "FOB-SMALL" then + _smallFobCrates = _smallFobCrates + 1 + table.insert(_nearbyMultiCrates, _nearbyCrate) + end - ctld.beaconCount = ctld.beaconCount + 1 + --catch divide by 0 + if _smallFobCrates > 0 then + _totalCrates = _bigFobCrates + (_smallFobCrates/3.0) + else + _totalCrates = _bigFobCrates + end - local _radioBeaconName = "FOB Beacon #" .. ctld.beaconCount + if _totalCrates >= ctld.cratesRequiredForFOB then + break + end + end + end - local _radioBeaconDetails = ctld.createRadioBeacon(_args[1], _args[3], _args[2], _radioBeaconName, nil, true) + --- check crate count + if _totalCrates >= ctld.cratesRequiredForFOB then - ctld.fobBeacons[_name] = { vhf = _radioBeaconDetails.vhf, uhf = _radioBeaconDetails.uhf, fm = _radioBeaconDetails.fm } + -- destroy crates - if ctld.troopPickupAtFOB == true then - table.insert(ctld.builtFOBS, _fob:getName()) + local _points = {} - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Finished building FOB! Crates and Troops can now be picked up."), 10) - else - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Finished building FOB! Crates can now be picked up."), 10) - end - end, { _centroid, _heli:getCountry(), _heli:getCoalition() }, timer.getTime() + ctld.buildTimeFOB) + for _, _crate in pairs(_nearbyMultiCrates) do - ctld.processCallback({unit = _heli, position = _centroid, action = "fob"}) + if _heli:getCoalition() == 1 then + ctld.droppedFOBCratesRED[_crate.crateUnit:getName()] = nil + ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil + else + ctld.droppedFOBCratesBLUE[_crate.crateUnit:getName()] = nil + ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil + end - trigger.action.smoke(_centroid, trigger.smokeColor.Green) + table.insert(_points, _crate.crateUnit:getPoint()) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke.", ctld.getPlayerNameOrType(_heli), _totalCrates, ctld.buildTimeFOB, 10)) - else - local _txt = ctld.i18n_translate("Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other", ctld.cratesRequiredForFOB, _totalCrates) - ctld.displayMessageToGroup(_heli, _txt, 20) - end + --destroy + _crate.crateUnit:destroy() + end + + local _centroid = ctld.getCentroid(_points) + + timer.scheduleFunction(function(_args) + + local _unitId = ctld.getNextUnitId() + local _name = "Deployed FOB #" .. _unitId + + local _fob = ctld.spawnFOB(_args[2], _unitId, _args[1], _name) + + --make it able to deploy crates + table.insert(ctld.logisticUnits, _fob:getName()) + + ctld.beaconCount = ctld.beaconCount + 1 + + local _radioBeaconName = "FOB Beacon #" .. ctld.beaconCount + + local _radioBeaconDetails = ctld.createRadioBeacon(_args[1], _args[3], _args[2], _radioBeaconName, nil, true) + + ctld.fobBeacons[_name] = { vhf = _radioBeaconDetails.vhf, uhf = _radioBeaconDetails.uhf, fm = _radioBeaconDetails.fm } + + if ctld.troopPickupAtFOB == true then + table.insert(ctld.builtFOBS, _fob:getName()) + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Finished building FOB! Crates and Troops can now be picked up."), 10) + else + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Finished building FOB! Crates can now be picked up."), 10) + end + end, { _centroid, _heli:getCountry(), _heli:getCoalition() }, timer.getTime() + ctld.buildTimeFOB) + + ctld.processCallback({unit = _heli, position = _centroid, action = "fob"}) + + trigger.action.smoke(_centroid, trigger.smokeColor.Green) + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke.", ctld.getPlayerNameOrType(_heli), _totalCrates, ctld.buildTimeFOB, 10)) + else + local _txt = ctld.i18n_translate("Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other", ctld.cratesRequiredForFOB, _totalCrates) + ctld.displayMessageToGroup(_heli, _txt, 20) + end end --unloads the sling crate when the helicopter is on the ground or between 4.5 - 10 meters function ctld.dropSlingCrate(_args) - local _unitName = _args[1] - local _heli = ctld.getTransportUnit(_unitName) - ctld.inTransitSlingLoadCrates[_unitName] = ctld.inTransitSlingLoadCrates[_unitName] or {} + local _unitName = _args[1] + local _heli = ctld.getTransportUnit(_unitName) + ctld.inTransitSlingLoadCrates[_unitName] = ctld.inTransitSlingLoadCrates[_unitName] or {} - if _heli == nil then - return -- no heli! - end - - local _currentCrate = ctld.inTransitSlingLoadCrates[_unitName][#ctld.inTransitSlingLoadCrates[_unitName]] - - if _currentCrate == nil then - if ctld.hoverPickup and ctld.loadCrateFromMenu then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands.", ctld.hoverTime), 10) - elseif ctld.hoverPickup then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate.", ctld.hoverTime), 10) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."), 10) + if _heli == nil then + return -- no heli! end - else - local _point = _heli:getPoint() - local _side = _heli:getCoalition() - local _hdg = mist.getHeading(_heli, true) - local _heightDiff = ctld.heightDiff(_heli) - if _heightDiff > 40.0 then - table.remove(ctld.inTransitSlingLoadCrates[_unitName],#ctld.inTransitSlingLoadCrates[_unitName]) - ctld.adaptWeightToCargo(_unitName) - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You were too high! The crate has been destroyed"), 10) - return - end - local _loadedCratesCopy = mist.utils.deepCopy(ctld.inTransitSlingLoadCrates[_unitName]) - ctld.logTrace("_loadedCratesCopy = %s", ctld.p(_loadedCratesCopy)) - for _, _crate in pairs(_loadedCratesCopy) do - ctld.logTrace("_crate = %s", ctld.p(_crate)) - ctld.logTrace("ctld.inAir(_heli) = %s", ctld.p(ctld.inAir(_heli))) - ctld.logTrace("_heightDiff = %s", ctld.p(_heightDiff)) - local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", _crate.desc, _unitId) - local _model_type = nil - if ctld.inAir(_heli) == false or _heightDiff <= 7.5 then - _point = ctld.getPointAt12Oclock(_heli, 30) - local _position = "12" - if ctld.unitDynamicCargoCapable(_heli) then - _model_type = "dynamic" - _point = ctld.getPointAt6Oclock(_heli, 15) - _position = "6" + local _currentCrate = ctld.inTransitSlingLoadCrates[_unitName][#ctld.inTransitSlingLoadCrates[_unitName]] + + if _currentCrate == nil then + if ctld.hoverPickup and ctld.loadCrateFromMenu then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands.", ctld.hoverTime), 10) + elseif ctld.hoverPickup then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate.", ctld.hoverTime), 10) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."), 10) end - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("%1 crate has been safely unhooked and is at your %2 o'clock", _crate.desc, _position), 10) - elseif _heightDiff > 7.5 and _heightDiff <= 40.0 then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("%1 crate has been safely dropped below you", _crate.desc), 10) - end - --remove crate from cargo - table.remove(ctld.inTransitSlingLoadCrates[_unitName],#ctld.inTransitSlingLoadCrates[_unitName]) - ctld.spawnCrateStatic(_heli:getCountry(), _unitId, _point, _name, _crate.weight, _side, _hdg, _model_type) + else + local _point = _heli:getPoint() + local _side = _heli:getCoalition() + local _hdg = mist.getHeading(_heli, true) + local _heightDiff = ctld.heightDiff(_heli) + + if _heightDiff > 40.0 then + table.remove(ctld.inTransitSlingLoadCrates[_unitName],#ctld.inTransitSlingLoadCrates[_unitName]) + ctld.adaptWeightToCargo(_unitName) + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You were too high! The crate has been destroyed"), 10) + return + end + local _loadedCratesCopy = mist.utils.deepCopy(ctld.inTransitSlingLoadCrates[_unitName]) + ctld.logTrace("_loadedCratesCopy = %s", ctld.p(_loadedCratesCopy)) + for _, _crate in pairs(_loadedCratesCopy) do + ctld.logTrace("_crate = %s", ctld.p(_crate)) + ctld.logTrace("ctld.inAir(_heli) = %s", ctld.p(ctld.inAir(_heli))) + ctld.logTrace("_heightDiff = %s", ctld.p(_heightDiff)) + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s #%i", _crate.desc, _unitId) + local _model_type = nil + if ctld.inAir(_heli) == false or _heightDiff <= 7.5 then + _point = ctld.getPointAt12Oclock(_heli, 30) + local _position = "12" + if ctld.unitDynamicCargoCapable(_heli) then + _model_type = "dynamic" + _point = ctld.getPointAt6Oclock(_heli, 15) + _position = "6" + end + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("%1 crate has been safely unhooked and is at your %2 o'clock", _crate.desc, _position), 10) + elseif _heightDiff > 7.5 and _heightDiff <= 40.0 then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("%1 crate has been safely dropped below you", _crate.desc), 10) + end + --remove crate from cargo + table.remove(ctld.inTransitSlingLoadCrates[_unitName],#ctld.inTransitSlingLoadCrates[_unitName]) + ctld.spawnCrateStatic(_heli:getCountry(), _unitId, _point, _name, _crate.weight, _side, _hdg, _model_type) + end + ctld.adaptWeightToCargo(_unitName) end - ctld.adaptWeightToCargo(_unitName) - end end --spawns a radio beacon made up of two units, @@ -4253,690 +4253,690 @@ end -- The units are set to to NOT engage function ctld.createRadioBeacon(_point, _coalition, _country, _name, _batteryTime, _isFOB) - local _freq = ctld.generateADFFrequencies() + local _freq = ctld.generateADFFrequencies() - --create timeout - local _battery + --create timeout + local _battery - if _batteryTime == nil then - _battery = timer.getTime() + (ctld.deployedBeaconBattery * 60) - else - _battery = timer.getTime() + (_batteryTime * 60) - end + if _batteryTime == nil then + _battery = timer.getTime() + (ctld.deployedBeaconBattery * 60) + else + _battery = timer.getTime() + (_batteryTime * 60) + end - local _lat, _lon = coord.LOtoLL(_point) + local _lat, _lon = coord.LOtoLL(_point) - local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) + local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) - --local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_point)), 5) + --local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_point)), 5) - local _freqsText = _name + local _freqsText = _name - if _isFOB then - -- _message = "FOB " .. _message - _battery = -1 --never run out of power! - end + if _isFOB then + -- _message = "FOB " .. _message + _battery = -1 --never run out of power! + end - _freqsText = _freqsText .. " - " .. _latLngStr + _freqsText = _freqsText .. " - " .. _latLngStr - _freqsText = string.format("%.2f kHz - %.2f / %.2f MHz", _freq.vhf / 1000, _freq.uhf / 1000000, _freq.fm / 1000000) + _freqsText = string.format("%.2f kHz - %.2f / %.2f MHz", _freq.vhf / 1000, _freq.uhf / 1000000, _freq.fm / 1000000) - local _uhfGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) - local _vhfGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) - local _fmGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) + local _uhfGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) + local _vhfGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) + local _fmGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) - local _beaconDetails = { - vhf = _freq.vhf, - vhfGroup = _vhfGroup:getName(), - uhf = _freq.uhf, - uhfGroup = _uhfGroup:getName(), - fm = _freq.fm, - fmGroup = _fmGroup:getName(), - text = _freqsText, - battery = _battery, - coalition = _coalition, - } + local _beaconDetails = { + vhf = _freq.vhf, + vhfGroup = _vhfGroup:getName(), + uhf = _freq.uhf, + uhfGroup = _uhfGroup:getName(), + fm = _freq.fm, + fmGroup = _fmGroup:getName(), + text = _freqsText, + battery = _battery, + coalition = _coalition, + } - ctld.updateRadioBeacon(_beaconDetails) + ctld.updateRadioBeacon(_beaconDetails) - table.insert(ctld.deployedRadioBeacons, _beaconDetails) + table.insert(ctld.deployedRadioBeacons, _beaconDetails) - return _beaconDetails + return _beaconDetails end function ctld.generateADFFrequencies() - if #ctld.freeUHFFrequencies <= 3 then - ctld.freeUHFFrequencies = ctld.usedUHFFrequencies - ctld.usedUHFFrequencies = {} - end + if #ctld.freeUHFFrequencies <= 3 then + ctld.freeUHFFrequencies = ctld.usedUHFFrequencies + ctld.usedUHFFrequencies = {} + end - --remove frequency at RANDOM - local _uhf = table.remove(ctld.freeUHFFrequencies, math.random(#ctld.freeUHFFrequencies)) - table.insert(ctld.usedUHFFrequencies, _uhf) + --remove frequency at RANDOM + local _uhf = table.remove(ctld.freeUHFFrequencies, math.random(#ctld.freeUHFFrequencies)) + table.insert(ctld.usedUHFFrequencies, _uhf) - if #ctld.freeVHFFrequencies <= 3 then - ctld.freeVHFFrequencies = ctld.usedVHFFrequencies - ctld.usedVHFFrequencies = {} - end + if #ctld.freeVHFFrequencies <= 3 then + ctld.freeVHFFrequencies = ctld.usedVHFFrequencies + ctld.usedVHFFrequencies = {} + end - local _vhf = table.remove(ctld.freeVHFFrequencies, math.random(#ctld.freeVHFFrequencies)) - table.insert(ctld.usedVHFFrequencies, _vhf) + local _vhf = table.remove(ctld.freeVHFFrequencies, math.random(#ctld.freeVHFFrequencies)) + table.insert(ctld.usedVHFFrequencies, _vhf) - if #ctld.freeFMFrequencies <= 3 then - ctld.freeFMFrequencies = ctld.usedFMFrequencies - ctld.usedFMFrequencies = {} - end + if #ctld.freeFMFrequencies <= 3 then + ctld.freeFMFrequencies = ctld.usedFMFrequencies + ctld.usedFMFrequencies = {} + end - local _fm = table.remove(ctld.freeFMFrequencies, math.random(#ctld.freeFMFrequencies)) - table.insert(ctld.usedFMFrequencies, _fm) + local _fm = table.remove(ctld.freeFMFrequencies, math.random(#ctld.freeFMFrequencies)) + table.insert(ctld.usedFMFrequencies, _fm) - return { uhf = _uhf, vhf = _vhf, fm = _fm } - --- return {uhf=_uhf,vhf=_vhf} + return { uhf = _uhf, vhf = _vhf, fm = _fm } + --- return {uhf=_uhf,vhf=_vhf} end function ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) - local _groupId = ctld.getNextGroupId() + local _groupId = ctld.getNextGroupId() - local _unitId = ctld.getNextUnitId() + local _unitId = ctld.getNextUnitId() - local _radioGroup = { - ["visible"] = false, - -- ["groupId"] = _groupId, - ["hidden"] = false, - ["units"] = { - [1] = { - ["y"] = _point.z, - ["type"] = "TACAN_beacon", - ["name"] = "Unit #" .. _unitId .. " - " .. _name .. " [" .. _freqsText .. "]", - -- ["unitId"] = _unitId, - ["heading"] = 0, - ["playerCanDrive"] = true, - ["skill"] = "Excellent", - ["x"] = _point.x, - } - }, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = "Group #" .. _groupId .. " - " .. _name, - ["task"] = {}, - --added two fields below for MIST - ["category"] = Group.Category.GROUND, - ["country"] = _country - } + local _radioGroup = { + ["visible"] = false, + -- ["groupId"] = _groupId, + ["hidden"] = false, + ["units"] = { + [1] = { + ["y"] = _point.z, + ["type"] = "TACAN_beacon", + ["name"] = "Unit #" .. _unitId .. " - " .. _name .. " [" .. _freqsText .. "]", + -- ["unitId"] = _unitId, + ["heading"] = 0, + ["playerCanDrive"] = true, + ["skill"] = "Excellent", + ["x"] = _point.x, + } + }, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = "Group #" .. _groupId .. " - " .. _name, + ["task"] = {}, + --added two fields below for MIST + ["category"] = Group.Category.GROUND, + ["country"] = _country + } - -- return coalition.addGroup(_country, Group.Category.GROUND, _radioGroup) - return Group.getByName(mist.dynAdd(_radioGroup).name) + -- return coalition.addGroup(_country, Group.Category.GROUND, _radioGroup) + return Group.getByName(mist.dynAdd(_radioGroup).name) end function ctld.updateRadioBeacon(_beaconDetails) - local _vhfGroup = Group.getByName(_beaconDetails.vhfGroup) + local _vhfGroup = Group.getByName(_beaconDetails.vhfGroup) - local _uhfGroup = Group.getByName(_beaconDetails.uhfGroup) + local _uhfGroup = Group.getByName(_beaconDetails.uhfGroup) - local _fmGroup = Group.getByName(_beaconDetails.fmGroup) + local _fmGroup = Group.getByName(_beaconDetails.fmGroup) - local _radioLoop = {} + local _radioLoop = {} - if _vhfGroup ~= nil and _vhfGroup:getUnits() ~= nil and #_vhfGroup:getUnits() == 1 then - table.insert(_radioLoop, { group = _vhfGroup, freq = _beaconDetails.vhf, silent = false, mode = 0 }) - end - - if _uhfGroup ~= nil and _uhfGroup:getUnits() ~= nil and #_uhfGroup:getUnits() == 1 then - table.insert(_radioLoop, { group = _uhfGroup, freq = _beaconDetails.uhf, silent = true, mode = 0 }) - end - - if _fmGroup ~= nil and _fmGroup:getUnits() ~= nil and #_fmGroup:getUnits() == 1 then - table.insert(_radioLoop, { group = _fmGroup, freq = _beaconDetails.fm, silent = false, mode = 1 }) - end - - local _batLife = _beaconDetails.battery - timer.getTime() - - if (_batLife <= 0 and _beaconDetails.battery ~= -1) or #_radioLoop ~= 3 then - -- ran out of batteries - if _vhfGroup ~= nil then - trigger.action.stopRadioTransmission(_vhfGroup:getName()) - _vhfGroup:destroy() - end - if _uhfGroup ~= nil then - trigger.action.stopRadioTransmission(_uhfGroup:getName()) - _uhfGroup:destroy() - end - if _fmGroup ~= nil then - trigger.action.stopRadioTransmission(_fmGroup:getName()) - _fmGroup:destroy() + if _vhfGroup ~= nil and _vhfGroup:getUnits() ~= nil and #_vhfGroup:getUnits() == 1 then + table.insert(_radioLoop, { group = _vhfGroup, freq = _beaconDetails.vhf, silent = false, mode = 0 }) end - return false - end - - --fobs have unlimited battery life - -- if _battery ~= -1 then - -- _text = _text.." "..mist.utils.round(_batLife).." seconds of battery" - -- end - - for _, _radio in pairs(_radioLoop) do - - local _groupController = _radio.group:getController() - - local _sound = ctld.radioSound - if _radio.silent then - _sound = ctld.radioSoundFC3 + if _uhfGroup ~= nil and _uhfGroup:getUnits() ~= nil and #_uhfGroup:getUnits() == 1 then + table.insert(_radioLoop, { group = _uhfGroup, freq = _beaconDetails.uhf, silent = true, mode = 0 }) end - _sound = "l10n/DEFAULT/".._sound + if _fmGroup ~= nil and _fmGroup:getUnits() ~= nil and #_fmGroup:getUnits() == 1 then + table.insert(_radioLoop, { group = _fmGroup, freq = _beaconDetails.fm, silent = false, mode = 1 }) + end - _groupController:setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD) + local _batLife = _beaconDetails.battery - timer.getTime() + + if (_batLife <= 0 and _beaconDetails.battery ~= -1) or #_radioLoop ~= 3 then + -- ran out of batteries + if _vhfGroup ~= nil then + trigger.action.stopRadioTransmission(_vhfGroup:getName()) + _vhfGroup:destroy() + end + if _uhfGroup ~= nil then + trigger.action.stopRadioTransmission(_uhfGroup:getName()) + _uhfGroup:destroy() + end + if _fmGroup ~= nil then + trigger.action.stopRadioTransmission(_fmGroup:getName()) + _fmGroup:destroy() + end + + return false + end + + --fobs have unlimited battery life + -- if _battery ~= -1 then + -- _text = _text.." "..mist.utils.round(_batLife).." seconds of battery" + -- end + + for _, _radio in pairs(_radioLoop) do + + local _groupController = _radio.group:getController() + + local _sound = ctld.radioSound + if _radio.silent then + _sound = ctld.radioSoundFC3 + end + + _sound = "l10n/DEFAULT/".._sound + + _groupController:setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD) - -- stop the transmission at each call to the ctld.updateRadioBeacon method (default each minute) - trigger.action.stopRadioTransmission(_radio.group:getName()) + -- stop the transmission at each call to the ctld.updateRadioBeacon method (default each minute) + trigger.action.stopRadioTransmission(_radio.group:getName()) - -- restart it as the battery is still up - -- the transmission is set to loop and has the name of the transmitting DCS group (that includes the type - i.e. FM, UHF, VHF) - trigger.action.radioTransmission(_sound, _radio.group:getUnit(1):getPoint(), _radio.mode, true, _radio.freq, 1000, _radio.group:getName()) - end + -- restart it as the battery is still up + -- the transmission is set to loop and has the name of the transmitting DCS group (that includes the type - i.e. FM, UHF, VHF) + trigger.action.radioTransmission(_sound, _radio.group:getUnit(1):getPoint(), _radio.mode, true, _radio.freq, 1000, _radio.group:getName()) + end - return true + return true end function ctld.listRadioBeacons(_args) - local _heli = ctld.getTransportUnit(_args[1]) - local _message = "" + local _heli = ctld.getTransportUnit(_args[1]) + local _message = "" - if _heli ~= nil then + if _heli ~= nil then - for _x, _details in pairs(ctld.deployedRadioBeacons) do + for _x, _details in pairs(ctld.deployedRadioBeacons) do - if _details.coalition == _heli:getCoalition() then - _message = _message .. _details.text .. "\n" - end + if _details.coalition == _heli:getCoalition() then + _message = _message .. _details.text .. "\n" + end + end + + if _message ~= "" then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Radio Beacons:\n%1", _message), 20) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No Active Radio Beacons"), 20) + end end - - if _message ~= "" then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Radio Beacons:\n%1", _message), 20) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No Active Radio Beacons"), 20) - end - end end function ctld.dropRadioBeacon(_args) - local _heli = ctld.getTransportUnit(_args[1]) - local _message = "" + local _heli = ctld.getTransportUnit(_args[1]) + local _message = "" - if _heli ~= nil and ctld.inAir(_heli) == false then + if _heli ~= nil and ctld.inAir(_heli) == false then - --deploy 50 m infront - --try to spawn at 12 oclock to us - local _point = ctld.getPointAt12Oclock(_heli, 50) + --deploy 50 m infront + --try to spawn at 12 oclock to us + local _point = ctld.getPointAt12Oclock(_heli, 50) - ctld.beaconCount = ctld.beaconCount + 1 - local _name = "Beacon #" .. ctld.beaconCount + ctld.beaconCount = ctld.beaconCount + 1 + local _name = "Beacon #" .. ctld.beaconCount - local _radioBeaconDetails = ctld.createRadioBeacon(_point, _heli:getCoalition(), _heli:getCountry(), _name, nil, false) + local _radioBeaconDetails = ctld.createRadioBeacon(_point, _heli:getCoalition(), _heli:getCountry(), _name, nil, false) - -- mark with flare? + -- mark with flare? - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 deployed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), _radioBeaconDetails.text, 20)) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 deployed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), _radioBeaconDetails.text, 20)) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before you can deploy a Radio Beacon!"), 20) - end + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before you can deploy a Radio Beacon!"), 20) + end end --remove closet radio beacon function ctld.removeRadioBeacon(_args) - local _heli = ctld.getTransportUnit(_args[1]) - local _message = "" + local _heli = ctld.getTransportUnit(_args[1]) + local _message = "" - if _heli ~= nil and ctld.inAir(_heli) == false then + if _heli ~= nil and ctld.inAir(_heli) == false then - -- mark with flare? + -- mark with flare? - local _closestBeacon = nil - local _shortestDistance = -1 - local _distance = 0 + local _closestBeacon = nil + local _shortestDistance = -1 + local _distance = 0 - for _x, _details in pairs(ctld.deployedRadioBeacons) do + for _x, _details in pairs(ctld.deployedRadioBeacons) do - if _details.coalition == _heli:getCoalition() then + if _details.coalition == _heli:getCoalition() then - local _group = Group.getByName(_details.vhfGroup) + local _group = Group.getByName(_details.vhfGroup) - if _group ~= nil and #_group:getUnits() == 1 then + if _group ~= nil and #_group:getUnits() == 1 then - _distance = ctld.getDistance(_heli:getPoint(), _group:getUnit(1):getPoint()) - if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then - _shortestDistance = _distance - _closestBeacon = _details - end + _distance = ctld.getDistance(_heli:getPoint(), _group:getUnit(1):getPoint()) + if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then + _shortestDistance = _distance + _closestBeacon = _details + end + end + end end - end - end - if _closestBeacon ~= nil and _shortestDistance then - local _vhfGroup = Group.getByName(_closestBeacon.vhfGroup) + if _closestBeacon ~= nil and _shortestDistance then + local _vhfGroup = Group.getByName(_closestBeacon.vhfGroup) - local _uhfGroup = Group.getByName(_closestBeacon.uhfGroup) + local _uhfGroup = Group.getByName(_closestBeacon.uhfGroup) - local _fmGroup = Group.getByName(_closestBeacon.fmGroup) + local _fmGroup = Group.getByName(_closestBeacon.fmGroup) - if _vhfGroup ~= nil then - trigger.action.stopRadioTransmission(_vhfGroup:getName()) - _vhfGroup:destroy() - end - if _uhfGroup ~= nil then - trigger.action.stopRadioTransmission(_uhfGroup:getName()) - _uhfGroup:destroy() - end - if _fmGroup ~= nil then - trigger.action.stopRadioTransmission(_fmGroup:getName()) - _fmGroup:destroy() - end + if _vhfGroup ~= nil then + trigger.action.stopRadioTransmission(_vhfGroup:getName()) + _vhfGroup:destroy() + end + if _uhfGroup ~= nil then + trigger.action.stopRadioTransmission(_uhfGroup:getName()) + _uhfGroup:destroy() + end + if _fmGroup ~= nil then + trigger.action.stopRadioTransmission(_fmGroup:getName()) + _fmGroup:destroy() + end + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 removed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), _closestBeacon.text, 20)) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No Radio Beacons within 500m."), 20) + end - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 removed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), _closestBeacon.text, 20)) else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No Radio Beacons within 500m."), 20) + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before remove a Radio Beacon"), 20) end - - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before remove a Radio Beacon"), 20) - end end -- gets the center of a bunch of points! -- return proper DCS point with height function ctld.getCentroid(_points) - local _tx, _ty = 0, 0 - for _index, _point in ipairs(_points) do - _tx = _tx + _point.x - _ty = _ty + _point.z - end + local _tx, _ty = 0, 0 + for _index, _point in ipairs(_points) do + _tx = _tx + _point.x + _ty = _ty + _point.z + end - local _npoints = #_points + local _npoints = #_points - local _point = { x = _tx / _npoints, z = _ty / _npoints } + local _point = { x = _tx / _npoints, z = _ty / _npoints } - _point.y = land.getHeight({ _point.x, _point.z }) + _point.y = land.getHeight({ _point.x, _point.z }) - return _point + return _point end function ctld.getAATemplate(_unitName) - for _,_system in pairs(ctld.AASystemTemplate) do + for _,_system in pairs(ctld.AASystemTemplate) do - if _system.repair == _unitName then - return _system + if _system.repair == _unitName then + return _system + end + + for _,_part in pairs(_system.parts) do + + if _unitName == _part.name then + return _system + end + end end - for _,_part in pairs(_system.parts) do - - if _unitName == _part.name then - return _system - end - end - end - - return nil + return nil end function ctld.getLauncherUnitFromAATemplate(_aaTemplate) - for _,_part in pairs(_aaTemplate.parts) do + for _,_part in pairs(_aaTemplate.parts) do - if _part.launcher then - return _part.name + if _part.launcher then + return _part.name + end end - end - return nil + return nil end function ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTemplate) - -- are we adding to existing aa system? - -- check to see if the crate is a launcher - if ctld.getLauncherUnitFromAATemplate(_aaSystemTemplate) == _nearestCrate.details.unit then + -- are we adding to existing aa system? + -- check to see if the crate is a launcher + if ctld.getLauncherUnitFromAATemplate(_aaSystemTemplate) == _nearestCrate.details.unit then - -- find nearest COMPLETE AA system - local _nearestSystem = ctld.findNearestAASystem(_heli, _aaSystemTemplate) + -- find nearest COMPLETE AA system + local _nearestSystem = ctld.findNearestAASystem(_heli, _aaSystemTemplate) - if _nearestSystem ~= nil and _nearestSystem.dist < 300 then + if _nearestSystem ~= nil and _nearestSystem.dist < 300 then - local _uniqueTypes = {} -- stores each unique part of system - local _types = {} - local _points = {} - local _hdgs = {} + local _uniqueTypes = {} -- stores each unique part of system + local _types = {} + local _points = {} + local _hdgs = {} - local _units = _nearestSystem.group:getUnits() + local _units = _nearestSystem.group:getUnits() - if _units ~= nil and #_units > 0 then + if _units ~= nil and #_units > 0 then - for x = 1, #_units do - if _units[x]:getLife() > 0 then + for x = 1, #_units do + if _units[x]:getLife() > 0 then - --this allows us to count each type once - _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() + --this allows us to count each type once + _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() - table.insert(_points, _units[x]:getPoint()) - table.insert(_types, _units[x]:getTypeName()) - table.insert(_hdgs, mist.getHeading(_units[x], true)) - end + table.insert(_points, _units[x]:getPoint()) + table.insert(_types, _units[x]:getTypeName()) + table.insert(_hdgs, mist.getHeading(_units[x], true)) + end + end + end + + -- do we have the correct number of unique pieces and do we have enough points for all the pieces + if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then + + -- rearm aa system + -- destroy old group + ctld.completeAASystems[_nearestSystem.group:getName()] = nil + + _nearestSystem.group:destroy() + + local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) + + ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, _aaSystemTemplate) + + ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "rearm"}) + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully rearmed a full %2 in the field", ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, 20)) + + if _heli:getCoalition() == 1 then + ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil + else + ctld.spawnedCratesBLUE[_nearestCrate.crateUnit:getName()] = nil + end + + -- remove crate + -- if ctld.slingLoad == false then + _nearestCrate.crateUnit:destroy() + -- end + + return true -- all done so quit + end end - end - - -- do we have the correct number of unique pieces and do we have enough points for all the pieces - if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then - - -- rearm aa system - -- destroy old group - ctld.completeAASystems[_nearestSystem.group:getName()] = nil - - _nearestSystem.group:destroy() - - local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) - - ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, _aaSystemTemplate) - - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "rearm"}) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully rearmed a full %2 in the field", ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, 20)) - - if _heli:getCoalition() == 1 then - ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil - else - ctld.spawnedCratesBLUE[_nearestCrate.crateUnit:getName()] = nil - end - - -- remove crate - -- if ctld.slingLoad == false then - _nearestCrate.crateUnit:destroy() - -- end - - return true -- all done so quit - end end - end - return false + return false end function ctld.getAASystemDetails(_hawkGroup,_aaSystemTemplate) - local _units = _hawkGroup:getUnits() + local _units = _hawkGroup:getUnits() - local _hawkDetails = {} + local _hawkDetails = {} - for _, _unit in pairs(_units) do - table.insert(_hawkDetails, { point = _unit:getPoint(), unit = _unit:getTypeName(), name = _unit:getName(), system =_aaSystemTemplate, hdg = mist.getHeading(_unit, true) }) - end + for _, _unit in pairs(_units) do + table.insert(_hawkDetails, { point = _unit:getPoint(), unit = _unit:getTypeName(), name = _unit:getName(), system =_aaSystemTemplate, hdg = mist.getHeading(_unit, true) }) + end - return _hawkDetails + return _hawkDetails end function ctld.countTableEntries(_table) - if _table == nil then - return 0 - end + if _table == nil then + return 0 + end - local _count = 0 + local _count = 0 - for _key, _value in pairs(_table) do + for _key, _value in pairs(_table) do - _count = _count + 1 - end + _count = _count + 1 + end - return _count + return _count end function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) - ctld.logTrace("_nearestCrate = %s", ctld.p(_nearestCrate)) - ctld.logTrace("_nearbyCrates = %s", ctld.p(_nearbyCrates)) - ctld.logTrace("_aaSystemTemplate = %s", ctld.p(_aaSystemTemplate)) + ctld.logTrace("_nearestCrate = %s", ctld.p(_nearestCrate)) + ctld.logTrace("_nearbyCrates = %s", ctld.p(_nearbyCrates)) + ctld.logTrace("_aaSystemTemplate = %s", ctld.p(_aaSystemTemplate)) - if ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) then - -- rearmed system - return - end - - local _systemParts = {} - - --initialise list of parts - for _,_part in pairs(_aaSystemTemplate.parts) do - 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 + if ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) then + -- rearmed system + return end - _systemParts[_part.name] = _systemPart - end - local _cratePositions = {} - local _crateHdg = {} + local _systemParts = {} - 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 - ctld.logTrace("_nearbyCrate = %s", ctld.p(_nearbyCrate)) - if _nearbyCrate.dist < crateDistance then - - local _name = _nearbyCrate.details.unit - ctld.logTrace("_name = %s", ctld.p(_name)) - - if _systemParts[_name] ~= nil then - - local foundCount = _systemParts[_name].found - ctld.logTrace("foundCount = %s", ctld.p(foundCount)) - - if not _cratePositions[_name] then - _cratePositions[_name] = {} + --initialise list of parts + for _,_part in pairs(_aaSystemTemplate.parts) do + 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 - if not _crateHdg[_name] then - _crateHdg[_name] = {} - end - - -- if this is our first time encountering this part of the system - if foundCount == 0 then - local _foundPart = _systemParts[_name] - - _foundPart.found = 1 - - -- 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 - ctld.logTrace("cratesRequired = %s", ctld.p(cratesRequired)) - if cratesRequired ~= nil then - _foundPart.required = cratesRequired - end - - _systemParts[_name] = _foundPart - 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 - if not _systemParts[_name].crates then - _systemParts[_name].crates = {} - end - table.insert(_systemParts[_name].crates, _nearbyCrate) - table.insert(_cratePositions[_name], crateUnit:getPoint()) - table.insert(_crateHdg[_name], mist.getHeading(crateUnit, true)) - end + _systemParts[_part.name] = _systemPart end - end - -- 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) + local _cratePositions = {} + local _crateHdg = {} - -- 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 crateDistance = 500 - local spawnDistance = 50 -- circle radius to spawn units in a circle and randomize position relative to the crate location - local arcRad = math.pi * 2 + -- 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 + ctld.logTrace("_nearbyCrate = %s", ctld.p(_nearbyCrate)) + if _nearbyCrate.dist < crateDistance then - local _txt = "" + local _name = _nearbyCrate.details.unit + ctld.logTrace("_name = %s", ctld.p(_name)) - local _posArray = {} - local _hdgArray = {} - local _typeArray = {} - -- for each part of the system parts - for _name, _systemPart in pairs(_systemParts) do + if _systemParts[_name] ~= nil then - -- check if enough crates were found to build the part - if _systemPart.found < _systemPart.required then - _txt = _txt..ctld.i18n_translate("Missing %1\n",_systemPart.desc) - 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 foundCount = _systemParts[_name].found + ctld.logTrace("foundCount = %s", ctld.p(foundCount)) - -- 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 + if not _cratePositions[_name] then + _cratePositions[_name] = {} + end + if not _crateHdg[_name] then + _crateHdg[_name] = {} + end - -- 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 + -- if this is our first time encountering this part of the system + if foundCount == 0 then + local _foundPart = _systemParts[_name] + + _foundPart.found = 1 + + -- 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 + ctld.logTrace("cratesRequired = %s", ctld.p(cratesRequired)) + if cratesRequired ~= nil then + _foundPart.required = cratesRequired + end + + _systemParts[_name] = _foundPart + 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 + if not _systemParts[_name].crates then + _systemParts[_name].crates = {} + end + table.insert(_systemParts[_name].crates, _nearbyCrate) + table.insert(_cratePositions[_name], crateUnit:getPoint()) + table.insert(_crateHdg[_name], mist.getHeading(crateUnit, true)) + 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 - - --handle multiple units per part by spawning them in a circle around the crate - if partAmount > 1 then - - local angular_step = arcRad / partAmount - - 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 - - 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) - end - else - table.insert(_posArray, _point) - table.insert(_hdgArray, _hdg) - table.insert(_typeArray, _name) - end end - end - local _activeLaunchers = ctld.countCompleteAASystems(_heli) + -- 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) - local _allowed = ctld.getAllowedAASystems(_heli) + -- 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 - env.info("Active: ".._activeLaunchers.." Allowed: ".._allowed) + local spawnDistance = 50 -- circle radius to spawn units in a circle and randomize position relative to the crate location + local arcRad = math.pi * 2 - if _activeLaunchers + 1 > _allowed then - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Out of parts for AA Systems. Current limit is %1\n", _allowed, 10)) - return - end + local _txt = "" - if _txt ~= "" then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Cannot build %1\n%2\n\nOr the crates are not close enough together", _aaSystemTemplate.name, _txt), 20) - return - else - - -- destroy crates + local _posArray = {} + local _hdgArray = {} + local _typeArray = {} + -- for each part of the system parts 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 - -- 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 + -- check if enough crates were found to build the part + if _systemPart.found < _systemPart.required then + _txt = _txt..ctld.i18n_translate("Missing %1\n",_systemPart.desc) + 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 - --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 + -- 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 + + -- 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 + + --handle multiple units per part by spawning them in a circle around the crate + if partAmount > 1 then + + local angular_step = arcRad / partAmount + + 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 + + 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) + end + else + table.insert(_posArray, _point) + table.insert(_hdgArray, _hdg) + table.insert(_typeArray, _name) + end end - end end - -- HAWK / BUK READY! - local _spawnedGroup = ctld.spawnCrateGroup(_heli, _posArray, _typeArray, _hdgArray) + local _activeLaunchers = ctld.countCompleteAASystems(_heli) - ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup,_aaSystemTemplate) + local _allowed = ctld.getAllowedAASystems(_heli) - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "unpack"}) + env.info("Active: ".._activeLaunchers.." Allowed: ".._allowed) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4", ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, _allowed, (_activeLaunchers+1)), 10) - end + if _activeLaunchers + 1 > _allowed then + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Out of parts for AA Systems. Current limit is %1\n", _allowed, 10)) + return + end + + if _txt ~= "" then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Cannot build %1\n%2\n\nOr the crates are not close enough together", _aaSystemTemplate.name, _txt), 20) + return + else + + -- 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 + + -- 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 + end + + -- HAWK / BUK READY! + local _spawnedGroup = ctld.spawnCrateGroup(_heli, _posArray, _typeArray, _hdgArray) + + ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup,_aaSystemTemplate) + + ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "unpack"}) + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4", ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, _allowed, (_activeLaunchers+1)), 10) + end end --count the number of captured cities, sets the amount of allowed AA Systems function ctld.getAllowedAASystems(_heli) - if _heli:getCoalition() == 1 then - return ctld.AASystemLimitBLUE - else - return ctld.AASystemLimitRED - end + if _heli:getCoalition() == 1 then + return ctld.AASystemLimitBLUE + else + return ctld.AASystemLimitRED + end end @@ -4944,220 +4944,220 @@ end function ctld.countCompleteAASystems(_heli) - local _count = 0 + local _count = 0 - for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do + for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do - local _hawkGroup = Group.getByName(_groupName) + local _hawkGroup = Group.getByName(_groupName) - -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) - if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() then + -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) + if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() then - local _units = _hawkGroup:getUnits() + local _units = _hawkGroup:getUnits() - if _units ~=nil and #_units > 0 then - --get the system template - local _aaSystemTemplate = _hawkDetails[1].system + if _units ~=nil and #_units > 0 then + --get the system template + local _aaSystemTemplate = _hawkDetails[1].system - local _uniqueTypes = {} -- stores each unique part of system - local _types = {} - local _points = {} + local _uniqueTypes = {} -- stores each unique part of system + local _types = {} + local _points = {} - if _units ~= nil and #_units > 0 then + if _units ~= nil and #_units > 0 then - for x = 1, #_units do - if _units[x]:getLife() > 0 then + for x = 1, #_units do + if _units[x]:getLife() > 0 then - --this allows us to count each type once - _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() + --this allows us to count each type once + _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() - table.insert(_points, _units[x]:getPoint()) - table.insert(_types, _units[x]:getTypeName()) + table.insert(_points, _units[x]:getPoint()) + table.insert(_types, _units[x]:getTypeName()) + end + end + end + + -- do we have the correct number of unique pieces and do we have enough points for all the pieces + if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then + _count = _count +1 + end end - end end - - -- do we have the correct number of unique pieces and do we have enough points for all the pieces - if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then - _count = _count +1 - end - end end - end - return _count + return _count end function ctld.repairAASystem(_heli, _nearestCrate,_aaSystem) - -- find nearest COMPLETE AA system - local _nearestHawk = ctld.findNearestAASystem(_heli,_aaSystem) + -- find nearest COMPLETE AA system + local _nearestHawk = ctld.findNearestAASystem(_heli,_aaSystem) - if _nearestHawk ~= nil and _nearestHawk.dist < 300 then + if _nearestHawk ~= nil and _nearestHawk.dist < 300 then - local _oldHawk = ctld.completeAASystems[_nearestHawk.group:getName()] + local _oldHawk = ctld.completeAASystems[_nearestHawk.group:getName()] - --spawn new one + --spawn new one - local _types = {} - local _hdgs = {} - local _points = {} + local _types = {} + local _hdgs = {} + local _points = {} - for _, _part in pairs(_oldHawk) do - table.insert(_points, _part.point) - table.insert(_hdgs, _part.hdg) - table.insert(_types, _part.unit) - end + for _, _part in pairs(_oldHawk) do + table.insert(_points, _part.point) + table.insert(_hdgs, _part.hdg) + table.insert(_types, _part.unit) + end - --remove old system - ctld.completeAASystems[_nearestHawk.group:getName()] = nil - _nearestHawk.group:destroy() + --remove old system + ctld.completeAASystems[_nearestHawk.group:getName()] = nil + _nearestHawk.group:destroy() - local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) + local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) - ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup,_aaSystem) + ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup,_aaSystem) - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "repair"}) + ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "repair"}) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully repaired a full %2 in the field.", ctld.getPlayerNameOrType(_heli), _aaSystem.name), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully repaired a full %2 in the field.", ctld.getPlayerNameOrType(_heli), _aaSystem.name), 10) + + if _heli:getCoalition() == 1 then + ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil + else + ctld.spawnedCratesBLUE[_nearestCrate.crateUnit:getName()] = nil + end + + -- remove crate + -- if ctld.slingLoad == false then + _nearestCrate.crateUnit:destroy() + -- end - if _heli:getCoalition() == 1 then - ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil else - ctld.spawnedCratesBLUE[_nearestCrate.crateUnit:getName()] = nil + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Cannot repair %1. No damaged %2 within 300m", _aaSystem.name, _aaSystem.name), 10) end - - -- remove crate - -- if ctld.slingLoad == false then - _nearestCrate.crateUnit:destroy() - -- end - - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Cannot repair %1. No damaged %2 within 300m", _aaSystem.name, _aaSystem.name), 10) - end end function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) - -- unpack multi crate - local _nearbyMultiCrates = {} + -- unpack multi crate + local _nearbyMultiCrates = {} - for _, _nearbyCrate in pairs(_nearbyCrates) do + for _, _nearbyCrate in pairs(_nearbyCrates) do - if _nearbyCrate.dist < 300 then + if _nearbyCrate.dist < 300 then - if _nearbyCrate.details.unit == _nearestCrate.details.unit then + if _nearbyCrate.details.unit == _nearestCrate.details.unit then - table.insert(_nearbyMultiCrates, _nearbyCrate) + table.insert(_nearbyMultiCrates, _nearbyCrate) - if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then - break + if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then + break + end + end end - end - end - end - - --- check crate count - if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then - - local _point = _nearestCrate.crateUnit:getPoint() - local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true) - - -- destroy crates - for _, _crate in pairs(_nearbyMultiCrates) do - - if _point == nil then - _point = _crate.crateUnit:getPoint() - end - - 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() - -- end end + --- check crate count + if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then - local _spawnedGroup = ctld.spawnCrateGroup(_heli, { _point }, { _nearestCrate.details.unit }, { _crateHdg }) - if _spawnedGroup == nil then - ctld.logError("ctld.unpackMultiCrate group was not spawned - skipping setGrpROE") + local _point = _nearestCrate.crateUnit:getPoint() + local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true) + + -- destroy crates + for _, _crate in pairs(_nearbyMultiCrates) do + + if _point == nil then + _point = _crate.crateUnit:getPoint() + end + + 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() + -- end + end + + + local _spawnedGroup = ctld.spawnCrateGroup(_heli, { _point }, { _nearestCrate.details.unit }, { _crateHdg }) + if _spawnedGroup == nil then + ctld.logError("ctld.unpackMultiCrate group was not spawned - skipping setGrpROE") + else + ctld.setGrpROE(_spawnedGroup) + ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "unpack"}) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed %2 to the field using %3 crates.", ctld.getPlayerNameOrType(_heli), _nearestCrate.details.desc, #_nearbyMultiCrates), 10) + end else - ctld.setGrpROE(_spawnedGroup) - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "unpack"}) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed %2 to the field using %3 crates.", ctld.getPlayerNameOrType(_heli), _nearestCrate.details.desc, #_nearbyMultiCrates), 10) + + local _txt = ctld.i18n_translate("Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other", _nearestCrate.details.desc, _nearestCrate.details.cratesRequired, #_nearbyMultiCrates) + + ctld.displayMessageToGroup(_heli, _txt, 20) end - else - - local _txt = ctld.i18n_translate("Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other", _nearestCrate.details.desc, _nearestCrate.details.cratesRequired, #_nearbyMultiCrates) - - ctld.displayMessageToGroup(_heli, _txt, 20) - end end function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) - local _id = ctld.getNextGroupId() - local _groupName = _types[1] .. " #" .. _id - local _side = _heli:getCoalition() - local _group = { - ["visible"] = false, - -- ["groupId"] = _id, - ["hidden"] = false, - ["units"] = {}, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = _groupName, - ["tasks"] = {}, - ["radioSet"] = false, - ["task"] = "Reconnaissance", - ["route"] = {}, - } + local _id = ctld.getNextGroupId() + local _groupName = _types[1] .. " #" .. _id + local _side = _heli:getCoalition() + local _group = { + ["visible"] = false, + -- ["groupId"] = _id, + ["hidden"] = false, + ["units"] = {}, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = _groupName, + ["tasks"] = {}, + ["radioSet"] = false, + ["task"] = "Reconnaissance", + ["route"] = {}, + } - local _hdg = 120 * math.pi / 180 -- radians = 120 degrees - if _types[1] ~= "MQ-9 Reaper" and _types[1] ~= "RQ-1A Predator" then -- non-drones - JTAC - local _spreadMin = 5 - local _spreadMax = 5 - local _spreadMult = 1 - 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 - _hdg = _hdgs[_i] - end + local _hdg = 120 * math.pi / 180 -- radians = 120 degrees + if _types[1] ~= "MQ-9 Reaper" and _types[1] ~= "RQ-1A Predator" then -- non-drones - JTAC + local _spreadMin = 5 + local _spreadMax = 5 + local _spreadMult = 1 + 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 + _hdg = _hdgs[_i] + end - _group.units[_i] = ctld.createUnit( _pos.x +math.random(_spreadMin,_spreadMax)*_spreadMult, - _pos.z +math.random(_spreadMin,_spreadMax)*_spreadMult, - _hdg, - _details) - end - _group.category = Group.Category.GROUND - else -- drones - JTAC - local _unitId = ctld.getNextUnitId() - local _details = { type = _types[1], - unitId = _unitId, - name = string.format("Unpacked %s #%i", _types[1], _unitId), - livery_id = "'camo' scheme", - skill = "High", - speed = 80, - payload = { pylons = {}, fuel = 1300, flare = 0, chaff = 0, gun = 100 } } - - _group.units[1] = ctld.createUnit( _positions[1].x, - _positions[1].z + ctld.jtacDroneRadius, - _hdg, - _details) - - _group.category = Group.Category.AIRPLANE -- for drones + _group.units[_i] = ctld.createUnit( _pos.x +math.random(_spreadMin,_spreadMax)*_spreadMult, + _pos.z +math.random(_spreadMin,_spreadMax)*_spreadMult, + _hdg, + _details) + end + _group.category = Group.Category.GROUND + else -- drones - JTAC + local _unitId = ctld.getNextUnitId() + local _details = { type = _types[1], + unitId = _unitId, + name = string.format("Unpacked %s #%i", _types[1], _unitId), + livery_id = "'camo' scheme", + skill = "High", + speed = 80, + payload = { pylons = {}, fuel = 1300, flare = 0, chaff = 0, gun = 100 } } + + _group.units[1] = ctld.createUnit( _positions[1].x, + _positions[1].z + ctld.jtacDroneRadius, + _hdg, + _details) + + _group.category = Group.Category.AIRPLANE -- for drones - -- create drone orbiting route + -- create drone orbiting route local DroneRoute = { ["points"] = { @@ -5206,8 +5206,8 @@ function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) ["params"] = { ["altitude"] = ctld.jtacDroneAltitude, - ["pattern"] = "Circle", - ["speed"] = 80, + ["pattern"] = "Circle", + ["speed"] = 80, }, -- end of ["params"] }, -- end of [2] [3] = @@ -5243,12 +5243,12 @@ function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) }, -- end of ["points"] } -- end of ["route"] --------------------------------------------------------------------------------- - _group.route = DroneRoute - end - - _group.country = _heli:getCountry() + _group.route = DroneRoute + end + + _group.country = _heli:getCountry() local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) - return _spawnedGroup + return _spawnedGroup end @@ -5256,630 +5256,630 @@ end -- spawn normal group function ctld.spawnDroppedGroup(_point, _details, _spawnBehind, _maxSearch) - local _groupName = _details.groupName + local _groupName = _details.groupName - local _group = { - ["visible"] = false, - -- ["groupId"] = _details.groupId, - ["hidden"] = false, - ["units"] = {}, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = _groupName, - ["task"] = {}, - } + local _group = { + ["visible"] = false, + -- ["groupId"] = _details.groupId, + ["hidden"] = false, + ["units"] = {}, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = _groupName, + ["task"] = {}, + } - if _spawnBehind == false then + if _spawnBehind == false then - -- spawn in circle around heli + -- spawn in circle around heli - local _pos = _point + local _pos = _point - for _i, _detail in ipairs(_details.units) do + for _i, _detail in ipairs(_details.units) do - local _angle = math.pi * 2 * (_i - 1) / #_details.units - local _xOffset = math.cos(_angle) * 30 - local _yOffset = math.sin(_angle) * 30 + local _angle = math.pi * 2 * (_i - 1) / #_details.units + local _xOffset = math.cos(_angle) * 30 + local _yOffset = math.sin(_angle) * 30 - _group.units[_i] = ctld.createUnit(_pos.x + _xOffset, _pos.z + _yOffset, _angle, _detail) + _group.units[_i] = ctld.createUnit(_pos.x + _xOffset, _pos.z + _yOffset, _angle, _detail) + end + + else + + local _pos = _point + + --try to spawn at 6 oclock to us + local _angle = math.atan2(_pos.z, _pos.x) + local _xOffset = math.cos(_angle) * -30 + local _yOffset = math.sin(_angle) * -30 + + + for _i, _detail in ipairs(_details.units) do + _group.units[_i] = ctld.createUnit(_pos.x + (_xOffset + 10 * _i), _pos.z + (_yOffset + 10 * _i), _angle, _detail) + end end - else + --switch to MIST + _group.category = Group.Category.GROUND; + _group.country = _details.country; - local _pos = _point + local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) - --try to spawn at 6 oclock to us - local _angle = math.atan2(_pos.z, _pos.x) - local _xOffset = math.cos(_angle) * -30 - local _yOffset = math.sin(_angle) * -30 + --local _spawnedGroup = coalition.addGroup(_details.country, Group.Category.GROUND, _group) - for _i, _detail in ipairs(_details.units) do - _group.units[_i] = ctld.createUnit(_pos.x + (_xOffset + 10 * _i), _pos.z + (_yOffset + 10 * _i), _angle, _detail) + -- find nearest enemy and head there + if _maxSearch == nil then + _maxSearch = ctld.maximumSearchDistance end - end - --switch to MIST - _group.category = Group.Category.GROUND; - _group.country = _details.country; + local _wpZone = ctld.inWaypointZone(_point,_spawnedGroup:getCoalition()) - local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) + if _wpZone.inZone then + ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _wpZone.point) + env.info("Heading to waypoint - In Zone ".._wpZone.name) + else + local _enemyPos = ctld.findNearestEnemy(_details.side, _point, _maxSearch) - --local _spawnedGroup = coalition.addGroup(_details.country, Group.Category.GROUND, _group) + ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _enemyPos) + end - - -- find nearest enemy and head there - if _maxSearch == nil then - _maxSearch = ctld.maximumSearchDistance - end - - local _wpZone = ctld.inWaypointZone(_point,_spawnedGroup:getCoalition()) - - if _wpZone.inZone then - ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _wpZone.point) - env.info("Heading to waypoint - In Zone ".._wpZone.name) - else - local _enemyPos = ctld.findNearestEnemy(_details.side, _point, _maxSearch) - - ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _enemyPos) - end - - return _spawnedGroup + return _spawnedGroup end function ctld.findNearestEnemy(_side, _point, _searchDistance) - local _closestEnemy = nil + local _closestEnemy = nil - local _groups + local _groups - local _closestEnemyDist = _searchDistance + local _closestEnemyDist = _searchDistance - local _heliPoint = _point + local _heliPoint = _point - if _side == 2 then - _groups = coalition.getGroups(1, Group.Category.GROUND) - else - _groups = coalition.getGroups(2, Group.Category.GROUND) - end - - for _, _group in pairs(_groups) do - - if _group ~= nil then - local _units = _group:getUnits() - - if _units ~= nil and #_units > 0 then - - local _leader = nil - - -- find alive leader - for x = 1, #_units do - if _units[x]:getLife() > 0 then - _leader = _units[x] - break - end - end - - if _leader ~= nil then - local _leaderPos = _leader:getPoint() - local _dist = ctld.getDistance(_heliPoint, _leaderPos) - if _dist < _closestEnemyDist then - _closestEnemyDist = _dist - _closestEnemy = _leaderPos - end - end - end + if _side == 2 then + _groups = coalition.getGroups(1, Group.Category.GROUND) + else + _groups = coalition.getGroups(2, Group.Category.GROUND) + end + + for _, _group in pairs(_groups) do + + if _group ~= nil then + local _units = _group:getUnits() + + if _units ~= nil and #_units > 0 then + + local _leader = nil + + -- find alive leader + for x = 1, #_units do + if _units[x]:getLife() > 0 then + _leader = _units[x] + break + end + end + + if _leader ~= nil then + local _leaderPos = _leader:getPoint() + local _dist = ctld.getDistance(_heliPoint, _leaderPos) + if _dist < _closestEnemyDist then + _closestEnemyDist = _dist + _closestEnemy = _leaderPos + end + end + end + end end - end - -- no enemy - move to random point - if _closestEnemy ~= nil then + -- no enemy - move to random point + if _closestEnemy ~= nil then - -- env.info("found enemy") - return _closestEnemy - else + -- env.info("found enemy") + return _closestEnemy + else - local _x = _heliPoint.x + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) - local _z = _heliPoint.z + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) - local _y = _heliPoint.y + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) + local _x = _heliPoint.x + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) + local _z = _heliPoint.z + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) + local _y = _heliPoint.y + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) - return { x = _x, z = _z,y=_y } - end + return { x = _x, z = _z,y=_y } + end end function ctld.findNearestGroup(_heli, _groups) - local _closestGroupDetails = {} - local _closestGroup = nil + local _closestGroupDetails = {} + local _closestGroup = nil - local _closestGroupDist = ctld.maxExtractDistance + local _closestGroupDist = ctld.maxExtractDistance - local _heliPoint = _heli:getPoint() + local _heliPoint = _heli:getPoint() - for _, _groupName in pairs(_groups) do + for _, _groupName in pairs(_groups) do - local _group = Group.getByName(_groupName) + local _group = Group.getByName(_groupName) - if _group ~= nil then - local _units = _group:getUnits() + if _group ~= nil then + local _units = _group:getUnits() - if _units ~= nil and #_units > 0 then + if _units ~= nil and #_units > 0 then - local _leader = nil + local _leader = nil - local _groupDetails = { groupId = _group:getID(), groupName = _group:getName(), side = _group:getCoalition(), units = {} } + local _groupDetails = { groupId = _group:getID(), groupName = _group:getName(), side = _group:getCoalition(), units = {} } - -- find alive leader - for x = 1, #_units do - if _units[x]:getLife() > 0 then + -- find alive leader + for x = 1, #_units do + if _units[x]:getLife() > 0 then - if _leader == nil then - _leader = _units[x] - -- set country based on leader - _groupDetails.country = _leader:getCountry() + if _leader == nil then + _leader = _units[x] + -- set country based on leader + _groupDetails.country = _leader:getCountry() + end + + local _unitDetails = { type = _units[x]:getTypeName(), unitId = _units[x]:getID(), name = _units[x]:getName() } + + table.insert(_groupDetails.units, _unitDetails) + end + end + + if _leader ~= nil then + local _leaderPos = _leader:getPoint() + local _dist = ctld.getDistance(_heliPoint, _leaderPos) + if _dist < _closestGroupDist then + _closestGroupDist = _dist + _closestGroupDetails = _groupDetails + _closestGroup = _group + end + end end - - local _unitDetails = { type = _units[x]:getTypeName(), unitId = _units[x]:getID(), name = _units[x]:getName() } - - table.insert(_groupDetails.units, _unitDetails) - end end - - if _leader ~= nil then - local _leaderPos = _leader:getPoint() - local _dist = ctld.getDistance(_heliPoint, _leaderPos) - if _dist < _closestGroupDist then - _closestGroupDist = _dist - _closestGroupDetails = _groupDetails - _closestGroup = _group - end - end - end end - end - if _closestGroup ~= nil then + if _closestGroup ~= nil then - return { group = _closestGroup, details = _closestGroupDetails } - else + return { group = _closestGroup, details = _closestGroupDetails } + else - return nil - end + return nil + end end function ctld.createUnit(_x, _y, _angle, _details) - local _newUnit = { - ["y"] = _y, - ["type"] = _details.type, - ["name"] = _details.name, - -- ["unitId"] = _details.unitId, - ["heading"] = _angle, - ["playerCanDrive"] = true, - ["skill"] = "Excellent", - ["x"] = _x, - } + local _newUnit = { + ["y"] = _y, + ["type"] = _details.type, + ["name"] = _details.name, + -- ["unitId"] = _details.unitId, + ["heading"] = _angle, + ["playerCanDrive"] = true, + ["skill"] = "Excellent", + ["x"] = _x, + } - return _newUnit + return _newUnit end function ctld.addEWRTask(_group) - -- delayed 2 second to work around bug - timer.scheduleFunction(function(_ewrGroup) - local _grp = ctld.getAliveGroup(_ewrGroup) + -- delayed 2 second to work around bug + timer.scheduleFunction(function(_ewrGroup) + local _grp = ctld.getAliveGroup(_ewrGroup) - if _grp ~= nil then - local _controller = _grp:getController(); - local _EWR = { - id = 'EWR', - auto = true, - params = { - } - } - _controller:setTask(_EWR) + if _grp ~= nil then + local _controller = _grp:getController(); + local _EWR = { + id = 'EWR', + auto = true, + params = { + } + } + _controller:setTask(_EWR) + end end - end - , _group:getName(), timer.getTime() + 2) + , _group:getName(), timer.getTime() + 2) end function ctld.orderGroupToMoveToPoint(_leader, _destination) - local _group = _leader:getGroup() + local _group = _leader:getGroup() - local _path = {} - table.insert(_path, mist.ground.buildWP(_leader:getPoint(), 'Off Road', 50)) - table.insert(_path, mist.ground.buildWP(_destination, 'Off Road', 50)) + local _path = {} + table.insert(_path, mist.ground.buildWP(_leader:getPoint(), 'Off Road', 50)) + table.insert(_path, mist.ground.buildWP(_destination, 'Off Road', 50)) - local _mission = { - id = 'Mission', - params = { - route = { - points =_path - }, - }, - } + local _mission = { + id = 'Mission', + params = { + route = { + points =_path + }, + }, + } - -- delayed 2 second to work around bug - timer.scheduleFunction(function(_arg) - local _grp = ctld.getAliveGroup(_arg[1]) + -- delayed 2 second to work around bug + timer.scheduleFunction(function(_arg) + local _grp = ctld.getAliveGroup(_arg[1]) - if _grp ~= nil then - local _controller = _grp:getController(); - Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) - Controller.setOption(_controller, AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE) - _controller:setTask(_arg[2]) + if _grp ~= nil then + local _controller = _grp:getController(); + Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) + Controller.setOption(_controller, AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE) + _controller:setTask(_arg[2]) + end end - end - , {_group:getName(), _mission}, timer.getTime() + 2) + , {_group:getName(), _mission}, timer.getTime() + 2) end -- are we in pickup zone function ctld.inPickupZone(_heli) - if ctld.inAir(_heli) then - return { inZone = false, limit = -1, index = -1 } - end - - local _heliPoint = _heli:getPoint() - - for _i, _zoneDetails in pairs(ctld.pickupZones) do - - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_zoneDetails[1]) - - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point - _triggerZone.radius = 200 -- should be big enough for ship - end - + if ctld.inAir(_heli) then + return { inZone = false, limit = -1, index = -1 } end - if _triggerZone ~= nil then + local _heliPoint = _heli:getPoint() - --get distance to center + for _i, _zoneDetails in pairs(ctld.pickupZones) do + + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) + + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_zoneDetails[1]) + + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point + _triggerZone.radius = 200 -- should be big enough for ship + end - local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) - if _dist <= _triggerZone.radius then - local _heliCoalition = _heli:getCoalition() - if _zoneDetails[4] == 1 and (_zoneDetails[5] == _heliCoalition or _zoneDetails[5] == 0) then - return { inZone = true, limit = _zoneDetails[3], index = _i } end - end + + if _triggerZone ~= nil then + + --get distance to center + + local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) + if _dist <= _triggerZone.radius then + local _heliCoalition = _heli:getCoalition() + if _zoneDetails[4] == 1 and (_zoneDetails[5] == _heliCoalition or _zoneDetails[5] == 0) then + return { inZone = true, limit = _zoneDetails[3], index = _i } + end + end + end end - end - local _fobs = ctld.getSpawnedFobs(_heli) + local _fobs = ctld.getSpawnedFobs(_heli) - -- now check spawned fobs - for _, _fob in ipairs(_fobs) do + -- now check spawned fobs + for _, _fob in ipairs(_fobs) do - --get distance to center + --get distance to center - local _dist = ctld.getDistance(_heliPoint, _fob:getPoint()) + local _dist = ctld.getDistance(_heliPoint, _fob:getPoint()) - if _dist <= 150 then - return { inZone = true, limit = 10000, index = -1 }; + if _dist <= 150 then + return { inZone = true, limit = 10000, index = -1 }; + end end - end - return { inZone = false, limit = -1, index = -1 }; + return { inZone = false, limit = -1, index = -1 }; end function ctld.getSpawnedFobs(_heli) - local _fobs = {} + local _fobs = {} - for _, _fobName in ipairs(ctld.builtFOBS) do + for _, _fobName in ipairs(ctld.builtFOBS) do - local _fob = StaticObject.getByName(_fobName) + local _fob = StaticObject.getByName(_fobName) - if _fob ~= nil and _fob:isExist() and _fob:getCoalition() == _heli:getCoalition() and _fob:getLife() > 0 then + if _fob ~= nil and _fob:isExist() and _fob:getCoalition() == _heli:getCoalition() and _fob:getLife() > 0 then - table.insert(_fobs, _fob) + table.insert(_fobs, _fob) + end end - end - return _fobs + return _fobs end -- are we in a dropoff zone function ctld.inDropoffZone(_heli) - if ctld.inAir(_heli) then - return false - end - - local _heliPoint = _heli:getPoint() - - for _, _zoneDetails in pairs(ctld.dropOffZones) do - - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - - if _triggerZone ~= nil and (_zoneDetails[3] == _heli:getCoalition() or _zoneDetails[3]== 0) then - - --get distance to center - - local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) - - if _dist <= _triggerZone.radius then - return true - end + if ctld.inAir(_heli) then + return false end - end - return false + local _heliPoint = _heli:getPoint() + + for _, _zoneDetails in pairs(ctld.dropOffZones) do + + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) + + if _triggerZone ~= nil and (_zoneDetails[3] == _heli:getCoalition() or _zoneDetails[3]== 0) then + + --get distance to center + + local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) + + if _dist <= _triggerZone.radius then + return true + end + end + end + + return false end -- are we in a waypoint zone function ctld.inWaypointZone(_point,_coalition) - for _, _zoneDetails in pairs(ctld.wpZones) do + for _, _zoneDetails in pairs(ctld.wpZones) do - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - --right coalition and active? - if _triggerZone ~= nil and (_zoneDetails[4] == _coalition or _zoneDetails[4]== 0) and _zoneDetails[3] == 1 then + --right coalition and active? + if _triggerZone ~= nil and (_zoneDetails[4] == _coalition or _zoneDetails[4]== 0) and _zoneDetails[3] == 1 then - --get distance to center + --get distance to center - local _dist = ctld.getDistance(_point, _triggerZone.point) + local _dist = ctld.getDistance(_point, _triggerZone.point) - if _dist <= _triggerZone.radius then - return {inZone = true, point = _triggerZone.point, name = _zoneDetails[1]} - end + if _dist <= _triggerZone.radius then + return {inZone = true, point = _triggerZone.point, name = _zoneDetails[1]} + end + end end - end - return {inZone = false} + return {inZone = false} end -- are we near friendly logistics zone function ctld.inLogisticsZone(_heli) - ctld.logDebug("ctld.inLogisticsZone(), _heli = %s", ctld.p(_heli)) + ctld.logDebug("ctld.inLogisticsZone(), _heli = %s", ctld.p(_heli)) + + if ctld.inAir(_heli) then + return false + end + local _heliPoint = _heli:getPoint() + ctld.logDebug("_heliPoint = %s", ctld.p(_heliPoint)) + for _, _name in pairs(ctld.logisticUnits) do + ctld.logDebug("_name = %s", ctld.p(_name)) + local _logistic = StaticObject.getByName(_name) + if not _logistic then + _logistic = Unit.getByName(_name) + end + ctld.logDebug("_logistic = %s", ctld.p(_logistic)) + if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() and _logistic:getLife() > 0 then + --get distance + local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) + ctld.logDebug("_dist = %s", ctld.p(_dist)) + if _dist <= ctld.maximumDistanceLogistic then + return true + end + end + end - if ctld.inAir(_heli) then return false - end - local _heliPoint = _heli:getPoint() - ctld.logDebug("_heliPoint = %s", ctld.p(_heliPoint)) - for _, _name in pairs(ctld.logisticUnits) do - ctld.logDebug("_name = %s", ctld.p(_name)) - local _logistic = StaticObject.getByName(_name) - if not _logistic then - _logistic = Unit.getByName(_name) - end - ctld.logDebug("_logistic = %s", ctld.p(_logistic)) - if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() and _logistic:getLife() > 0 then - --get distance - local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) - ctld.logDebug("_dist = %s", ctld.p(_dist)) - if _dist <= ctld.maximumDistanceLogistic then - return true - end - end - end - - return false end -- are far enough from a friendly logistics zone function ctld.farEnoughFromLogisticZone(_heli) - if ctld.inAir(_heli) then - return false - end - - local _heliPoint = _heli:getPoint() - - local _farEnough = true - - for _, _name in pairs(ctld.logisticUnits) do - - local _logistic = StaticObject.getByName(_name) - - if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() then - - --get distance - local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) - -- env.info("DIST ".._dist) - if _dist <= ctld.minimumDeployDistance then - -- env.info("TOO CLOSE ".._dist) - _farEnough = false - end + if ctld.inAir(_heli) then + return false end - end - return _farEnough + local _heliPoint = _heli:getPoint() + + local _farEnough = true + + for _, _name in pairs(ctld.logisticUnits) do + + local _logistic = StaticObject.getByName(_name) + + if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() then + + --get distance + local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) + -- env.info("DIST ".._dist) + if _dist <= ctld.minimumDeployDistance then + -- env.info("TOO CLOSE ".._dist) + _farEnough = false + end + end + end + + return _farEnough end function ctld.refreshSmoke() - if ctld.disableAllSmoke == true then - return - end + if ctld.disableAllSmoke == true then + return + end - for _, _zoneGroup in pairs({ ctld.pickupZones, ctld.dropOffZones }) do + for _, _zoneGroup in pairs({ ctld.pickupZones, ctld.dropOffZones }) do - for _, _zoneDetails in pairs(_zoneGroup) do + for _, _zoneDetails in pairs(_zoneGroup) do - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_triggerZone) + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_triggerZone) - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point + end + + end + + + --only trigger if smoke is on AND zone is active + if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[4] == 1 then + + -- Trigger smoke markers + + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + + trigger.action.smoke(_pos3, _zoneDetails[2]) + end end - - end - - - --only trigger if smoke is on AND zone is active - if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[4] == 1 then - - -- Trigger smoke markers - - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - - trigger.action.smoke(_pos3, _zoneDetails[2]) - end end - end - --waypoint zones - for _, _zoneDetails in pairs(ctld.wpZones) do + --waypoint zones + for _, _zoneDetails in pairs(ctld.wpZones) do - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - --only trigger if smoke is on AND zone is active - if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[3] == 1 then + --only trigger if smoke is on AND zone is active + if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[3] == 1 then - -- Trigger smoke markers + -- Trigger smoke markers - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - trigger.action.smoke(_pos3, _zoneDetails[2]) + trigger.action.smoke(_pos3, _zoneDetails[2]) + end end - end - --refresh in 5 minutes - timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 300) + --refresh in 5 minutes + timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 300) end function ctld.dropSmoke(_args) - local _heli = ctld.getTransportUnit(_args[1]) + local _heli = ctld.getTransportUnit(_args[1]) - if _heli ~= nil then + if _heli ~= nil then - local _colour = "" + local _colour = "" - if _args[2] == trigger.smokeColor.Red then + if _args[2] == trigger.smokeColor.Red then - _colour = "RED" - elseif _args[2] == trigger.smokeColor.Blue then + _colour = "RED" + elseif _args[2] == trigger.smokeColor.Blue then - _colour = "BLUE" - elseif _args[2] == trigger.smokeColor.Green then + _colour = "BLUE" + elseif _args[2] == trigger.smokeColor.Green then - _colour = "GREEN" - elseif _args[2] == trigger.smokeColor.Orange then + _colour = "GREEN" + elseif _args[2] == trigger.smokeColor.Orange then - _colour = "ORANGE" + _colour = "ORANGE" + end + + local _point = _heli:getPoint() + + local _pos2 = { x = _point.x, y = _point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _point.x, y = _alt, z = _point.z } + + trigger.action.smoke(_pos3, _args[2]) + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped %2 smoke.", ctld.getPlayerNameOrType(_heli), _colour), 10) end - - local _point = _heli:getPoint() - - local _pos2 = { x = _point.x, y = _point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _point.x, y = _alt, z = _point.z } - - trigger.action.smoke(_pos3, _args[2]) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped %2 smoke.", ctld.getPlayerNameOrType(_heli), _colour), 10) - end end function ctld.unitCanCarryVehicles(_unit) - local _type = string.lower(_unit:getTypeName()) + local _type = string.lower(_unit:getTypeName()) - for _, _name in ipairs(ctld.vehicleTransportEnabled) do - local _nameLower = string.lower(_name) - if string.find(_type, _nameLower, 1, true) then - return true + for _, _name in ipairs(ctld.vehicleTransportEnabled) do + local _nameLower = string.lower(_name) + if string.find(_type, _nameLower, 1, true) then + return true + end end - end - return false + return false end function ctld.unitDynamicCargoCapable(_unit) - local cache = {} - local _type = string.lower(_unit:getTypeName()) - local result = cache[_type] - if result == nil then - result = false - ctld.logDebug("ctld.unitDynamicCargoCapable(_type=[%s])", ctld.p(_type)) - for _, _name in ipairs(ctld.dynamicCargoUnits) do - local _nameLower = string.lower(_name) - if string.find(_type, _nameLower, 1, true) then --string.match does not work with patterns containing '-' as it is a magic character - result = true - break - end + local cache = {} + local _type = string.lower(_unit:getTypeName()) + local result = cache[_type] + if result == nil then + result = false + ctld.logDebug("ctld.unitDynamicCargoCapable(_type=[%s])", ctld.p(_type)) + for _, _name in ipairs(ctld.dynamicCargoUnits) do + local _nameLower = string.lower(_name) + if string.find(_type, _nameLower, 1, true) then --string.match does not work with patterns containing '-' as it is a magic character + result = true + break + end + end + cache[_type] = result + ctld.logDebug("result=[%s]", ctld.p(result)) end - cache[_type] = result - ctld.logDebug("result=[%s]", ctld.p(result)) - end - return result + return result end function ctld.isJTACUnitType(_type) - if _type then - _type = string.lower(_type) - for _, _name in ipairs(ctld.jtacUnitTypes) do - local _nameLower = string.lower(_name) - if string.match(_type, _nameLower) then - return true - end + if _type then + _type = string.lower(_type) + for _, _name in ipairs(ctld.jtacUnitTypes) do + local _nameLower = string.lower(_name) + if string.match(_type, _nameLower) then + return true + end + end end - end - return false + return false end function ctld.updateZoneCounter(_index, _diff) - if ctld.pickupZones[_index] ~= nil then + if ctld.pickupZones[_index] ~= nil then - ctld.pickupZones[_index][3] = ctld.pickupZones[_index][3] + _diff + ctld.pickupZones[_index][3] = ctld.pickupZones[_index][3] + _diff - if ctld.pickupZones[_index][3] < 0 then - ctld.pickupZones[_index][3] = 0 + if ctld.pickupZones[_index][3] < 0 then + ctld.pickupZones[_index][3] = 0 + end + + if ctld.pickupZones[_index][6] ~= nil then + trigger.action.setUserFlag(ctld.pickupZones[_index][6], ctld.pickupZones[_index][3]) + end + -- env.info(ctld.pickupZones[_index][1].." = " ..ctld.pickupZones[_index][3]) end - - if ctld.pickupZones[_index][6] ~= nil then - trigger.action.setUserFlag(ctld.pickupZones[_index][6], ctld.pickupZones[_index][3]) - end - -- env.info(ctld.pickupZones[_index][1].." = " ..ctld.pickupZones[_index][3]) - end end function ctld.processCallback(_callbackArgs) - for _, _callback in pairs(ctld.callbacks) do + for _, _callback in pairs(ctld.callbacks) do - local _status, _result = pcall(function() + local _status, _result = pcall(function() - _callback(_callbackArgs) + _callback(_callbackArgs) - end) + end) - if (not _status) then - env.error(string.format("CTLD Callback Error: %s", _result)) + if (not _status) then + env.error(string.format("CTLD Callback Error: %s", _result)) + end end - end end @@ -5887,558 +5887,579 @@ end -- as long as the troops are on the ground function ctld.checkAIStatus() - timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 2) + timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 2) - for _, _unitName in pairs(ctld.transportPilotNames) do - local status, error = pcall(function() + for _, _unitName in pairs(ctld.transportPilotNames) do + local status, error = pcall(function() - local _unit = ctld.getTransportUnit(_unitName) + local _unit = ctld.getTransportUnit(_unitName) - -- no player name means AI! - if _unit ~= nil and _unit:getPlayerName() == nil then - local _zone = ctld.inPickupZone(_unit) - -- env.error("Checking.. ".._unit:getName()) - if _zone.inZone == true and not ctld.troopsOnboard(_unit, true) then - -- env.error("in zone, loading.. ".._unit:getName()) + -- no player name means AI! + if _unit ~= nil and _unit:getPlayerName() == nil then + local _zone = ctld.inPickupZone(_unit) + -- env.error("Checking.. ".._unit:getName()) + if _zone.inZone == true and not ctld.troopsOnboard(_unit, true) then + -- env.error("in zone, loading.. ".._unit:getName()) - if ctld.allowRandomAiTeamPickups == true then - -- Random troop pickup implementation - local _team = nil - if _unit:getCoalition() == 1 then - _team = math.floor((math.random(#ctld.redTeams * 100) / 100) + 1) - ctld.loadTroopsFromZone({ _unitName, true,ctld.loadableGroups[ctld.redTeams[_team]],true }) - else - _team = math.floor((math.random(#ctld.blueTeams * 100) / 100) + 1) - ctld.loadTroopsFromZone({ _unitName, true,ctld.loadableGroups[ctld.blueTeams[_team]],true }) + if ctld.allowRandomAiTeamPickups == true then + -- Random troop pickup implementation + local _team = nil + if _unit:getCoalition() == 1 then + _team = math.floor((math.random(#ctld.redTeams * 100) / 100) + 1) + ctld.loadTroopsFromZone({ _unitName, true,ctld.loadableGroups[ctld.redTeams[_team]],true }) + else + _team = math.floor((math.random(#ctld.blueTeams * 100) / 100) + 1) + ctld.loadTroopsFromZone({ _unitName, true,ctld.loadableGroups[ctld.blueTeams[_team]],true }) + end + else + ctld.loadTroopsFromZone({ _unitName, true,"",true }) + end + + elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, true) then + -- env.error("in dropoff zone, unloading.. ".._unit:getName()) + ctld.unloadTroops( { _unitName, true }) + end + + if ctld.unitCanCarryVehicles(_unit) then + + if _zone.inZone == true and not ctld.troopsOnboard(_unit, false) then + + ctld.loadTroopsFromZone({ _unitName, false,"",true }) + + elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, false) then + + ctld.unloadTroops( { _unitName, false }) + end + end end - else - ctld.loadTroopsFromZone({ _unitName, true,"",true }) - end + end) - elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, true) then - -- env.error("in dropoff zone, unloading.. ".._unit:getName()) - ctld.unloadTroops( { _unitName, true }) + if (not status) then + env.error(string.format("Error with ai status: %s", error), false) end - - if ctld.unitCanCarryVehicles(_unit) then - - if _zone.inZone == true and not ctld.troopsOnboard(_unit, false) then - - ctld.loadTroopsFromZone({ _unitName, false,"",true }) - - elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, false) then - - ctld.unloadTroops( { _unitName, false }) - end - end - end - end) - - if (not status) then - env.error(string.format("Error with ai status: %s", error), false) end - end end function ctld.getTransportLimit(_unitType) - if ctld.unitLoadLimits[_unitType] then + if ctld.unitLoadLimits[_unitType] then - return ctld.unitLoadLimits[_unitType] - end + return ctld.unitLoadLimits[_unitType] + end - return ctld.numberOfTroops + return ctld.numberOfTroops end function ctld.getUnitActions(_unitType) - if ctld.unitActions[_unitType] then - return ctld.unitActions[_unitType] - end + if ctld.unitActions[_unitType] then + return ctld.unitActions[_unitType] + end - return {crates=true,troops=true} + return {crates=true,troops=true} end -- Adds menuitem to a human unit function ctld.addTransportF10MenuOptions(_unitName) - ctld.logDebug("ctld.addTransportF10MenuOptions(_unitName=[%s])", ctld.p(_unitName)) - local status, error = pcall(function() + ctld.logDebug("ctld.addTransportF10MenuOptions(_unitName=[%s])", ctld.p(_unitName)) + local status, error = pcall(function() - local _unit = ctld.getTransportUnit(_unitName) - ctld.logTrace("_unit = %s", ctld.p(_unit)) + local _unit = ctld.getTransportUnit(_unitName) + ctld.logTrace("_unit = %s", ctld.p(_unit)) - if _unit then - local _unitTypename = _unit:getTypeName() - local _groupId = ctld.getGroupId(_unit) - if _groupId then - ctld.logTrace("_groupId = %s", ctld.p(_groupId)) - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) - if ctld.addedTo[tostring(_groupId)] == nil then - ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) - local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) - local _unitActions = ctld.getUnitActions(_unitTypename) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _rootPath, ctld.checkTroopStatus, { _unitName }) - if _unitActions.troops then - local _troopCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Troop Transport"), _rootPath) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload / Extract Troops"), _troopCommandsPath, ctld.unloadExtractTroops, { _unitName }) + if _unit then + local _unitTypename = _unit:getTypeName() + local _groupId = ctld.getGroupId(_unit) + if _groupId then + ctld.logTrace("_groupId = %s", ctld.p(_groupId)) + ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) + if ctld.addedTo[tostring(_groupId)] == nil then + ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) + local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) + local _unitActions = ctld.getUnitActions(_unitTypename) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _rootPath, ctld.checkTroopStatus, { _unitName }) + if _unitActions.troops then + local _troopCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Troop Transport"), _rootPath) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload / Extract Troops"), _troopCommandsPath, ctld.unloadExtractTroops, { _unitName }) - -- local _loadPath = missionCommands.addSubMenuForGroup(_groupId, "Load From Zone", _troopCommandsPath) - local _transportLimit = ctld.getTransportLimit(_unitTypename) - local itemNb = 0 - local menuEntries = {} - local menuPath = _troopCommandsPath - for _,_loadGroup in pairs(ctld.loadableGroups) do - if not _loadGroup.side or _loadGroup.side == _unit:getCoalition() then - -- check size & unit - if _transportLimit >= _loadGroup.total then - table.insert(menuEntries, { text = ctld.i18n_translate("Load ").._loadGroup.name, group = _loadGroup }) - 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.vehicleCommandsPath == nil then - ctld.vehicleCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) - end + -- local _loadPath = missionCommands.addSubMenuForGroup(_groupId, "Load From Zone", _troopCommandsPath) + local _transportLimit = ctld.getTransportLimit(_unitTypename) + local itemNb = 0 + local menuEntries = {} + local menuPath = _troopCommandsPath + for _,_loadGroup in pairs(ctld.loadableGroups) do + if not _loadGroup.side or _loadGroup.side == _unit:getCoalition() then + -- check size & unit + if _transportLimit >= _loadGroup.total then + table.insert(menuEntries, { text = ctld.i18n_translate("Load ").._loadGroup.name, group = _loadGroup }) + 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.vehicleCommandsPath == nil then + ctld.vehicleCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) + end - if ctld.enableRepackingVehicles then - ctld.updateRepackMenu(_unitName) - end - 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 - - 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("Vehicle / FOB Crates / 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 - 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) + if ctld.enableRepackingVehicles then + ctld.updateRepackMenu(_unitName) + end + 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 + + 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("Vehicle / FOB Crates / 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 + 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 + end + end + for _i, _menu in ipairs(menuEntries) do + ctld.logTrace("_menu = [%s]", ctld.p(_menu)) + -- add the submenu item + itemNbSubmenu = itemNbSubmenu + 1 + if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached + _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"), _subMenuPath) + itemNbSubmenu = 1 + end + missionCommands.addCommandForGroup(_groupId, _menu.text, _subMenuPath, ctld.spawnCrate, { _unitName, _menu.crate.weight }) + 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.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 + 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 - for _i, _menu in ipairs(menuEntries) do - ctld.logTrace("_menu = [%s]", ctld.p(_menu)) - -- add the submenu item - itemNbSubmenu = itemNbSubmenu + 1 - if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached - _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"), _subMenuPath) - itemNbSubmenu = 1 - end - missionCommands.addCommandForGroup(_groupId, _menu.text, _subMenuPath, ctld.spawnCrate, { _unitName, _menu.crate.weight }) - 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.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 - 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) - if (not status) then - ctld.logError(string.format("Error adding f10 to transport: %s", error)) - end + end) + if (not status) then + ctld.logError(string.format("Error adding f10 to transport: %s", error)) + end end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) - local nextSubMenuPath = "" - local itemNbSubmenu = 0 - for i, menu in ipairs(_menuEntries) do - --ctld.logTrace("FG ctld.buildPaginatedMenu. [%s] mist.utils.tableShow(menu) = [%s]", i, mist.utils.tableShow(menu)) - if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then - menu.subMenuPath = nextSubMenuPath + ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) + local nextSubMenuPath = "" + local itemNbSubmenu = 0 + for i, menu in ipairs(_menuEntries) do + --ctld.logTrace("FG ctld.buildPaginatedMenu. [%s] mist.utils.tableShow(menu) = [%s]", i, mist.utils.tableShow(menu)) + if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then + menu.subMenuPath = nextSubMenuPath + end + -- add the submenu item + itemNbSubmenu = itemNbSubmenu + 1 + if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached + menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) + nextSubMenuPath = menu.subMenuPath + itemNbSubmenu = 1 + end + menu.menuArgsTable.subMenuPath = menu.subMenuPath + menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu + missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) end - -- add the submenu item - itemNbSubmenu = itemNbSubmenu + 1 - if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached - menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) - nextSubMenuPath = menu.subMenuPath - itemNbSubmenu = 1 - end - menu.menuArgsTable.subMenuPath = menu.subMenuPath - menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu - missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) - end end --****************************************************************************************************** function ctld.updateRepackMenu(_playerUnitName) - local playerUnit = ctld.getTransportUnit(_playerUnitName) - if playerUnit then - local _unitTypename = playerUnit:getTypeName() - local _groupId = ctld.getGroupId(playerUnit) - if ctld.enableRepackingVehicles then - local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) - if repackableVehicles then - - missionCommands.removeItemForGroup(1, ctld.vehicleCommandsPath) -- remove the old menu - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) - - local menuEntries = {} - local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) - RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") - for _, _vehicle in pairs(repackableVehicles) do - table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, - groupId = _groupId, - subMenuPath = RepackCommandsPath, - menuFunction = ctld.repackVehicleRequest, - menuArgsTable = {_vehicle, _playerUnitName} }) - end - ctld.buildPaginatedMenu(menuEntries) + local playerUnit = ctld.getTransportUnit(_playerUnitName) + if playerUnit then + local _unitTypename = playerUnit:getTypeName() + local _groupId = ctld.getGroupId(playerUnit) + if ctld.enableRepackingVehicles then + local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) + if repackableVehicles then + missionCommands.removeItemForGroup(1, ctld.vehicleCommandsPath) -- remove the old menu + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) + + local menuEntries = {} + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) + RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") + for _, _vehicle in pairs(repackableVehicles) do + table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, + groupId = _groupId, + subMenuPath = RepackCommandsPath, + menuFunction = ctld.repackVehicleRequest, + menuArgsTable = {_vehicle, _playerUnitName} }) end + ctld.buildPaginatedMenu(menuEntries) + end + end + end +end +--****************************************************************************************************** +function ctld.autoUpdateRepackMenu() + timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + if ctld.enableRepackingVehicles then + for _, _unitName in pairs(ctld.transportPilotNames) do + local status, error = pcall(function() + local _unit = ctld.getTransportUnit(_unitName) + if _unit then + local _unitTypename = _unit:getTypeName() + local _groupId = ctld.getGroupId(_unit) + if _groupId then + --if ctld.addedTo[tostring(_groupId)] == nil then + ctld.updateRepackMenu(_unitName) + --end + end + end + end) + if (not status) then + env.error(string.format("Error with repack menu: %s", error), false) end end end end --****************************************************************************************************** function ctld.addOtherF10MenuOptions() - ctld.logDebug("ctld.addOtherF10MenuOptions") - -- reschedule every 10 seconds - timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 10) - local status, error = pcall(function() + ctld.logDebug("ctld.addOtherF10MenuOptions") + -- reschedule every 10 seconds + timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 10) + local status, error = pcall(function() - -- now do any player controlled aircraft that ARENT transport units - if ctld.enabledRadioBeaconDrop then - -- get all BLUE players - ctld.addRadioListCommand(2) + -- now do any player controlled aircraft that ARENT transport units + if ctld.enabledRadioBeaconDrop then + -- get all BLUE players + ctld.addRadioListCommand(2) - -- get all RED players - ctld.addRadioListCommand(1) + -- get all RED players + ctld.addRadioListCommand(1) + end + + if ctld.JTAC_jtacStatusF10 then + ctld.addJTACRadioCommand(2) -- get all BLUE players + ctld.addJTACRadioCommand(1) -- get all RED players + end + + if ctld.reconF10Menu then + ctld.addReconRadioCommand(2) -- get all BLUE players + ctld.addReconRadioCommand(1) -- get all RED players + end + end) + + if (not status) then + env.error(string.format("Error adding f10 to other players: %s", error), false) end - - if ctld.JTAC_jtacStatusF10 then - ctld.addJTACRadioCommand(2) -- get all BLUE players - ctld.addJTACRadioCommand(1) -- get all RED players - end - - if ctld.reconF10Menu then - ctld.addReconRadioCommand(2) -- get all BLUE players - ctld.addReconRadioCommand(1) -- get all RED players - end - end) - - if (not status) then - env.error(string.format("Error adding f10 to other players: %s", error), false) - end end --add to all players that arent transport function ctld.addRadioListCommand(_side) - local _players = coalition.getPlayers(_side) + local _players = coalition.getPlayers(_side) - if _players ~= nil then + if _players ~= nil then - for _, _playerUnit in pairs(_players) do + for _, _playerUnit in pairs(_players) do - local _groupId = ctld.getGroupId(_playerUnit) + local _groupId = ctld.getGroupId(_playerUnit) - if _groupId then + if _groupId then - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) - if ctld.addedTo[tostring(_groupId)] == nil then - ctld.logTrace("adding List Radio Beacons for _groupId = %s", ctld.p(_groupId)) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Radio Beacons"), nil, ctld.listRadioBeacons, { _playerUnit:getName() }) - ctld.addedTo[tostring(_groupId)] = true + ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) + if ctld.addedTo[tostring(_groupId)] == nil then + ctld.logTrace("adding List Radio Beacons for _groupId = %s", ctld.p(_groupId)) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Radio Beacons"), nil, ctld.listRadioBeacons, { _playerUnit:getName() }) + ctld.addedTo[tostring(_groupId)] = true + end + end end - end end - end end function ctld.addJTACRadioCommand(_side) - local _players = coalition.getPlayers(_side) + local _players = coalition.getPlayers(_side) - if _players ~= nil then + if _players ~= nil then - for _, _playerUnit in pairs(_players) do + for _, _playerUnit in pairs(_players) do - local _groupId = ctld.getGroupId(_playerUnit) + local _groupId = ctld.getGroupId(_playerUnit) - if _groupId then + if _groupId then - local newGroup = false - if ctld.jtacRadioAdded[tostring(_groupId)] == nil then - ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", ctld.p(_playerUnit:getName())) - newGroup = true - local JTACpath = missionCommands.addSubMenuForGroup(_groupId, ctld.jtacMenuName) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("JTAC Status"), JTACpath, ctld.getJTACStatus, { _playerUnit:getName() }) - ctld.jtacRadioAdded[tostring(_groupId)] = true - end - - --fetch the time to check for a regular refresh - local time = timer.getTime() - - --depending on the delay, this part of the radio menu will be refreshed less often or as often as the static JTAC status command, this is for better reliability for the user when navigating through the menus. New groups will get the lists regardless and if a new JTAC is added all lists will be refreshed regardless of the delay. - if ctld.jtacLastRadioRefresh + ctld.jtacRadioRefreshDelay <= time or ctld.refreshJTACmenu[_side] or newGroup then - - ctld.jtacLastRadioRefresh = time - - --build the path to the CTLD JTAC menu - local jtacCurrentPagePath = {[1]=ctld.jtacMenuName} - --build the path for the NextPage submenu on the first page of the CTLD JTAC menu - local NextPageText = "Next Page" - local MainNextPagePath = {[1]=ctld.jtacMenuName, [2]=NextPageText} - --remove it along with everything that's in it - missionCommands.removeItemForGroup(_groupId, MainNextPagePath) - - --counter to know when to add the next page submenu to fit all of the JTAC group submenus - local jtacCounter = 0 - - for _jtacGroupName,jtacUnit in pairs(ctld.jtacUnits) do - --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 - if jtacCoalition and jtacCoalition == _side then - --only bother removing the submenus on the first page of the CTLD JTAC menu as the other pages were deleted entirely above - 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]))) - - local jtacActionMenu = false - for _,_specialOptionTable in pairs(ctld.jtacSpecialOptions) do - if _specialOptionTable.globalToggle then - jtacActionMenu = true - break + local newGroup = false + if ctld.jtacRadioAdded[tostring(_groupId)] == nil then + ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", ctld.p(_playerUnit:getName())) + newGroup = true + local JTACpath = missionCommands.addSubMenuForGroup(_groupId, ctld.jtacMenuName) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("JTAC Status"), JTACpath, ctld.getJTACStatus, { _playerUnit:getName() }) + ctld.jtacRadioAdded[tostring(_groupId)] = true end - end - --if JTAC has at least one other target in sight or (if special options are available (NOTE : accessed through the JTAC's own menu also) and the JTAC has at least one target) - if (ctld.jtacTargetsList[_jtacGroupName] and #ctld.jtacTargetsList[_jtacGroupName] >= 1) or (ctld.jtacCurrentTargets[_jtacGroupName] and jtacActionMenu) then + --fetch the time to check for a regular refresh + local time = timer.getTime() - local jtacGroupSubMenuName = string.format(_jtacGroupName .. " Selection") + --depending on the delay, this part of the radio menu will be refreshed less often or as often as the static JTAC status command, this is for better reliability for the user when navigating through the menus. New groups will get the lists regardless and if a new JTAC is added all lists will be refreshed regardless of the delay. + if ctld.jtacLastRadioRefresh + ctld.jtacRadioRefreshDelay <= time or ctld.refreshJTACmenu[_side] or newGroup then - jtacCounter = jtacCounter + 1 - --F2 through F10 makes 9 entries possible per page, with one being the NextMenu submenu. F1 is taken by JTAC status entry. - if jtacCounter % 9 == 0 then - --recover the path to the current page with space available for JTAC group submenus - jtacCurrentPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacCurrentPagePath) - 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.jtacLastRadioRefresh = time - --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]) + --build the path to the CTLD JTAC menu + local jtacCurrentPagePath = {[1]=ctld.jtacMenuName} + --build the path for the NextPage submenu on the first page of the CTLD JTAC menu + local NextPageText = "Next Page" + local MainNextPagePath = {[1]=ctld.jtacMenuName, [2]=NextPageText} + --remove it along with everything that's in it + missionCommands.removeItemForGroup(_groupId, MainNextPagePath) - --counter to know when to add the next page submenu to fit all of the targets in the JTAC's group submenu. SMay not actually start at 0 due to static items being present on the first page - local itemCounter = 0 - local jtacSpecialOptPagePath = nil + --counter to know when to add the next page submenu to fit all of the JTAC group submenus + local jtacCounter = 0 - if jtacActionMenu then - --special options - local SpecialOptionsCounter = 0 + for _jtacGroupName,jtacUnit in pairs(ctld.jtacUnits) do + --ctld.logTrace(string.format("JTAC - MENU - [%s] - processing menu", ctld.p(_jtacGroupName))) - for _,_specialOption in pairs(ctld.jtacSpecialOptions) do - if _specialOption.globalToggle then + --if the JTAC is on the same team as the group being considered + local jtacCoalition = ctld.jtacUnits[_jtacGroupName].side + if jtacCoalition and jtacCoalition == _side then + --only bother removing the submenus on the first page of the CTLD JTAC menu as the other pages were deleted entirely above + 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]))) - if not jtacSpecialOptPagePath then - itemCounter = itemCounter + 1 --one item is added to the first JTAC target page - jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Actions"), jtacTargetPagePath) - end + local jtacActionMenu = false + for _,_specialOptionTable in pairs(ctld.jtacSpecialOptions) do + if _specialOptionTable.globalToggle then + jtacActionMenu = true + break + end + end - SpecialOptionsCounter = SpecialOptionsCounter+1 + --if JTAC has at least one other target in sight or (if special options are available (NOTE : accessed through the JTAC's own menu also) and the JTAC has at least one target) + if (ctld.jtacTargetsList[_jtacGroupName] and #ctld.jtacTargetsList[_jtacGroupName] >= 1) or (ctld.jtacCurrentTargets[_jtacGroupName] and jtacActionMenu) then - if SpecialOptionsCounter%10 == 0 then - jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacSpecialOptPagePath) - SpecialOptionsCounter = SpecialOptionsCounter+1 --Added Next Page item - end + local jtacGroupSubMenuName = string.format(_jtacGroupName .. " Selection") - if _specialOption.jtacs then - if _specialOption.jtacs[_jtacGroupName] then - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("DISABLE ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = false}) - else - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("ENABLE ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = true}) + jtacCounter = jtacCounter + 1 + --F2 through F10 makes 9 entries possible per page, with one being the NextMenu submenu. F1 is taken by JTAC status entry. + if jtacCounter % 9 == 0 then + --recover the path to the current page with space available for JTAC group submenus + jtacCurrentPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacCurrentPagePath) + 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]))) + + --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]) + + --counter to know when to add the next page submenu to fit all of the targets in the JTAC's group submenu. SMay not actually start at 0 due to static items being present on the first page + local itemCounter = 0 + local jtacSpecialOptPagePath = nil + + if jtacActionMenu then + --special options + local SpecialOptionsCounter = 0 + + for _,_specialOption in pairs(ctld.jtacSpecialOptions) do + if _specialOption.globalToggle then + + if not jtacSpecialOptPagePath then + itemCounter = itemCounter + 1 --one item is added to the first JTAC target page + jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Actions"), jtacTargetPagePath) + end + + SpecialOptionsCounter = SpecialOptionsCounter+1 + + if SpecialOptionsCounter%10 == 0 then + jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacSpecialOptPagePath) + SpecialOptionsCounter = SpecialOptionsCounter+1 --Added Next Page item + end + + if _specialOption.jtacs then + if _specialOption.jtacs[_jtacGroupName] then + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("DISABLE ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = false}) + else + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("ENABLE ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = true}) + end + else + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("REQUEST ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = false}) --value is not used here + end + end + end + end + + if #ctld.jtacTargetsList[_jtacGroupName] >= 1 then + --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, ctld.i18n_translate("Reset TGT Selection"), jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = nil}) + + itemCounter = itemCounter + 1 --one item is added to the first JTAC target page + + --indicator table to know which unitType was already added to the radio submenu + local typeNameList = {} + for _,target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do + local targetName = target.unit:getName() + --check if the jtac has a current target before filtering it out if possible + if (ctld.jtacCurrentTargets[_jtacGroupName] and targetName ~= ctld.jtacCurrentTargets[_jtacGroupName].name) then + local targetType_name = target.unit:getTypeName() + + if targetType_name then + if typeNameList[targetType_name] then + typeNameList[targetType_name].amount = typeNameList[targetType_name].amount + 1 + else + typeNameList[targetType_name] = {} + typeNameList[targetType_name].targetName = targetName --store the first targetName + typeNameList[targetType_name].amount = 1 + end + end + end + end + + for typeName,info in pairs(typeNameList) do + local amount = info.amount + local targetName = info.targetName + itemCounter = itemCounter + 1 + + --F1 through F10 makes 10 entries possible per page, with one being the NextMenu submenu. + if itemCounter%10 == 0 then + jtacTargetPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacTargetPagePath) + itemCounter = itemCounter + 1 --added the next page item + end + + missionCommands.addCommandForGroup(_groupId, string.format(typeName .. "(" .. amount .. ")"), jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = targetName}) + end + end + end end - else - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("REQUEST ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = false}) --value is not used here - end end - end end - - if #ctld.jtacTargetsList[_jtacGroupName] >= 1 then - --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, ctld.i18n_translate("Reset TGT Selection"), jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = nil}) - - itemCounter = itemCounter + 1 --one item is added to the first JTAC target page - - --indicator table to know which unitType was already added to the radio submenu - local typeNameList = {} - for _,target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do - local targetName = target.unit:getName() - --check if the jtac has a current target before filtering it out if possible - if (ctld.jtacCurrentTargets[_jtacGroupName] and targetName ~= ctld.jtacCurrentTargets[_jtacGroupName].name) then - local targetType_name = target.unit:getTypeName() - - if targetType_name then - if typeNameList[targetType_name] then - typeNameList[targetType_name].amount = typeNameList[targetType_name].amount + 1 - else - typeNameList[targetType_name] = {} - typeNameList[targetType_name].targetName = targetName --store the first targetName - typeNameList[targetType_name].amount = 1 - end - end - end - end - - for typeName,info in pairs(typeNameList) do - local amount = info.amount - local targetName = info.targetName - itemCounter = itemCounter + 1 - - --F1 through F10 makes 10 entries possible per page, with one being the NextMenu submenu. - if itemCounter%10 == 0 then - jtacTargetPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacTargetPagePath) - itemCounter = itemCounter + 1 --added the next page item - end - - missionCommands.addCommandForGroup(_groupId, string.format(typeName .. "(" .. amount .. ")"), jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = targetName}) - end - end - end end - end end - end - end - if ctld.refreshJTACmenu[_side] then - ctld.refreshJTACmenu[_side] = false + if ctld.refreshJTACmenu[_side] then + ctld.refreshJTACmenu[_side] = false + end end - end end function ctld.getGroupId(_unit) - local _unitDB = mist.DBs.unitsById[tonumber(_unit:getID())] - if _unitDB ~= nil and _unitDB.groupId then - return _unitDB.groupId - end + local _unitDB = mist.DBs.unitsById[tonumber(_unit:getID())] + if _unitDB ~= nil and _unitDB.groupId then + return _unitDB.groupId + end - return nil + return nil end --get distance in meters assuming a Flat world function ctld.getDistance(_point1, _point2) - local xUnit = _point1.x - local yUnit = _point1.z - local xZone = _point2.x - local yZone = _point2.z + local xUnit = _point1.x + local yUnit = _point1.z + local xZone = _point2.x + local yZone = _point2.z - local xDiff = xUnit - xZone - local yDiff = yUnit - yZone + local xDiff = xUnit - xZone + local yDiff = yUnit - yZone - return math.sqrt(xDiff * xDiff + yDiff * yDiff) + return math.sqrt(xDiff * xDiff + yDiff * yDiff) end @@ -6454,32 +6475,32 @@ ctld.jtacCurrentTargets = {} ctld.jtacTargetsList = {} --current available targets to each JTAC for lasing (targets from other JTACs are filtered out). Contains DCS unit objects with their methods and the distance to the JTAC {unit, dist} ctld.jtacSelectedTarget = {} --currently user selected target if it contains a unit's name, otherwise contains 1 or nil (if not initialized) ctld.jtacSpecialOptions = { --list which contains the status of special options for each jtac, ordered for them to show up in the correct order in the corresponding radio menu - standbyMode = { --#1 - globalToggle = ctld.JTAC_allowStandbyMode; - message = "Standby Mode"; - setter = nil; --ctld.setStdbMode, will be set after declaration of said function - jtacs = { - --enable flag for each JTAC - }; - }; --disable designation by the JTAC - smokeMarker = { --#4 - globalToggle = ctld.JTAC_allowSmokeRequest; - message = "Smoke on TGT"; - setter = nil; --ctld.setSmokeOnTarget - }; --smoke marker on target - laseSpotCorrections = { --#2 - globalToggle = ctld.JTAC_laseSpotCorrections; - message = "Speed Corrections"; - setter = nil; --ctld.setLaseCompensation - jtacs = { - --enable flag for each JTAC - }; - }; --target speed and wind compensation for laser spot - _9Line = { --#3 - globalToggle = ctld.JTAC_allow9Line; - message = "9 Line"; - setter = nil; --ctld.setJTAC9Line - }; --9Line message for JTAC + standbyMode = { --#1 + globalToggle = ctld.JTAC_allowStandbyMode; + message = "Standby Mode"; + setter = nil; --ctld.setStdbMode, will be set after declaration of said function + jtacs = { + --enable flag for each JTAC + }; + }; --disable designation by the JTAC + smokeMarker = { --#4 + globalToggle = ctld.JTAC_allowSmokeRequest; + message = "Smoke on TGT"; + setter = nil; --ctld.setSmokeOnTarget + }; --smoke marker on target + laseSpotCorrections = { --#2 + globalToggle = ctld.JTAC_laseSpotCorrections; + message = "Speed Corrections"; + setter = nil; --ctld.setLaseCompensation + jtacs = { + --enable flag for each JTAC + }; + }; --target speed and wind compensation for laser spot + _9Line = { --#3 + globalToggle = ctld.JTAC_allow9Line; + message = "9 Line"; + setter = nil; --ctld.setJTAC9Line + }; --9Line message for JTAC } ctld.jtacRadioAdded = {} --keeps track of who's had the radio command added ctld.jtacGroupSubMenuPath = {} --keeps track of which submenu contains each JTAC's target selection menu @@ -6491,372 +6512,372 @@ ctld.jtacLaserPointCodes = {} ctld.jtacRadioData = {} --[[ - Called when a new JTAC is spawned, it will wait one second for DCS to have time to fill the group with units, and then call ctld.JTACAutoLase. + Called when a new JTAC is spawned, it will wait one second for DCS to have time to fill the group with units, and then call ctld.JTACAutoLase. - The goal here is to correct a bug: when a group is respawned (i.e. when any group with the name of a previously existing group is spawned), - DCS spawns a group which exists (Group.getByName gets a valid table, and group:isExist returns true), but has no units (i.e. group:getUnits returns an empty table). - This causes JTACAutoLase to call cleanupJTAC because it does not find the JTAC unit, and the JTAC to be put out of the JTACAutoLase loop, and never processed again. - By waiting a bit, the group gets populated before JTACAutoLase is called, hence avoiding a trip to cleanupJTAC. + The goal here is to correct a bug: when a group is respawned (i.e. when any group with the name of a previously existing group is spawned), + DCS spawns a group which exists (Group.getByName gets a valid table, and group:isExist returns true), but has no units (i.e. group:getUnits returns an empty table). + This causes JTACAutoLase to call cleanupJTAC because it does not find the JTAC unit, and the JTAC to be put out of the JTACAutoLase loop, and never processed again. + By waiting a bit, the group gets populated before JTACAutoLase is called, hence avoiding a trip to cleanupJTAC. ]] function ctld.JTACStart(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio) - mist.scheduleFunction(ctld.JTACAutoLase, {_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio}, timer.getTime()+1) + mist.scheduleFunction(ctld.JTACAutoLase, {_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio}, timer.getTime()+1) end function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio) - ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), ctld.p(_laserCode))) + ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), ctld.p(_laserCode))) - local _radio = _radio - if not _radio then - _radio = {} - if _laserCode then - local _laserCode = tonumber(_laserCode) - if _laserCode and _laserCode >= 1111 and _laserCode <= 1688 then - local _laserB = math.floor((_laserCode - 1000)/100) - local _laserCD = _laserCode - 1000 - _laserB*100 - local _frequency = tostring(30+_laserB+_laserCD*0.05) - ctld.logTrace(string.format("_laserB=%s", ctld.p(_laserB))) - ctld.logTrace(string.format("_laserCD=%s", ctld.p(_laserCD))) - ctld.logTrace(string.format("_frequency=%s", ctld.p(_frequency))) - _radio.freq = _frequency - _radio.mod = "fm" - end - end - end - - if _radio and not _radio.name then - _radio.name = _jtacGroupName - end - - if ctld.jtacStop[_jtacGroupName] == true then - ctld.jtacStop[_jtacGroupName] = nil -- allow it to be started again - ctld.cleanupJTAC(_jtacGroupName) - return - end - - if _lock == nil then - _lock = ctld.JTAC_lock - end - - ctld.jtacLaserPointCodes[_jtacGroupName] = _laserCode - ctld.jtacRadioData[_jtacGroupName] = _radio - - local _jtacGroup = ctld.getGroup(_jtacGroupName) - local _jtacUnit - - if _jtacGroup == nil or #_jtacGroup == 0 then - - --check not in a heli - if ctld.inTransitTroops then - for _, _onboard in pairs(ctld.inTransitTroops) do - if _onboard ~= nil then - if _onboard.troops ~= nil and _onboard.troops.groupName ~= nil and _onboard.troops.groupName == _jtacGroupName then - - --jtac soldier being transported by heli - ctld.cleanupJTAC(_jtacGroupName) - - ctld.logTrace(string.format("JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) - return - end - - if _onboard.vehicles ~= nil and _onboard.vehicles.groupName ~= nil and _onboard.vehicles.groupName == _jtacGroupName then - --jtac vehicle being transported by heli - ctld.cleanupJTAC(_jtacGroupName) - - ctld.logTrace(string.format("JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) - return - end + local _radio = _radio + if not _radio then + _radio = {} + if _laserCode then + local _laserCode = tonumber(_laserCode) + if _laserCode and _laserCode >= 1111 and _laserCode <= 1688 then + local _laserB = math.floor((_laserCode - 1000)/100) + local _laserCD = _laserCode - 1000 - _laserB*100 + local _frequency = tostring(30+_laserB+_laserCD*0.05) + ctld.logTrace(string.format("_laserB=%s", ctld.p(_laserB))) + ctld.logTrace(string.format("_laserCD=%s", ctld.p(_laserCD))) + ctld.logTrace(string.format("_frequency=%s", ctld.p(_frequency))) + _radio.freq = _frequency + _radio.mod = "fm" + end end - end end - if ctld.jtacUnits[_jtacGroupName] ~= nil then - ctld.notifyCoalition(ctld.i18n_translate("JTAC Group %1 KIA!", _jtacGroupName), 10, ctld.jtacUnits[_jtacGroupName].side, _radio) + if _radio and not _radio.name then + _radio.name = _jtacGroupName end - --remove from list - ctld.cleanupJTAC(_jtacGroupName) + if ctld.jtacStop[_jtacGroupName] == true then + ctld.jtacStop[_jtacGroupName] = nil -- allow it to be started again + ctld.cleanupJTAC(_jtacGroupName) + return + end - return - else + if _lock == nil then + _lock = ctld.JTAC_lock + end - _jtacUnit = _jtacGroup[1] - local _jtacCoalition = _jtacUnit:getCoalition() - --add to list - ctld.jtacUnits[_jtacGroupName] = { name = _jtacUnit:getName(), side = _jtacCoalition, radio = _radio } + ctld.jtacLaserPointCodes[_jtacGroupName] = _laserCode + ctld.jtacRadioData[_jtacGroupName] = _radio - --Targets list, special options and Selected target initialization - if not ctld.jtacTargetsList[_jtacGroupName] then - --Target list - ctld.jtacTargetsList[_jtacGroupName] = {} - if _jtacCoalition then ctld.refreshJTACmenu[_jtacCoalition] = true end + local _jtacGroup = ctld.getGroup(_jtacGroupName) + local _jtacUnit - --Special Options - for _,_specialOption in pairs(ctld.jtacSpecialOptions) do - if _specialOption.jtacs then - _specialOption.jtacs[_jtacGroupName] = false + if _jtacGroup == nil or #_jtacGroup == 0 then + + --check not in a heli + if ctld.inTransitTroops then + for _, _onboard in pairs(ctld.inTransitTroops) do + if _onboard ~= nil then + if _onboard.troops ~= nil and _onboard.troops.groupName ~= nil and _onboard.troops.groupName == _jtacGroupName then + + --jtac soldier being transported by heli + ctld.cleanupJTAC(_jtacGroupName) + + ctld.logTrace(string.format("JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) + return + end + + if _onboard.vehicles ~= nil and _onboard.vehicles.groupName ~= nil and _onboard.vehicles.groupName == _jtacGroupName then + --jtac vehicle being transported by heli + ctld.cleanupJTAC(_jtacGroupName) + + ctld.logTrace(string.format("JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) + return + end + end + end end - end - end - if not ctld.jtacSelectedTarget[_jtacGroupName] then - ctld.jtacSelectedTarget[_jtacGroupName] = 1 - end - - -- work out smoke colour - if _colour == nil then - - if _jtacUnit:getCoalition() == 1 then - _colour = ctld.JTAC_smokeColour_RED - else - _colour = ctld.JTAC_smokeColour_BLUE - end - end - - - if _smoke == nil then - - if _jtacUnit:getCoalition() == 1 then - _smoke = ctld.JTAC_smokeOn_RED - else - _smoke = ctld.JTAC_smokeOn_BLUE - end - end - end - - - -- search for current unit - - if _jtacUnit:isActive() == false then - - ctld.cleanupJTAC(_jtacGroupName) - - ctld.logTrace(string.format("JTAC - LASE - [%s] - not active, scheduling JTACAutoLase in 30s at %s", ctld.p(_jtacGroupName), ctld.p(timer.getTime() + 30))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 30) - - return - end - - local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) - --update targets list and store the next potential target if the selected one was lost - local _defaultEnemyUnit = ctld.findNearestVisibleEnemy(_jtacUnit, _lock) - - -- if the JTAC sees a unit and a target was selected by users but is not the current unit, check if the selected target is in the targets list, if it is, then it's been reacquired - if _enemyUnit and ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 and ctld.jtacSelectedTarget[_jtacGroupName] ~= _enemyUnit:getName() then - for _,target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do - if target then - local targetUnit = target.unit - local targetName = targetUnit:getName() - - if ctld.jtacSelectedTarget[_jtacGroupName] == targetName then - - ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = targetUnit:getTypeName(), unitId = targetUnit:getID() } - _enemyUnit = targetUnit - - local message = ctld.i18n_translate("%1, selected target reacquired, %2", _jtacGroupName, _enemyUnit:getTypeName()) - local fullMessage = message .. ctld.i18n_translate(". CODE: %1. POSITION: %2", _laserCode, ctld.getPositionString(_enemyUnit)) - ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) + if ctld.jtacUnits[_jtacGroupName] ~= nil then + ctld.notifyCoalition(ctld.i18n_translate("JTAC Group %1 KIA!", _jtacGroupName), 10, ctld.jtacUnits[_jtacGroupName].side, _radio) end - end - end - end - local targetDestroyed = false - local targetLost = false - local wasSelected = false + --remove from list + ctld.cleanupJTAC(_jtacGroupName) - if _enemyUnit == nil and ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then - - local _tempUnitInfo = ctld.jtacCurrentTargets[_jtacGroupName] - - -- env.info("TEMP UNIT INFO: " .. tempUnitInfo.name .. " " .. tempUnitInfo.unitType) - - local _tempUnit = Unit.getByName(_tempUnitInfo.name) - - wasSelected = (ctld.jtacCurrentTargets[_jtacGroupName].name == ctld.jtacSelectedTarget[_jtacGroupName]) - - if _tempUnit ~= nil and _tempUnit:getLife() > 0 and _tempUnit:isActive() == true then - targetLost = true + return else - targetDestroyed = true - ctld.jtacSelectedTarget[_jtacGroupName] = 1 + + _jtacUnit = _jtacGroup[1] + local _jtacCoalition = _jtacUnit:getCoalition() + --add to list + ctld.jtacUnits[_jtacGroupName] = { name = _jtacUnit:getName(), side = _jtacCoalition, radio = _radio } + + --Targets list, special options and Selected target initialization + if not ctld.jtacTargetsList[_jtacGroupName] then + --Target list + ctld.jtacTargetsList[_jtacGroupName] = {} + if _jtacCoalition then ctld.refreshJTACmenu[_jtacCoalition] = true end + + --Special Options + for _,_specialOption in pairs(ctld.jtacSpecialOptions) do + if _specialOption.jtacs then + _specialOption.jtacs[_jtacGroupName] = false + end + end + end + + if not ctld.jtacSelectedTarget[_jtacGroupName] then + ctld.jtacSelectedTarget[_jtacGroupName] = 1 + end + + -- work out smoke colour + if _colour == nil then + + if _jtacUnit:getCoalition() == 1 then + _colour = ctld.JTAC_smokeColour_RED + else + _colour = ctld.JTAC_smokeColour_BLUE + end + end + + + if _smoke == nil then + + if _jtacUnit:getCoalition() == 1 then + _smoke = ctld.JTAC_smokeOn_RED + else + _smoke = ctld.JTAC_smokeOn_BLUE + end + end end - --remove from smoke list - ctld.jtacSmokeMarks[_tempUnitInfo.name] = nil - -- JTAC Unit: resume his route ------------ - trigger.action.groupContinueMoving(Group.getByName(_jtacGroupName)) + -- search for current unit - -- remove from target list - ctld.jtacCurrentTargets[_jtacGroupName] = nil + if _jtacUnit:isActive() == false then - --stop lasing - ctld.cancelLase(_jtacGroupName) - end + ctld.cleanupJTAC(_jtacGroupName) + ctld.logTrace(string.format("JTAC - LASE - [%s] - not active, scheduling JTACAutoLase in 30s at %s", ctld.p(_jtacGroupName), ctld.p(timer.getTime() + 30))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 30) - if _enemyUnit == nil then - if _defaultEnemyUnit ~= nil then - - -- store current target for easy lookup - ctld.jtacCurrentTargets[_jtacGroupName] = { name = _defaultEnemyUnit:getName(), unitType = _defaultEnemyUnit:getTypeName(), unitId = _defaultEnemyUnit:getID() } - - --add check for lasing or not - local action = ctld.i18n_translate("new target, ") - - if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - action = ctld.i18n_translate("standing by on %1", action) - else - action = ctld.i18n_translate("lasing %1", action) - end - - if wasSelected and targetLost then - action = ctld.i18n_translate(", temporarily %1", action) - else - action = ", " .. action - end - - if targetLost then - action = ctld.i18n_translate("target lost") .. action - elseif targetDestroyed then - action = ctld.i18n_translate("target destroyed") .. action - end - - if wasSelected then - action = ctld.i18n_translate(", selected %1", action) - elseif targetLost or targetDestroyed then - action = ", " .. action - end - wasSelected = false - targetDestroyed = false - targetLost = false - - local message = _jtacGroupName .. action .. _defaultEnemyUnit:getTypeName() - local fullMessage = message .. '. CODE: ' .. _laserCode .. ". POSITION: " .. ctld.getPositionString(_defaultEnemyUnit) - ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) - - -- JTAC Unit stop his route ----------------- - trigger.action.groupStopMoving(Group.getByName(_jtacGroupName)) -- stop JTAC - - -- create smoke - if _smoke == true then - - --create first smoke - ctld.createSmokeMarker(_defaultEnemyUnit, _colour) - end - end - end - - if _enemyUnit ~= nil and not ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - - local refreshDelay = 15 --delay in between JTACAutoLase scheduled calls when a target is tracked - local targetSpeedVec = _enemyUnit:getVelocity() - local targetSpeed = math.sqrt(targetSpeedVec.x^2+targetSpeedVec.y^2+targetSpeedVec.z^2) - local maxUpdateDist = 5 --maximum distance the unit will be allowed to travel before the lase spot is updated again - ctld.logTrace(string.format("targetSpeed=%s", ctld.p(targetSpeed))) - - ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) - - --if the target is going sufficiently fast for it to wander off futher than the maxUpdateDist, schedule laseUnit calls to update the lase spot only (we consider that the unit lives and drives on between JTACAutoLase calls) - if targetSpeed >= maxUpdateDist/refreshDelay then - local updateTimeStep = maxUpdateDist/targetSpeed --calculate the time step so that the target is never more than maxUpdateDist from it's last lased position - ctld.logTrace(string.format("JTAC - LASE - [%s] - target is moving at %s m/s, schedulting lasing steps every %ss", ctld.p(_jtacGroupName), ctld.p(targetSpeed), ctld.p(updateTimeStep))) - - local i = 1 - while i*updateTimeStep <= refreshDelay - updateTimeStep do --while the scheduled time for the laseUnit call isn't greater than the time between two JTACAutoLase() calls minus one time step (because at the next time step JTACAutoLase() should have been called and this in term also calls laseUnit()) - timer.scheduleFunction(ctld.timerLaseUnit,{_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode}, timer.getTime()+i*updateTimeStep) - i = i + 1 - end - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduled %s moving target lasing steps", ctld.p(_jtacGroupName), ctld.p(i))) + return end - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(refreshDelay), ctld.p(timer.getTime() + refreshDelay))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + refreshDelay) + local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) + --update targets list and store the next potential target if the selected one was lost + local _defaultEnemyUnit = ctld.findNearestVisibleEnemy(_jtacUnit, _lock) - if _smoke == true then - local _nextSmokeTime = ctld.jtacSmokeMarks[_enemyUnit:getName()] + -- if the JTAC sees a unit and a target was selected by users but is not the current unit, check if the selected target is in the targets list, if it is, then it's been reacquired + if _enemyUnit and ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 and ctld.jtacSelectedTarget[_jtacGroupName] ~= _enemyUnit:getName() then + for _,target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do + if target then + local targetUnit = target.unit + local targetName = targetUnit:getName() - --recreate smoke marker after 5 mins - if _nextSmokeTime ~= nil and _nextSmokeTime < timer.getTime() then + if ctld.jtacSelectedTarget[_jtacGroupName] == targetName then - ctld.createSmokeMarker(_enemyUnit, _colour) - end + ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = targetUnit:getTypeName(), unitId = targetUnit:getID() } + _enemyUnit = targetUnit + + local message = ctld.i18n_translate("%1, selected target reacquired, %2", _jtacGroupName, _enemyUnit:getTypeName()) + local fullMessage = message .. ctld.i18n_translate(". CODE: %1. POSITION: %2", _laserCode, ctld.getPositionString(_enemyUnit)) + ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) + end + end + end end - else - ctld.logDebug(string.format("JTAC - MODE - [%s] - No Enemies Nearby / Standby mode", ctld.p(_jtacGroupName))) + local targetDestroyed = false + local targetLost = false + local wasSelected = false - -- stop lazing the old spot - ctld.logDebug(string.format("JTAC - LASE - [%s] - canceling lasing of the old spot", ctld.p(_jtacGroupName))) - ctld.cancelLase(_jtacGroupName) + if _enemyUnit == nil and ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(5), ctld.p(timer.getTime() + 5))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 5) - end + local _tempUnitInfo = ctld.jtacCurrentTargets[_jtacGroupName] - local action = ", " - if wasSelected then - action = action .. "selected " - end + -- env.info("TEMP UNIT INFO: " .. tempUnitInfo.name .. " " .. tempUnitInfo.unitType) - if targetLost then - ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target lost.", _jtacGroupName , action), 10, _jtacUnit:getCoalition(), _radio) - elseif targetDestroyed then - ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target destroyed.", _jtacGroupName , action), 10, _jtacUnit:getCoalition(), _radio) - end + local _tempUnit = Unit.getByName(_tempUnitInfo.name) + + wasSelected = (ctld.jtacCurrentTargets[_jtacGroupName].name == ctld.jtacSelectedTarget[_jtacGroupName]) + + if _tempUnit ~= nil and _tempUnit:getLife() > 0 and _tempUnit:isActive() == true then + targetLost = true + else + targetDestroyed = true + ctld.jtacSelectedTarget[_jtacGroupName] = 1 + end + + --remove from smoke list + ctld.jtacSmokeMarks[_tempUnitInfo.name] = nil + + -- JTAC Unit: resume his route ------------ + trigger.action.groupContinueMoving(Group.getByName(_jtacGroupName)) + + -- remove from target list + ctld.jtacCurrentTargets[_jtacGroupName] = nil + + --stop lasing + ctld.cancelLase(_jtacGroupName) + end + + + if _enemyUnit == nil then + if _defaultEnemyUnit ~= nil then + + -- store current target for easy lookup + ctld.jtacCurrentTargets[_jtacGroupName] = { name = _defaultEnemyUnit:getName(), unitType = _defaultEnemyUnit:getTypeName(), unitId = _defaultEnemyUnit:getID() } + + --add check for lasing or not + local action = ctld.i18n_translate("new target, ") + + if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then + action = ctld.i18n_translate("standing by on %1", action) + else + action = ctld.i18n_translate("lasing %1", action) + end + + if wasSelected and targetLost then + action = ctld.i18n_translate(", temporarily %1", action) + else + action = ", " .. action + end + + if targetLost then + action = ctld.i18n_translate("target lost") .. action + elseif targetDestroyed then + action = ctld.i18n_translate("target destroyed") .. action + end + + if wasSelected then + action = ctld.i18n_translate(", selected %1", action) + elseif targetLost or targetDestroyed then + action = ", " .. action + end + wasSelected = false + targetDestroyed = false + targetLost = false + + local message = _jtacGroupName .. action .. _defaultEnemyUnit:getTypeName() + local fullMessage = message .. '. CODE: ' .. _laserCode .. ". POSITION: " .. ctld.getPositionString(_defaultEnemyUnit) + ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) + + -- JTAC Unit stop his route ----------------- + trigger.action.groupStopMoving(Group.getByName(_jtacGroupName)) -- stop JTAC + + -- create smoke + if _smoke == true then + + --create first smoke + ctld.createSmokeMarker(_defaultEnemyUnit, _colour) + end + end + end + + if _enemyUnit ~= nil and not ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then + + local refreshDelay = 15 --delay in between JTACAutoLase scheduled calls when a target is tracked + local targetSpeedVec = _enemyUnit:getVelocity() + local targetSpeed = math.sqrt(targetSpeedVec.x^2+targetSpeedVec.y^2+targetSpeedVec.z^2) + local maxUpdateDist = 5 --maximum distance the unit will be allowed to travel before the lase spot is updated again + ctld.logTrace(string.format("targetSpeed=%s", ctld.p(targetSpeed))) + + ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) + + --if the target is going sufficiently fast for it to wander off futher than the maxUpdateDist, schedule laseUnit calls to update the lase spot only (we consider that the unit lives and drives on between JTACAutoLase calls) + if targetSpeed >= maxUpdateDist/refreshDelay then + local updateTimeStep = maxUpdateDist/targetSpeed --calculate the time step so that the target is never more than maxUpdateDist from it's last lased position + ctld.logTrace(string.format("JTAC - LASE - [%s] - target is moving at %s m/s, schedulting lasing steps every %ss", ctld.p(_jtacGroupName), ctld.p(targetSpeed), ctld.p(updateTimeStep))) + + local i = 1 + while i*updateTimeStep <= refreshDelay - updateTimeStep do --while the scheduled time for the laseUnit call isn't greater than the time between two JTACAutoLase() calls minus one time step (because at the next time step JTACAutoLase() should have been called and this in term also calls laseUnit()) + timer.scheduleFunction(ctld.timerLaseUnit,{_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode}, timer.getTime()+i*updateTimeStep) + i = i + 1 + end + ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduled %s moving target lasing steps", ctld.p(_jtacGroupName), ctld.p(i))) + end + + ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(refreshDelay), ctld.p(timer.getTime() + refreshDelay))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + refreshDelay) + + if _smoke == true then + local _nextSmokeTime = ctld.jtacSmokeMarks[_enemyUnit:getName()] + + --recreate smoke marker after 5 mins + if _nextSmokeTime ~= nil and _nextSmokeTime < timer.getTime() then + + ctld.createSmokeMarker(_enemyUnit, _colour) + end + end + + else + ctld.logDebug(string.format("JTAC - MODE - [%s] - No Enemies Nearby / Standby mode", ctld.p(_jtacGroupName))) + + -- stop lazing the old spot + ctld.logDebug(string.format("JTAC - LASE - [%s] - canceling lasing of the old spot", ctld.p(_jtacGroupName))) + ctld.cancelLase(_jtacGroupName) + + ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(5), ctld.p(timer.getTime() + 5))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 5) + end + + local action = ", " + if wasSelected then + action = action .. "selected " + end + + if targetLost then + ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target lost.", _jtacGroupName , action), 10, _jtacUnit:getCoalition(), _radio) + elseif targetDestroyed then + ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target destroyed.", _jtacGroupName , action), 10, _jtacUnit:getCoalition(), _radio) + end end function ctld.JTACAutoLaseStop(_jtacGroupName) - ctld.jtacStop[_jtacGroupName] = true + ctld.jtacStop[_jtacGroupName] = true end -- used by the timer function function ctld.timerJTACAutoLase(_args) - ctld.JTACAutoLase(_args[1], _args[2], _args[3], _args[4], _args[5], _args[6]) + ctld.JTACAutoLase(_args[1], _args[2], _args[3], _args[4], _args[5], _args[6]) end function ctld.cleanupJTAC(_jtacGroupName) - -- clear laser - just in case - ctld.cancelLase(_jtacGroupName) + -- clear laser - just in case + ctld.cancelLase(_jtacGroupName) - -- Cleanup - ctld.jtacCurrentTargets[_jtacGroupName] = nil + -- Cleanup + ctld.jtacCurrentTargets[_jtacGroupName] = nil - ctld.jtacTargetsList[_jtacGroupName] = nil + ctld.jtacTargetsList[_jtacGroupName] = nil - ctld.jtacSelectedTarget[_jtacGroupName] = nil + ctld.jtacSelectedTarget[_jtacGroupName] = nil - for _,_specialOption in pairs(ctld.jtacSpecialOptions) do --delete jtac specific settings for all special options - if _specialOption.jtacs then - _specialOption.jtacs[_jtacGroupName] = nil - end - end - - ctld.jtacRadioData[_jtacGroupName] = nil - - --remove the JTAC's group submenu and all of the target pages it potentially contained if the JTAC has or had a menu - if ctld.jtacUnits[_jtacGroupName] and ctld.jtacUnits[_jtacGroupName].side and ctld.jtacGroupSubMenuPath[_jtacGroupName] then - local _players = coalition.getPlayers(ctld.jtacUnits[_jtacGroupName].side) - - if _players ~= nil then - - for _, _playerUnit in pairs(_players) do - - local _groupId = ctld.getGroupId(_playerUnit) - - if _groupId then - missionCommands.removeItemForGroup(_groupId, ctld.jtacGroupSubMenuPath[_jtacGroupName]) + for _,_specialOption in pairs(ctld.jtacSpecialOptions) do --delete jtac specific settings for all special options + if _specialOption.jtacs then + _specialOption.jtacs[_jtacGroupName] = nil end - end end - end - ctld.jtacUnits[_jtacGroupName] = nil + ctld.jtacRadioData[_jtacGroupName] = nil - ctld.jtacGroupSubMenuPath[_jtacGroupName] = nil + --remove the JTAC's group submenu and all of the target pages it potentially contained if the JTAC has or had a menu + if ctld.jtacUnits[_jtacGroupName] and ctld.jtacUnits[_jtacGroupName].side and ctld.jtacGroupSubMenuPath[_jtacGroupName] then + local _players = coalition.getPlayers(ctld.jtacUnits[_jtacGroupName].side) + + if _players ~= nil then + + for _, _playerUnit in pairs(_players) do + + local _groupId = ctld.getGroupId(_playerUnit) + + if _groupId then + missionCommands.removeItemForGroup(_groupId, ctld.jtacGroupSubMenuPath[_jtacGroupName]) + end + end + end + end + + ctld.jtacUnits[_jtacGroupName] = nil + + ctld.jtacGroupSubMenuPath[_jtacGroupName] = nil end @@ -6864,378 +6885,378 @@ end --- if _radio is set, the message will be read out loud via SRS function ctld.notifyCoalition(_message, _displayFor, _side, _radio, _shortMessage) - trigger.action.outTextForCoalition(_side, _message, _displayFor) + trigger.action.outTextForCoalition(_side, _message, _displayFor) - local _shortMessage = _shortMessage - if _shortMessage == nil then - _shortMessage = _message - end + local _shortMessage = _shortMessage + if _shortMessage == nil then + _shortMessage = _message + end - if STTS and STTS.TextToSpeech and _radio and _radio.freq then - local _freq = _radio.freq - local _modulation = _radio.mod or "FM" - local _volume = _radio.volume or "1.0" - local _name = _radio.name or "JTAC" - local _gender = _radio.gender or "male" - local _culture = _radio.culture or "en-US" - local _voice = _radio.voice - local _googleTTS = _radio.googleTTS or false - STTS.TextToSpeech(_shortMessage, _freq, _modulation, _volume, _name, _side, nil, 1, _gender, _culture, _voice, _googleTTS) - else - trigger.action.outSoundForCoalition(_side, "radiobeep.ogg") - end + if STTS and STTS.TextToSpeech and _radio and _radio.freq then + local _freq = _radio.freq + local _modulation = _radio.mod or "FM" + local _volume = _radio.volume or "1.0" + local _name = _radio.name or "JTAC" + local _gender = _radio.gender or "male" + local _culture = _radio.culture or "en-US" + local _voice = _radio.voice + local _googleTTS = _radio.googleTTS or false + STTS.TextToSpeech(_shortMessage, _freq, _modulation, _volume, _name, _side, nil, 1, _gender, _culture, _voice, _googleTTS) + else + trigger.action.outSoundForCoalition(_side, "radiobeep.ogg") + end end function ctld.createSmokeMarker(_enemyUnit, _colour) - --recreate in 5 mins - ctld.jtacSmokeMarks[_enemyUnit:getName()] = timer.getTime() + 300.0 + --recreate in 5 mins + ctld.jtacSmokeMarks[_enemyUnit:getName()] = timer.getTime() + 300.0 - local _enemyPoint = _enemyUnit:getPoint() - trigger.action.smoke({ x = _enemyPoint.x + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_x, y = _enemyPoint.y + ctld.JTAC_smokeOffset_y, z = _enemyPoint.z + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_z }, _colour) + local _enemyPoint = _enemyUnit:getPoint() + trigger.action.smoke({ x = _enemyPoint.x + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_x, y = _enemyPoint.y + ctld.JTAC_smokeOffset_y, z = _enemyPoint.z + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_z }, _colour) end function ctld.cancelLase(_jtacGroupName) - --local index = "JTAC_"..jtacUnit:getID() + --local index = "JTAC_"..jtacUnit:getID() - local _tempLase = ctld.jtacLaserPoints[_jtacGroupName] + local _tempLase = ctld.jtacLaserPoints[_jtacGroupName] - if _tempLase ~= nil then - Spot.destroy(_tempLase) - ctld.jtacLaserPoints[_jtacGroupName] = nil + if _tempLase ~= nil then + Spot.destroy(_tempLase) + ctld.jtacLaserPoints[_jtacGroupName] = nil - -- env.info('Destroy laze '..index) + -- env.info('Destroy laze '..index) - _tempLase = nil - end + _tempLase = nil + end - local _tempIR = ctld.jtacIRPoints[_jtacGroupName] + local _tempIR = ctld.jtacIRPoints[_jtacGroupName] - if _tempIR ~= nil then - Spot.destroy(_tempIR) - ctld.jtacIRPoints[_jtacGroupName] = nil + if _tempIR ~= nil then + Spot.destroy(_tempIR) + ctld.jtacIRPoints[_jtacGroupName] = nil - -- env.info('Destroy laze '..index) + -- env.info('Destroy laze '..index) - _tempIR = nil - end + _tempIR = nil + end end -- used by the timer function function ctld.timerLaseUnit(_args) - ctld.laseUnit(_args[1], _args[2], _args[3], _args[4]) + ctld.laseUnit(_args[1], _args[2], _args[3], _args[4]) end function ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) - --cancelLase(jtacGroupName) - ctld.logTrace("ctld.laseUnit()") + --cancelLase(jtacGroupName) + ctld.logTrace("ctld.laseUnit()") - local _spots = {} + local _spots = {} - if _enemyUnit:isExist() then - local _enemyVector = _enemyUnit:getPoint() - local _enemyVectorUpdated = { x = _enemyVector.x, y = _enemyVector.y + 2.0, z = _enemyVector.z } + if _enemyUnit:isExist() then + local _enemyVector = _enemyUnit:getPoint() + local _enemyVectorUpdated = { x = _enemyVector.x, y = _enemyVector.y + 2.0, z = _enemyVector.z } - if ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] then - local _enemySpeedVector = _enemyUnit:getVelocity() - ctld.logTrace(string.format("_enemySpeedVector=%s", ctld.p(_enemySpeedVector))) + if ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] then + local _enemySpeedVector = _enemyUnit:getVelocity() + ctld.logTrace(string.format("_enemySpeedVector=%s", ctld.p(_enemySpeedVector))) - local _WindSpeedVector = atmosphere.getWind(_enemyVectorUpdated) - ctld.logTrace(string.format("_WindSpeedVector=%s", ctld.p(_WindSpeedVector))) + local _WindSpeedVector = atmosphere.getWind(_enemyVectorUpdated) + ctld.logTrace(string.format("_WindSpeedVector=%s", ctld.p(_WindSpeedVector))) - --if target speed is greater than 0, calculated using absolute value norm - if math.abs(_enemySpeedVector.x) + math.abs(_enemySpeedVector.y) + math.abs(_enemySpeedVector.z) > 0 then - local CorrectionFactor = 1 --correction factor in seconds applied to the target speed components to determine the lasing spot for a direct hit on a moving vehicle + --if target speed is greater than 0, calculated using absolute value norm + if math.abs(_enemySpeedVector.x) + math.abs(_enemySpeedVector.y) + math.abs(_enemySpeedVector.z) > 0 then + local CorrectionFactor = 1 --correction factor in seconds applied to the target speed components to determine the lasing spot for a direct hit on a moving vehicle - --correct in the direction of the movement - _enemyVectorUpdated.x = _enemyVectorUpdated.x + _enemySpeedVector.x * CorrectionFactor - _enemyVectorUpdated.y = _enemyVectorUpdated.y + _enemySpeedVector.y * CorrectionFactor - _enemyVectorUpdated.z = _enemyVectorUpdated.z + _enemySpeedVector.z * CorrectionFactor - end + --correct in the direction of the movement + _enemyVectorUpdated.x = _enemyVectorUpdated.x + _enemySpeedVector.x * CorrectionFactor + _enemyVectorUpdated.y = _enemyVectorUpdated.y + _enemySpeedVector.y * CorrectionFactor + _enemyVectorUpdated.z = _enemyVectorUpdated.z + _enemySpeedVector.z * CorrectionFactor + end - --if wind speed is greater than 0, calculated using absolute value norm - if math.abs(_WindSpeedVector.x) + math.abs(_WindSpeedVector.y) + math.abs(_WindSpeedVector.z) > 0 then - local CorrectionFactor = 1.05 --correction factor in seconds applied to the wind speed components to determine the lasing spot for a direct hit in adverse conditions + --if wind speed is greater than 0, calculated using absolute value norm + if math.abs(_WindSpeedVector.x) + math.abs(_WindSpeedVector.y) + math.abs(_WindSpeedVector.z) > 0 then + local CorrectionFactor = 1.05 --correction factor in seconds applied to the wind speed components to determine the lasing spot for a direct hit in adverse conditions - --correct to the opposite of the wind direction - _enemyVectorUpdated.x = _enemyVectorUpdated.x - _WindSpeedVector.x * CorrectionFactor - _enemyVectorUpdated.y = _enemyVectorUpdated.y - _WindSpeedVector.y * CorrectionFactor --not sure about correcting altitude but that component is always 0 in testing - _enemyVectorUpdated.z = _enemyVectorUpdated.z - _WindSpeedVector.z * CorrectionFactor - end - --combination of both should result in near perfect accuracy if the bomb doesn't stall itself following fast vehicles or correcting for heavy winds, correction factors can be adjusted but should work up to 40kn of wind for vehicles moving at 90kph (beware to drop the bomb in a way to not stall it, facing which ever is larger, target speed or wind) - end - - local _oldLase = ctld.jtacLaserPoints[_jtacGroupName] - local _oldIR = ctld.jtacIRPoints[_jtacGroupName] - - if _oldLase == nil or _oldIR == nil then - - -- create lase - - local _status, _result = pcall(function() - _spots['irPoint'] = Spot.createInfraRed(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated) - _spots['laserPoint'] = Spot.createLaser(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated, _laserCode) - return _spots - end) - - if not _status then - env.error('ERROR: ' .. _result, false) - else - if _result.irPoint then - - -- env.info(jtacUnit:getName() .. ' placed IR Pointer on '..enemyUnit:getName()) - - ctld.jtacIRPoints[_jtacGroupName] = _result.irPoint --store so we can remove after + --correct to the opposite of the wind direction + _enemyVectorUpdated.x = _enemyVectorUpdated.x - _WindSpeedVector.x * CorrectionFactor + _enemyVectorUpdated.y = _enemyVectorUpdated.y - _WindSpeedVector.y * CorrectionFactor --not sure about correcting altitude but that component is always 0 in testing + _enemyVectorUpdated.z = _enemyVectorUpdated.z - _WindSpeedVector.z * CorrectionFactor + end + --combination of both should result in near perfect accuracy if the bomb doesn't stall itself following fast vehicles or correcting for heavy winds, correction factors can be adjusted but should work up to 40kn of wind for vehicles moving at 90kph (beware to drop the bomb in a way to not stall it, facing which ever is larger, target speed or wind) end - if _result.laserPoint then - -- env.info(jtacUnit:getName() .. ' is Lasing '..enemyUnit:getName()..'. CODE:'..laserCode) + local _oldLase = ctld.jtacLaserPoints[_jtacGroupName] + local _oldIR = ctld.jtacIRPoints[_jtacGroupName] - ctld.jtacLaserPoints[_jtacGroupName] = _result.laserPoint + if _oldLase == nil or _oldIR == nil then + + -- create lase + + local _status, _result = pcall(function() + _spots['irPoint'] = Spot.createInfraRed(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated) + _spots['laserPoint'] = Spot.createLaser(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated, _laserCode) + return _spots + end) + + if not _status then + env.error('ERROR: ' .. _result, false) + else + if _result.irPoint then + + -- env.info(jtacUnit:getName() .. ' placed IR Pointer on '..enemyUnit:getName()) + + ctld.jtacIRPoints[_jtacGroupName] = _result.irPoint --store so we can remove after + end + if _result.laserPoint then + + -- env.info(jtacUnit:getName() .. ' is Lasing '..enemyUnit:getName()..'. CODE:'..laserCode) + + ctld.jtacLaserPoints[_jtacGroupName] = _result.laserPoint + end + end + + else + + -- update lase + + if _oldLase ~= nil then + _oldLase:setPoint(_enemyVectorUpdated) + end + + if _oldIR ~= nil then + _oldIR:setPoint(_enemyVectorUpdated) + end end - end - - else - - -- update lase - - if _oldLase ~= nil then - _oldLase:setPoint(_enemyVectorUpdated) - end - - if _oldIR ~= nil then - _oldIR:setPoint(_enemyVectorUpdated) - end end - end end -- get currently selected unit and check they're still in range function ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) - local _unit = nil + local _unit = nil - if ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then - _unit = Unit.getByName(ctld.jtacCurrentTargets[_jtacGroupName].name) - end - - local _tempPoint = nil - local _tempDist = nil - local _tempPosition = nil - - local _jtacPosition = _jtacUnit:getPosition() - local _jtacPoint = _jtacUnit:getPoint() - - if _unit ~= nil and _unit:getLife() > 0 and _unit:isActive() == true then - - -- calc distance - _tempPoint = _unit:getPoint() - -- tempPosition = unit:getPosition() - - _tempDist = ctld.getDistance(_unit:getPoint(), _jtacUnit:getPoint()) - if _tempDist < ctld.JTAC_maxDistance then - -- calc visible - - -- check slightly above the target as rounding errors can cause issues, plus the unit has some height anyways - local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } - local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } - - if land.isVisible(_offsetEnemyPos, _offsetJTACPos) then - return _unit - end + if ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then + _unit = Unit.getByName(ctld.jtacCurrentTargets[_jtacGroupName].name) end - end - return nil + + local _tempPoint = nil + local _tempDist = nil + local _tempPosition = nil + + local _jtacPosition = _jtacUnit:getPosition() + local _jtacPoint = _jtacUnit:getPoint() + + if _unit ~= nil and _unit:getLife() > 0 and _unit:isActive() == true then + + -- calc distance + _tempPoint = _unit:getPoint() + -- tempPosition = unit:getPosition() + + _tempDist = ctld.getDistance(_unit:getPoint(), _jtacUnit:getPoint()) + if _tempDist < ctld.JTAC_maxDistance then + -- calc visible + + -- check slightly above the target as rounding errors can cause issues, plus the unit has some height anyways + local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } + local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } + + if land.isVisible(_offsetEnemyPos, _offsetJTACPos) then + return _unit + end + end + end + return nil end -- Find nearest enemy to JTAC that isn't blocked by terrain function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType,_distance) - --local startTime = os.clock() + --local startTime = os.clock() - local _maxDistance = _distance or ctld.JTAC_maxDistance + local _maxDistance = _distance or ctld.JTAC_maxDistance - local _nearestDistance = _maxDistance + local _nearestDistance = _maxDistance - local _jtacGroupName = _jtacUnit:getGroup():getName() - local _jtacPoint = _jtacUnit:getPoint() - local _coa = _jtacUnit:getCoalition() + local _jtacGroupName = _jtacUnit:getGroup():getName() + local _jtacPoint = _jtacUnit:getPoint() + local _coa = _jtacUnit:getCoalition() - local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } + local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } - local _volume = { - id = world.VolumeType.SPHERE, - params = { - point = _offsetJTACPos, - radius = _maxDistance + local _volume = { + id = world.VolumeType.SPHERE, + params = { + point = _offsetJTACPos, + radius = _maxDistance + } } - } - local _unitList = {} + local _unitList = {} - local _search = function(_unit, _coa) - pcall(function() + local _search = function(_unit, _coa) + pcall(function() - if _unit ~= nil - and _unit:getLife() > 0 - and _unit:isActive() - and _unit:getCoalition() ~= _coa - and not _unit:inAir() - and not ctld.alreadyTarget(_jtacUnit,_unit) then + if _unit ~= nil + and _unit:getLife() > 0 + and _unit:isActive() + and _unit:getCoalition() ~= _coa + and not _unit:inAir() + and not ctld.alreadyTarget(_jtacUnit,_unit) then - local _tempPoint = _unit:getPoint() - local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } + local _tempPoint = _unit:getPoint() + local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } - if land.isVisible(_offsetJTACPos,_offsetEnemyPos ) then + if land.isVisible(_offsetJTACPos,_offsetEnemyPos ) then - local _dist = ctld.getDistance(_offsetJTACPos, _offsetEnemyPos) + local _dist = ctld.getDistance(_offsetJTACPos, _offsetEnemyPos) - if _dist < _maxDistance then - table.insert(_unitList,{unit=_unit, dist=_dist}) + if _dist < _maxDistance then + table.insert(_unitList,{unit=_unit, dist=_dist}) - end + end + end + end + end) + + return true + end + + world.searchObjects(Object.Category.UNIT, _volume, _search, _coa) + + --log.info(string.format("JTAC Search elapsed time: %.4f\n", os.clock() - startTime)) + + -- generate list order by distance & visible + + -- first check + -- hpriority + -- priority + -- vehicle + -- unit + + + ctld.jtacTargetsList[_jtacGroupName] = _unitList + --from the units in range, build the targets list, unsorted as to keep consistency between radio menu refreshes + + local _sort = function( a,b ) return a.dist < b.dist end + table.sort(_unitList,_sort) + -- sort list + + -- check for hpriority + for _, _enemyUnit in ipairs(_unitList) do + local _enemyName = _enemyUnit.unit:getName() + + if string.match(_enemyName, "hpriority") then + return _enemyUnit.unit end - end - end) - - return true - end - - world.searchObjects(Object.Category.UNIT, _volume, _search, _coa) - - --log.info(string.format("JTAC Search elapsed time: %.4f\n", os.clock() - startTime)) - - -- generate list order by distance & visible - - -- first check - -- hpriority - -- priority - -- vehicle - -- unit - - - ctld.jtacTargetsList[_jtacGroupName] = _unitList - --from the units in range, build the targets list, unsorted as to keep consistency between radio menu refreshes - - local _sort = function( a,b ) return a.dist < b.dist end - table.sort(_unitList,_sort) - -- sort list - - -- check for hpriority - for _, _enemyUnit in ipairs(_unitList) do - local _enemyName = _enemyUnit.unit:getName() - - if string.match(_enemyName, "hpriority") then - return _enemyUnit.unit end - end - for _, _enemyUnit in ipairs(_unitList) do - local _enemyName = _enemyUnit.unit:getName() + for _, _enemyUnit in ipairs(_unitList) do + local _enemyName = _enemyUnit.unit:getName() - if string.match(_enemyName, "priority") then - return _enemyUnit.unit + if string.match(_enemyName, "priority") then + return _enemyUnit.unit + end end - end - local result = nil - for _, _enemyUnit in ipairs(_unitList) do - local _enemyName = _enemyUnit.unit:getName() - --log.info(string.format("CTLD - checking _enemyName=%s", _enemyName)) + local result = nil + for _, _enemyUnit in ipairs(_unitList) do + local _enemyName = _enemyUnit.unit:getName() + --log.info(string.format("CTLD - checking _enemyName=%s", _enemyName)) - -- check for air defenses - --log.info(string.format("CTLD - _enemyUnit.unit:getDesc()[attributes]=%s", ctld.p(_enemyUnit.unit:getDesc()["attributes"]))) - local airdefense = (_enemyUnit.unit:getDesc()["attributes"]["Air Defence"] ~= nil) - --log.info(string.format("CTLD - airdefense=%s", tostring(airdefense))) + -- check for air defenses + --log.info(string.format("CTLD - _enemyUnit.unit:getDesc()[attributes]=%s", ctld.p(_enemyUnit.unit:getDesc()["attributes"]))) + local airdefense = (_enemyUnit.unit:getDesc()["attributes"]["Air Defence"] ~= nil) + --log.info(string.format("CTLD - airdefense=%s", tostring(airdefense))) - if (_targetType == "vehicle" and ctld.isVehicle(_enemyUnit.unit)) or _targetType == "all" then - if airdefense then - return _enemyUnit.unit - else - result = _enemyUnit.unit - end + if (_targetType == "vehicle" and ctld.isVehicle(_enemyUnit.unit)) or _targetType == "all" then + if airdefense then + return _enemyUnit.unit + else + result = _enemyUnit.unit + end - elseif (_targetType == "troop" and ctld.isInfantry(_enemyUnit.unit)) or _targetType == "all" then - if airdefense then - return _enemyUnit.unit - else - result = _enemyUnit.unit - end + elseif (_targetType == "troop" and ctld.isInfantry(_enemyUnit.unit)) or _targetType == "all" then + if airdefense then + return _enemyUnit.unit + else + result = _enemyUnit.unit + end + end end - end - return result + return result end function ctld.listNearbyEnemies(_jtacUnit) - local _maxDistance = ctld.JTAC_maxDistance + local _maxDistance = ctld.JTAC_maxDistance - local _jtacPoint = _jtacUnit:getPoint() - local _coa = _jtacUnit:getCoalition() + local _jtacPoint = _jtacUnit:getPoint() + local _coa = _jtacUnit:getCoalition() - local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } + local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } - local _volume = { - id = world.VolumeType.SPHERE, - params = { - point = _offsetJTACPos, - radius = _maxDistance + local _volume = { + id = world.VolumeType.SPHERE, + params = { + point = _offsetJTACPos, + radius = _maxDistance + } } - } - local _enemies = nil + local _enemies = nil - local _search = function(_unit, _coa) - pcall(function() + local _search = function(_unit, _coa) + pcall(function() - if _unit ~= nil - and _unit:getLife() > 0 - and _unit:isActive() - and _unit:getCoalition() ~= _coa - and not _unit:inAir() then + if _unit ~= nil + and _unit:getLife() > 0 + and _unit:isActive() + and _unit:getCoalition() ~= _coa + and not _unit:inAir() then - local _tempPoint = _unit:getPoint() - local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } + local _tempPoint = _unit:getPoint() + local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } - if land.isVisible(_offsetJTACPos,_offsetEnemyPos ) then + if land.isVisible(_offsetJTACPos,_offsetEnemyPos ) then - if not _enemies then - _enemies = {} - end + if not _enemies then + _enemies = {} + end - _enemies[_unit:getTypeName()] = _unit:getTypeName() + _enemies[_unit:getTypeName()] = _unit:getTypeName() - end - end - end) + end + end + end) - return true - end + return true + end - world.searchObjects(Object.Category.UNIT, _volume, _search, _coa) + world.searchObjects(Object.Category.UNIT, _volume, _search, _coa) - return _enemies + return _enemies end -- tests whether the unit is targeted by another JTAC function ctld.alreadyTarget(_jtacUnit, _enemyUnit) - for _, _jtacTarget in pairs(ctld.jtacCurrentTargets) do + for _, _jtacTarget in pairs(ctld.jtacCurrentTargets) do - if _jtacTarget.unitId == _enemyUnit:getID() then - -- env.info("ALREADY TARGET") - return true + if _jtacTarget.unitId == _enemyUnit:getID() then + -- env.info("ALREADY TARGET") + return true + end end - end - return false + return false end @@ -7243,367 +7264,367 @@ end function ctld.getGroup(groupName) - local _group = Group.getByName(groupName) + local _group = Group.getByName(groupName) - local _filteredUnits = {} --contains alive units - local _x = 1 + local _filteredUnits = {} --contains alive units + local _x = 1 - if _group ~= nil then - ctld.logTrace(string.format("ctld.getGroup - %s - group ~= nil", ctld.p(groupName))) - if _group:isExist() then - ctld.logTrace(string.format("ctld.getGroup - %s - group:isExist()", ctld.p(groupName))) - local _groupUnits = _group:getUnits() + if _group ~= nil then + ctld.logTrace(string.format("ctld.getGroup - %s - group ~= nil", ctld.p(groupName))) + if _group:isExist() then + ctld.logTrace(string.format("ctld.getGroup - %s - group:isExist()", ctld.p(groupName))) + local _groupUnits = _group:getUnits() - if _groupUnits ~= nil and #_groupUnits > 0 then - ctld.logTrace(string.format("ctld.getGroup - %s - group has %s units", ctld.p(groupName), ctld.p(#_groupUnits))) - for _x = 1, #_groupUnits do - if _groupUnits[_x]:getLife() > 0 then -- removed and _groupUnits[_x]:isExist() as isExist doesnt work on single units! - table.insert(_filteredUnits, _groupUnits[_x]) - else - ctld.logTrace(string.format("ctld.getGroup - %s - dead unit %s", ctld.p(groupName), ctld.p(_groupUnits[_x]:getName()))) - end + if _groupUnits ~= nil and #_groupUnits > 0 then + ctld.logTrace(string.format("ctld.getGroup - %s - group has %s units", ctld.p(groupName), ctld.p(#_groupUnits))) + for _x = 1, #_groupUnits do + if _groupUnits[_x]:getLife() > 0 then -- removed and _groupUnits[_x]:isExist() as isExist doesnt work on single units! + table.insert(_filteredUnits, _groupUnits[_x]) + else + ctld.logTrace(string.format("ctld.getGroup - %s - dead unit %s", ctld.p(groupName), ctld.p(_groupUnits[_x]:getName()))) + end + end + end end - end end - end - return _filteredUnits + return _filteredUnits end function ctld.getAliveGroup(_groupName) - local _group = Group.getByName(_groupName) + local _group = Group.getByName(_groupName) - if _group and _group:isExist() == true and #_group:getUnits() > 0 then - return _group - end + if _group and _group:isExist() == true and #_group:getUnits() > 0 then + return _group + end - return nil + return nil end -- gets the JTAC status and displays to coalition units function ctld.getJTACStatus(_args) - --returns the status of all JTAC units unless the status of a single JTAC is asked for (by inserting it's groupName in _args[2]) + --returns the status of all JTAC units unless the status of a single JTAC is asked for (by inserting it's groupName in _args[2]) - local _playerUnit = ctld.getTransportUnit(_args[1]) - local _singleJtacGroupName = _args[2] + local _playerUnit = ctld.getTransportUnit(_args[1]) + local _singleJtacGroupName = _args[2] - if _playerUnit == nil and _singleJtacGroupName == nil then - return - end - - local _side = nil - - if _playerUnit == nil then - _side = ctld.jtacUnits[_singleJtacGroupName].side - else - _side = _playerUnit:getCoalition() - end - - local _jtacUnit = nil - local hasJTAC = false - local _message = ctld.i18n_translate("JTAC STATUS: \n\n") - - for _jtacGroupName, _jtacDetails in pairs(ctld.jtacUnits) do - - --look up units - if _singleJtacGroupName == nil or (_singleJtacGroupName and _singleJtacGroupName == _jtacGroupName) then --if the status of a single JTAC or if the status of a single JTAC was asked and this is the correct JTAC we're going over in the loop - _jtacUnit = Unit.getByName(_jtacDetails.name) - - if _jtacUnit ~= nil and _jtacUnit:getLife() > 0 and _jtacUnit:isActive() == true and _jtacUnit:getCoalition() == _side then - - hasJTAC = true - - local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) - - local _laserCode = ctld.jtacLaserPointCodes[_jtacGroupName] - - local _start = "->" .. _jtacGroupName - if (_jtacDetails.radio) then - _start = _start .. ctld.i18n_translate(", available on %1 %2,", _jtacDetails.radio.freq, _jtacDetails.radio.mod) - end - - if _laserCode == nil then - _laserCode = ctld.i18n_translate("UNKNOWN") - end - - if _enemyUnit ~= nil and _enemyUnit:getLife() > 0 and _enemyUnit:isActive() == true then - - local action = ctld.i18n_translate(" targeting ") - - if ctld.jtacSelectedTarget[_jtacGroupName] == _enemyUnit:getName() then - action = ctld.i18n_translate(" targeting selected unit ") - else - if ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then - action = ctld.i18n_translate(" attempting to find selected unit, temporarily targeting ") - end - end - - if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - action = action .. ctld.i18n_translate("(Laser OFF) ") - end - - _message = _message .. "" .. _start .. action .. _enemyUnit:getTypeName() .. " CODE: " .. _laserCode .. ctld.getPositionString(_enemyUnit) .. "\n" - - local _list = ctld.listNearbyEnemies(_jtacUnit) - - if _list then - _message = _message .. ctld.i18n_translate("Visual On: ") - - for _,_type in pairs(_list) do - _message = _message.._type..", " - end - _message = _message.."\n" - end - - else - _message = _message .. "" .. _start .. ctld.i18n_translate(" searching for targets %1\n", ctld.getPositionString(_jtacUnit)) - end - end + if _playerUnit == nil and _singleJtacGroupName == nil then + return end - end - if not hasJTAC then - ctld.notifyCoalition(ctld.i18n_translate("No Active JTACs"), 10, _side) - else - ctld.notifyCoalition(_message, 10, _side) - end + local _side = nil + + if _playerUnit == nil then + _side = ctld.jtacUnits[_singleJtacGroupName].side + else + _side = _playerUnit:getCoalition() + end + + local _jtacUnit = nil + local hasJTAC = false + local _message = ctld.i18n_translate("JTAC STATUS: \n\n") + + for _jtacGroupName, _jtacDetails in pairs(ctld.jtacUnits) do + + --look up units + if _singleJtacGroupName == nil or (_singleJtacGroupName and _singleJtacGroupName == _jtacGroupName) then --if the status of a single JTAC or if the status of a single JTAC was asked and this is the correct JTAC we're going over in the loop + _jtacUnit = Unit.getByName(_jtacDetails.name) + + if _jtacUnit ~= nil and _jtacUnit:getLife() > 0 and _jtacUnit:isActive() == true and _jtacUnit:getCoalition() == _side then + + hasJTAC = true + + local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) + + local _laserCode = ctld.jtacLaserPointCodes[_jtacGroupName] + + local _start = "->" .. _jtacGroupName + if (_jtacDetails.radio) then + _start = _start .. ctld.i18n_translate(", available on %1 %2,", _jtacDetails.radio.freq, _jtacDetails.radio.mod) + end + + if _laserCode == nil then + _laserCode = ctld.i18n_translate("UNKNOWN") + end + + if _enemyUnit ~= nil and _enemyUnit:getLife() > 0 and _enemyUnit:isActive() == true then + + local action = ctld.i18n_translate(" targeting ") + + if ctld.jtacSelectedTarget[_jtacGroupName] == _enemyUnit:getName() then + action = ctld.i18n_translate(" targeting selected unit ") + else + if ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then + action = ctld.i18n_translate(" attempting to find selected unit, temporarily targeting ") + end + end + + if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then + action = action .. ctld.i18n_translate("(Laser OFF) ") + end + + _message = _message .. "" .. _start .. action .. _enemyUnit:getTypeName() .. " CODE: " .. _laserCode .. ctld.getPositionString(_enemyUnit) .. "\n" + + local _list = ctld.listNearbyEnemies(_jtacUnit) + + if _list then + _message = _message .. ctld.i18n_translate("Visual On: ") + + for _,_type in pairs(_list) do + _message = _message.._type..", " + end + _message = _message.."\n" + end + + else + _message = _message .. "" .. _start .. ctld.i18n_translate(" searching for targets %1\n", ctld.getPositionString(_jtacUnit)) + end + end + end + end + + if not hasJTAC then + ctld.notifyCoalition(ctld.i18n_translate("No Active JTACs"), 10, _side) + else + ctld.notifyCoalition(_message, 10, _side) + end end function ctld.setJTACTarget(_args) - if _args then - local _jtacGroupName = _args.jtacGroupName - local targetName = _args.targetName + if _args then + local _jtacGroupName = _args.jtacGroupName + local targetName = _args.targetName - if _jtacGroupName and targetName and ctld.jtacSelectedTarget[_jtacGroupName] and ctld.jtacTargetsList[_jtacGroupName] then + if _jtacGroupName and targetName and ctld.jtacSelectedTarget[_jtacGroupName] and ctld.jtacTargetsList[_jtacGroupName] then - --look for the unit's (target) name in the Targets List, create the required data structure for jtacCurrentTargets and then assign it to the JTAC called _jtacGroupName - for _, target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do + --look for the unit's (target) name in the Targets List, create the required data structure for jtacCurrentTargets and then assign it to the JTAC called _jtacGroupName + for _, target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do - if target then + if target then - local listedTargetUnit = target.unit - local ListedTargetName = listedTargetUnit:getName() + local listedTargetUnit = target.unit + local ListedTargetName = listedTargetUnit:getName() - if ListedTargetName == targetName then + if ListedTargetName == targetName then - ctld.jtacSelectedTarget[_jtacGroupName] = targetName - ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = listedTargetUnit:getTypeName(), unitId = listedTargetUnit:getID() } + ctld.jtacSelectedTarget[_jtacGroupName] = targetName + ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = listedTargetUnit:getTypeName(), unitId = listedTargetUnit:getID() } - local message = _jtacGroupName .. ctld.i18n_translate(", targeting selected unit, %1",listedTargetUnit:getTypeName()) - local fullMessage = message .. ctld.i18n_translate(". CODE: %1. POSITION: %2", ctld.jtacLaserPointCodes[_jtacGroupName], ctld.getPositionString(listedTargetUnit)) - ctld.notifyCoalition(fullMessage, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName], message) - end + local message = _jtacGroupName .. ctld.i18n_translate(", targeting selected unit, %1",listedTargetUnit:getTypeName()) + local fullMessage = message .. ctld.i18n_translate(". CODE: %1. POSITION: %2", ctld.jtacLaserPointCodes[_jtacGroupName], ctld.getPositionString(listedTargetUnit)) + ctld.notifyCoalition(fullMessage, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName], message) + end + end + end + elseif not targetName and ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then + ctld.jtacSelectedTarget[_jtacGroupName] = 1 + ctld.jtacCurrentTargets[_jtacGroupName] = nil + + local message = _jtacGroupName .. ctld.i18n_translate(", target selection reset.") + ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) + + if ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] then + ctld.setLaseCompensation({jtacGroupName = _jtacGroupName, value = false}) --disable laser spot corrections + end + + if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then + ctld.setStdbMode({jtacGroupName = _jtacGroupName, value = false}) --make the JTAC exit standby mode after either target selection or targeting selection reset + end end - end - elseif not targetName and ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then - ctld.jtacSelectedTarget[_jtacGroupName] = 1 - ctld.jtacCurrentTargets[_jtacGroupName] = nil - local message = _jtacGroupName .. ctld.i18n_translate(", target selection reset.") - ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) - - if ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] then - ctld.setLaseCompensation({jtacGroupName = _jtacGroupName, value = false}) --disable laser spot corrections - end - - if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - ctld.setStdbMode({jtacGroupName = _jtacGroupName, value = false}) --make the JTAC exit standby mode after either target selection or targeting selection reset - end + ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true end - - ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true - end end --special option setters (make sure to affect the function pointer to the corresponding .setter in the special options table after declaration of said function) function ctld.setSpecialOptionArgsCheck(_args) - if _args then - local _jtacGroupName = _args.jtacGroupName - local _value = _args.value --expected boolean - local _notOutput = _args.noOutput --expected boolean + if _args then + local _jtacGroupName = _args.jtacGroupName + local _value = _args.value --expected boolean + local _notOutput = _args.noOutput --expected boolean - if _jtacGroupName then - return {jtacGroupName = _jtacGroupName, value = _value, noOutput = _notOutput} + if _jtacGroupName then + return {jtacGroupName = _jtacGroupName, value = _value, noOutput = _notOutput} + end end - end - return nil + return nil end function ctld.setStdbMode(_args) - local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) - if parsedArgs then + local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) + if parsedArgs then - local _jtacGroupName = parsedArgs.jtacGroupName - local _value = parsedArgs.value - local _noOutput = parsedArgs.noOutput + local _jtacGroupName = parsedArgs.jtacGroupName + local _value = parsedArgs.value + local _noOutput = parsedArgs.noOutput - local message = ctld.i18n_translate("%1, laser and smokes enabled", _jtacGroupName) - if _value then - message = ctld.i18n_translate("%1, laser and smokes disabled", _jtacGroupName) + local message = ctld.i18n_translate("%1, laser and smokes enabled", _jtacGroupName) + if _value then + message = ctld.i18n_translate("%1, laser and smokes disabled", _jtacGroupName) + end + if not _noOutput then + ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) + end + + ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] = _value + ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true end - if not _noOutput then - ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) - end - - ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] = _value - ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true - end end ctld.jtacSpecialOptions.standbyMode.setter = ctld.setStdbMode function ctld.setLaseCompensation(_args) - local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) - if parsedArgs then + local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) + if parsedArgs then - local _jtacGroupName = parsedArgs.jtacGroupName - local _value = parsedArgs.value - local _noOutput = parsedArgs.noOutput + local _jtacGroupName = parsedArgs.jtacGroupName + local _value = parsedArgs.value + local _noOutput = parsedArgs.noOutput - local message = ctld.i18n_translate("%1, wind and target speed laser spot compensations enabled", _jtacGroupName) - if _value then - message = ctld.i18n_translate("%1, wind and target speed laser spot compensations disabled", _jtacGroupName) + local message = ctld.i18n_translate("%1, wind and target speed laser spot compensations enabled", _jtacGroupName) + if _value then + message = ctld.i18n_translate("%1, wind and target speed laser spot compensations disabled", _jtacGroupName) + end + if not _noOutput then + ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) + end + + ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] = _value + ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true end - if not _noOutput then - ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) - end - - ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] = _value - ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true - end end ctld.jtacSpecialOptions.laseSpotCorrections.setter = ctld.setLaseCompensation function ctld.setSmokeOnTarget(_args) - local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) - if parsedArgs then + local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) + if parsedArgs then - local _jtacGroupName = parsedArgs.jtacGroupName - local _noOutput = parsedArgs.noOutput - local _enemyUnit = Unit.getByName(ctld.jtacCurrentTargets[_jtacGroupName].name) + local _jtacGroupName = parsedArgs.jtacGroupName + local _noOutput = parsedArgs.noOutput + local _enemyUnit = Unit.getByName(ctld.jtacCurrentTargets[_jtacGroupName].name) - if _enemyUnit then - if not _noOutput then - ctld.notifyCoalition(ctld.i18n_translate("%1, WHITE smoke deployed near target", _jtacGroupName), 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) - end + if _enemyUnit then + if not _noOutput then + ctld.notifyCoalition(ctld.i18n_translate("%1, WHITE smoke deployed near target", _jtacGroupName), 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) + end - local _enemyPoint = _enemyUnit:getPoint() - local randomCircleDiam = 30; - trigger.action.smoke({ x = _enemyPoint.x + math.random(randomCircleDiam,-randomCircleDiam), y = _enemyPoint.y + 2.0, z = _enemyPoint.z + math.random(randomCircleDiam,-randomCircleDiam)}, 2) - end - end + local _enemyPoint = _enemyUnit:getPoint() + local randomCircleDiam = 30; + trigger.action.smoke({ x = _enemyPoint.x + math.random(randomCircleDiam,-randomCircleDiam), y = _enemyPoint.y + 2.0, z = _enemyPoint.z + math.random(randomCircleDiam,-randomCircleDiam)}, 2) + end end + end ctld.jtacSpecialOptions.smokeMarker.setter = ctld.setSmokeOnTarget function ctld.setJTAC9Line(_args) - local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) - if parsedArgs then + local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) + if parsedArgs then - local _jtacGroupName = parsedArgs.jtacGroupName + local _jtacGroupName = parsedArgs.jtacGroupName - ctld.getJTACStatus({nil, _jtacGroupName}) - end + ctld.getJTACStatus({nil, _jtacGroupName}) + end end ctld.jtacSpecialOptions._9Line.setter = ctld.setJTAC9Line function ctld.setGrpROE(_grp, _ROE) - if _grp == nil then - ctld.logError("ctld.setGrpROE called with a nil group") - return - end + if _grp == nil then + ctld.logError("ctld.setGrpROE called with a nil group") + return + end - if _ROE == nil then - _ROE = AI.Option.Ground.val.ROE.OPEN_FIRE - end + if _ROE == nil then + _ROE = AI.Option.Ground.val.ROE.OPEN_FIRE + end - if _grp and _grp:isExist() == true and #_grp:getUnits() > 0 then -- check if the group truly exists - local _controller = _grp:getController(); - Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) - Controller.setOption(_controller, AI.Option.Ground.id.ROE, _ROE) - _controller:setTask(_grp) - end + if _grp and _grp:isExist() == true and #_grp:getUnits() > 0 then -- check if the group truly exists + local _controller = _grp:getController(); + Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) + Controller.setOption(_controller, AI.Option.Ground.id.ROE, _ROE) + _controller:setTask(_grp) + end end function ctld.isInfantry(_unit) - local _typeName = _unit:getTypeName() + local _typeName = _unit:getTypeName() - --type coerce tostring - _typeName = string.lower(_typeName .. "") + --type coerce tostring + _typeName = string.lower(_typeName .. "") - local _soldierType = { "infantry", "paratrooper", "stinger", "manpad", "mortar" } + local _soldierType = { "infantry", "paratrooper", "stinger", "manpad", "mortar" } - for _key, _value in pairs(_soldierType) do - if string.match(_typeName, _value) then - return true + for _key, _value in pairs(_soldierType) do + if string.match(_typeName, _value) then + return true + end end - end - return false + return false end -- assume anything that isnt soldier is vehicle function ctld.isVehicle(_unit) - if ctld.isInfantry(_unit) then - return false - end + if ctld.isInfantry(_unit) then + return false + end - return true + return true end -- The entered value can range from 1111 - 1788, -- -- but the first digit of the series must be a 1 or 2 -- -- and the last three digits must be between 1 and 8. --- The range used to be bugged so its not 1 - 8 but 0 - 7. +-- The range used to be bugged so its not 1 - 8 but 0 - 7. -- function below will use the range 1-7 just incase function ctld.generateLaserCode() - ctld.jtacGeneratedLaserCodes = {} + ctld.jtacGeneratedLaserCodes = {} - -- generate list of laser codes - local _code = 1511 + -- generate list of laser codes + local _code = 1511 - local _count = 1 + local _count = 1 - while _code < 1777 and _count < 30 do + while _code < 1777 and _count < 30 do - while true do + while true do - _code = _code + 1 + _code = _code + 1 - if not ctld.containsDigit(_code, 8) - and not ctld.containsDigit(_code, 9) - and not ctld.containsDigit(_code, 0) then + if not ctld.containsDigit(_code, 8) + and not ctld.containsDigit(_code, 9) + and not ctld.containsDigit(_code, 0) then - table.insert(ctld.jtacGeneratedLaserCodes, _code) + table.insert(ctld.jtacGeneratedLaserCodes, _code) - --env.info(_code.." Code") - break - end + --env.info(_code.." Code") + break + end + end + _count = _count + 1 end - _count = _count + 1 - end end function ctld.containsDigit(_number, _numberToFind) - local _thisNumber = _number - local _thisDigit = 0 + local _thisNumber = _number + local _thisDigit = 0 - while _thisNumber ~= 0 do + while _thisNumber ~= 0 do - _thisDigit = _thisNumber % 10 - _thisNumber = math.floor(_thisNumber / 10) + _thisDigit = _thisNumber % 10 + _thisNumber = math.floor(_thisNumber / 10) - if _thisDigit == _numberToFind then - return true + if _thisDigit == _numberToFind then + return true + end end - end - return false + return false end -- 200 - 400 in 10KHz @@ -7611,173 +7632,173 @@ end -- 850 - 1250 in 50 KHz function ctld.generateVHFrequencies() - --ignore list - --list of all frequencies in KHZ that could conflict with - -- 191 - 1290 KHz, beacon range - local _skipFrequencies = { - 745, --Astrahan - 381, - 384, - 300.50, - 312.5, - 1175, - 342, - 735, - 300.50, - 353.00, - 440, - 795, - 525, - 520, - 690, - 625, - 291.5, - 300.50, - 435, - 309.50, - 920, - 1065, - 274, - 312.50, - 580, - 602, - 297.50, - 750, - 485, - 950, - 214, - 1025, 730, 995, 455, 307, 670, 329, 395, 770, - 380, 705, 300.5, 507, 740, 1030, 515, - 330, 309.5, - 348, 462, 905, 352, 1210, 942, 435, - 324, - 320, 420, 311, 389, 396, 862, 680, 297.5, - 920, 662, - 866, 907, 309.5, 822, 515, 470, 342, 1182, 309.5, 720, 528, - 337, 312.5, 830, 740, 309.5, 641, 312, 722, 682, 1050, - 1116, 935, 1000, 430, 577, - 326 -- Nevada - } + --ignore list + --list of all frequencies in KHZ that could conflict with + -- 191 - 1290 KHz, beacon range + local _skipFrequencies = { + 745, --Astrahan + 381, + 384, + 300.50, + 312.5, + 1175, + 342, + 735, + 300.50, + 353.00, + 440, + 795, + 525, + 520, + 690, + 625, + 291.5, + 300.50, + 435, + 309.50, + 920, + 1065, + 274, + 312.50, + 580, + 602, + 297.50, + 750, + 485, + 950, + 214, + 1025, 730, 995, 455, 307, 670, 329, 395, 770, + 380, 705, 300.5, 507, 740, 1030, 515, + 330, 309.5, + 348, 462, 905, 352, 1210, 942, 435, + 324, + 320, 420, 311, 389, 396, 862, 680, 297.5, + 920, 662, + 866, 907, 309.5, 822, 515, 470, 342, 1182, 309.5, 720, 528, + 337, 312.5, 830, 740, 309.5, 641, 312, 722, 682, 1050, + 1116, 935, 1000, 430, 577, + 326 -- Nevada + } - ctld.freeVHFFrequencies = {} - local _start = 200000 + ctld.freeVHFFrequencies = {} + local _start = 200000 - -- first range - while _start < 400000 do + -- first range + while _start < 400000 do - -- skip existing NDB frequencies - local _found = false - for _, value in pairs(_skipFrequencies) do - if value * 1000 == _start then - _found = true - break - end + -- skip existing NDB frequencies + local _found = false + for _, value in pairs(_skipFrequencies) do + if value * 1000 == _start then + _found = true + break + end + end + + + if _found == false then + table.insert(ctld.freeVHFFrequencies, _start) + end + + _start = _start + 10000 end + _start = 400000 + -- second range + while _start < 850000 do - if _found == false then - table.insert(ctld.freeVHFFrequencies, _start) + -- skip existing NDB frequencies + local _found = false + for _, value in pairs(_skipFrequencies) do + if value * 1000 == _start then + _found = true + break + end + end + + if _found == false then + table.insert(ctld.freeVHFFrequencies, _start) + end + + + _start = _start + 10000 end - _start = _start + 10000 - end + _start = 850000 + -- third range + while _start <= 1250000 do - _start = 400000 - -- second range - while _start < 850000 do + -- skip existing NDB frequencies + local _found = false + for _, value in pairs(_skipFrequencies) do + if value * 1000 == _start then + _found = true + break + end + end - -- skip existing NDB frequencies - local _found = false - for _, value in pairs(_skipFrequencies) do - if value * 1000 == _start then - _found = true - break - end + if _found == false then + table.insert(ctld.freeVHFFrequencies, _start) + end + + _start = _start + 50000 end - - if _found == false then - table.insert(ctld.freeVHFFrequencies, _start) - end - - - _start = _start + 10000 - end - - _start = 850000 - -- third range - while _start <= 1250000 do - - -- skip existing NDB frequencies - local _found = false - for _, value in pairs(_skipFrequencies) do - if value * 1000 == _start then - _found = true - break - end - end - - if _found == false then - table.insert(ctld.freeVHFFrequencies, _start) - end - - _start = _start + 50000 - end end -- 220 - 399 MHZ, increments of 0.5MHZ function ctld.generateUHFrequencies() - ctld.freeUHFFrequencies = {} - local _start = 220000000 + ctld.freeUHFFrequencies = {} + local _start = 220000000 - while _start < 399000000 do - table.insert(ctld.freeUHFFrequencies, _start) - _start = _start + 500000 - end + while _start < 399000000 do + table.insert(ctld.freeUHFFrequencies, _start) + _start = _start + 500000 + end end -- 220 - 399 MHZ, increments of 0.5MHZ --- -- first digit 3-7MHz --- -- second digit 0-5KHz --- -- third digit 0-9 --- -- fourth digit 0 or 5 --- -- times by 10000 +-- -- first digit 3-7MHz +-- -- second digit 0-5KHz +-- -- third digit 0-9 +-- -- fourth digit 0 or 5 +-- -- times by 10000 -- function ctld.generateFMFrequencies() - ctld.freeFMFrequencies = {} - local _start = 220000000 + ctld.freeFMFrequencies = {} + local _start = 220000000 - while _start < 399000000 do + while _start < 399000000 do - _start = _start + 500000 - end - - for _first = 3, 7 do - for _second = 0, 5 do - for _third = 0, 9 do - local _frequency = ((100 * _first) + (10 * _second) + _third) * 100000 --extra 0 because we didnt bother with 4th digit - table.insert(ctld.freeFMFrequencies, _frequency) - end + _start = _start + 500000 + end + + for _first = 3, 7 do + for _second = 0, 5 do + for _third = 0, 9 do + local _frequency = ((100 * _first) + (10 * _second) + _third) * 100000 --extra 0 because we didnt bother with 4th digit + table.insert(ctld.freeFMFrequencies, _frequency) + end + end end - end end function ctld.getPositionString(_unit) - if ctld.JTAC_location == false then - return "" - end + if ctld.JTAC_location == false then + return "" + end - local _lat, _lon = coord.LOtoLL(_unit:getPosition().p) - local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) - local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_unit:getPosition().p)), 5) - local _TargetAlti = land.getHeight(mist.utils.makeVec2(_unit:getPoint())) - return " @ " .. _latLngStr .. " - MGRS " .. _mgrsString .. " - ALTI: " .. mist.utils.round(_TargetAlti, 0) .. " m / " .. mist.utils.round(_TargetAlti/0.3048, 0) .. " ft" + local _lat, _lon = coord.LOtoLL(_unit:getPosition().p) + local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) + local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_unit:getPosition().p)), 5) + local _TargetAlti = land.getHeight(mist.utils.makeVec2(_unit:getPoint())) + return " @ " .. _latLngStr .. " - MGRS " .. _mgrsString .. " - ALTI: " .. mist.utils.round(_TargetAlti, 0) .. " m / " .. mist.utils.round(_TargetAlti/0.3048, 0) .. " ft" end --********************************************************************** --- RECOGNITION SUPPORT FUNCTIONS +-- RECOGNITION SUPPORT FUNCTIONS -- Shows/remove/refresh marks in F10 map on targets in LOS of a unit passed in params --------------------------------------------------------------------- -- examples --------------------------------------------------------- @@ -7785,176 +7806,176 @@ end --ctld.reconRemoveTargetsInLosOnF10Map(Unit.getByName("uh2-1")) --ctld.reconShowTargetsInLosOnF10Map(Unit.getByName("uh2-1"), 2000, 200) ---------------------------------------------------------------------- ---if ctld == nil then ctld = {} end +--if ctld == nil then ctld = {} end if ctld.lastMarkId == nil then - ctld.lastMarkId = 0 + ctld.lastMarkId = 0 end -- ***************** RECON CONFIGURATION ***************** -ctld.reconF10Menu = true -- enables F10 RECON menu -ctld.reconMenuName = ctld.i18n_translate("RECON") --name of the CTLD JTAC radio menu -ctld.reconRadioAdded = {} --stores the groups that have had the radio menu added +ctld.reconF10Menu = true -- enables F10 RECON menu +ctld.reconMenuName = ctld.i18n_translate("RECON") --name of the CTLD JTAC radio menu +ctld.reconRadioAdded = {} --stores the groups that have had the radio menu added ctld.reconLosSearchRadius = 2000 -- search radius 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.reconLosMarkRadius = 100 -- mark radius dimension in meters +ctld.reconAutoRefreshLosTargetMarks = true -- if true recon LOS marks are automaticaly refreshed on F10 map ctld.reconLastScheduleIdAutoRefresh = 0 ---- F10 RECON Menus ------------------------------------------------------------------ -function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) - if ctld.reconF10Menu then - if _side == 1 or _side == 2 then - local _players = coalition.getPlayers(_side) - if _players ~= nil then - for _, _playerUnit in pairs(_players) do - local _groupId = ctld.getGroupId(_playerUnit) - if _groupId then - if ctld.reconRadioAdded[tostring(_groupId)] == nil then - ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", ctld.p(_playerUnit:getName())) - local RECONpath = missionCommands.addSubMenuForGroup(_groupId, ctld.reconMenuName) - missionCommands.addCommandForGroup(_groupId, - ctld.i18n_translate("Show targets in LOS (refresh)"), RECONpath, - ctld.reconRefreshTargetsInLosOnF10Map, { - _groupId = _groupId, - _playerUnit = _playerUnit, - _searchRadius = ctld.reconLosSearchRadius, - _markRadius = ctld.reconLosMarkRadius, - _boolRemove = true - }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Hide targets in LOS"), RECONpath, ctld.reconRemoveTargetsInLosOnF10Map, _playerUnit) - if ctld.reconAutoRefreshLosTargetMarks then - missionCommands.addCommandForGroup(_groupId, - ctld.i18n_translate("STOP autoRefresh targets in LOS"), RECONpath, - ctld.reconStopAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - ctld.reconLosSearchRadius, - ctld.reconLosMarkRadius, - true) - else - missionCommands.addCommandForGroup(_groupId, - ctld.i18n_translate("START autoRefresh targets in LOS"), RECONpath, - ctld.reconStartAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - ctld.reconLosSearchRadius, - ctld.reconLosMarkRadius, - true - ) - end - ctld.reconRadioAdded[tostring(_groupId)] = timer.getTime() --fetch the time to check for a regular refresh +function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) + if ctld.reconF10Menu then + if _side == 1 or _side == 2 then + local _players = coalition.getPlayers(_side) + if _players ~= nil then + for _, _playerUnit in pairs(_players) do + local _groupId = ctld.getGroupId(_playerUnit) + if _groupId then + if ctld.reconRadioAdded[tostring(_groupId)] == nil then + ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", ctld.p(_playerUnit:getName())) + local RECONpath = missionCommands.addSubMenuForGroup(_groupId, ctld.reconMenuName) + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("Show targets in LOS (refresh)"), RECONpath, + ctld.reconRefreshTargetsInLosOnF10Map, { + _groupId = _groupId, + _playerUnit = _playerUnit, + _searchRadius = ctld.reconLosSearchRadius, + _markRadius = ctld.reconLosMarkRadius, + _boolRemove = true + }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Hide targets in LOS"), RECONpath, ctld.reconRemoveTargetsInLosOnF10Map, _playerUnit) + if ctld.reconAutoRefreshLosTargetMarks then + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("STOP autoRefresh targets in LOS"), RECONpath, + ctld.reconStopAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + ctld.reconLosSearchRadius, + ctld.reconLosMarkRadius, + true) + else + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("START autoRefresh targets in LOS"), RECONpath, + ctld.reconStartAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + ctld.reconLosSearchRadius, + ctld.reconLosMarkRadius, + true + ) + end + ctld.reconRadioAdded[tostring(_groupId)] = timer.getTime() --fetch the time to check for a regular refresh + end + end + end end - end end - end end - end end -------------------------------------------------------------------- function ctld.reconStopAutorefreshTargetsInLosOnF10Map(_groupId, _playerUnit, _searchRadius, _markRadius, _boolRemove) - ctld.reconAutoRefreshLosTargetMarks = false + ctld.reconAutoRefreshLosTargetMarks = false - if ctld.reconLastScheduleIdAutoRefresh ~= 0 then - timer.removeFunction(ctld.reconLastScheduleIdAutoRefresh) -- reset last schedule - end + if ctld.reconLastScheduleIdAutoRefresh ~= 0 then + timer.removeFunction(ctld.reconLastScheduleIdAutoRefresh) -- reset last schedule + end - ctld.reconRemoveTargetsInLosOnF10Map(_playerUnit) - missionCommands.removeItemForGroup(_groupId, {ctld.reconMenuName, ctld.i18n_translate("STOP autoRefresh targets in LOS")}) - missionCommands.addCommandForGroup( _groupId, ctld.i18n_translate("START autoRefresh targets in LOS"), {ctld.reconMenuName}, - ctld.reconStartAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - _searchRadius, - _markRadius, - _boolRemove) + ctld.reconRemoveTargetsInLosOnF10Map(_playerUnit) + missionCommands.removeItemForGroup(_groupId, {ctld.reconMenuName, ctld.i18n_translate("STOP autoRefresh targets in LOS")}) + missionCommands.addCommandForGroup( _groupId, ctld.i18n_translate("START autoRefresh targets in LOS"), {ctld.reconMenuName}, + ctld.reconStartAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + _searchRadius, + _markRadius, + _boolRemove) end -------------------------------------------------------------------- function ctld.reconStartAutorefreshTargetsInLosOnF10Map(_groupId, _playerUnit, _searchRadius, _markRadius, _boolRemove) - ctld.reconAutoRefreshLosTargetMarks = true - ctld.reconRefreshTargetsInLosOnF10Map({ _groupId = _groupId, - _playerUnit = _playerUnit, - _searchRadius = _searchRadius or ctld.reconLosSearchRadius, - _markRadius = _markRadius or ctld.reconLosMarkRadius, - _boolRemove = _boolRemove or true}, - timer.getTime()) - missionCommands.removeItemForGroup( _groupId, {ctld.reconMenuName, ctld.i18n_translate("START autoRefresh targets in LOS")}) - missionCommands.addCommandForGroup( _groupId, ctld.i18n_translate("STOP autoRefresh targets in LOS"), {ctld.reconMenuName}, - ctld.reconStopAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - _searchRadius, - _markRadius, - _boolRemove) + ctld.reconAutoRefreshLosTargetMarks = true + ctld.reconRefreshTargetsInLosOnF10Map({ _groupId = _groupId, + _playerUnit = _playerUnit, + _searchRadius = _searchRadius or ctld.reconLosSearchRadius, + _markRadius = _markRadius or ctld.reconLosMarkRadius, + _boolRemove = _boolRemove or true}, + timer.getTime()) + missionCommands.removeItemForGroup( _groupId, {ctld.reconMenuName, ctld.i18n_translate("START autoRefresh targets in LOS")}) + missionCommands.addCommandForGroup( _groupId, ctld.i18n_translate("STOP autoRefresh targets in LOS"), {ctld.reconMenuName}, + ctld.reconStopAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + _searchRadius, + _markRadius, + _boolRemove) end -------------------------------------------------------------------- -function ctld.reconShowTargetsInLosOnF10Map(_playerUnit, _searchRadius, _markRadius) -- _groupId targeting - -- _searchRadius and _markRadius in meters - if _playerUnit then - local TargetsInLOS = {} +function ctld.reconShowTargetsInLosOnF10Map(_playerUnit, _searchRadius, _markRadius) -- _groupId targeting + -- _searchRadius and _markRadius in meters + if _playerUnit then + local TargetsInLOS = {} - local enemyColor = "red" - local color = {1, 0, 0, 0.2} -- red + local enemyColor = "red" + local color = {1, 0, 0, 0.2} -- red - if _playerUnit:getCoalition() == 1 then - enemyColor = "blue" - color = {51/255, 51/255, 1, 0.2} -- blue - end - - local t = mist.getUnitsLOS({_playerUnit:getName()}, 180, mist.makeUnitTable({'['..enemyColor..'][vehicle]'}),180, _searchRadius) - - local MarkIds = {} - if t then - for i=1, #t do -- for each unit having los on enemies - for j=1, #t[i].vis do -- for each enemy unit in los - local targetPoint = t[i].vis[j]:getPoint() -- point of each target on LOS - ctld.lastMarkId = ctld.lastMarkId + 1 - trigger.action.circleToAll(_playerUnit:getCoalition(), ctld.lastMarkId, targetPoint, _markRadius , color, color, 1, false, nil) - MarkIds[#MarkIds+1] = ctld.lastMarkId - TargetsInLOS[#TargetsInLOS+1] = { targetObject = t[i].vis[j]:getName(), - targetTypeName = t[i].vis[j]:getTypeName(), - targetPoint = targetPoint} + if _playerUnit:getCoalition() == 1 then + enemyColor = "blue" + color = {51/255, 51/255, 1, 0.2} -- blue end - end + + local t = mist.getUnitsLOS({_playerUnit:getName()}, 180, mist.makeUnitTable({'['..enemyColor..'][vehicle]'}),180, _searchRadius) + + local MarkIds = {} + if t then + for i=1, #t do -- for each unit having los on enemies + for j=1, #t[i].vis do -- for each enemy unit in los + local targetPoint = t[i].vis[j]:getPoint() -- point of each target on LOS + ctld.lastMarkId = ctld.lastMarkId + 1 + trigger.action.circleToAll(_playerUnit:getCoalition(), ctld.lastMarkId, targetPoint, _markRadius , color, color, 1, false, nil) + MarkIds[#MarkIds+1] = ctld.lastMarkId + TargetsInLOS[#TargetsInLOS+1] = { targetObject = t[i].vis[j]:getName(), + targetTypeName = t[i].vis[j]:getTypeName(), + targetPoint = targetPoint} + end + end + end + mist.DBs.humansByName[_playerUnit:getName()].losMarkIds = MarkIds -- store list of marksIds generated and showed on F10 map + return TargetsInLOS + else + return nil end - mist.DBs.humansByName[_playerUnit:getName()].losMarkIds = MarkIds -- store list of marksIds generated and showed on F10 map - return TargetsInLOS - else - return nil - end end --------------------------------------------------------- function ctld.reconRemoveTargetsInLosOnF10Map(_playerUnit) - local unitName = _playerUnit:getName() - if mist.DBs.humansByName[unitName].losMarkIds then - for i=1, #mist.DBs.humansByName[unitName].losMarkIds do -- for each unit having los on enemies - trigger.action.removeMark(mist.DBs.humansByName[unitName].losMarkIds[i]) + local unitName = _playerUnit:getName() + if mist.DBs.humansByName[unitName].losMarkIds then + for i=1, #mist.DBs.humansByName[unitName].losMarkIds do -- for each unit having los on enemies + trigger.action.removeMark(mist.DBs.humansByName[unitName].losMarkIds[i]) + end + mist.DBs.humansByName[unitName].losMarkIds = nil end - mist.DBs.humansByName[unitName].losMarkIds = nil - end end --------------------------------------------------------- -function ctld.reconRefreshTargetsInLosOnF10Map(_params, _t) -- _params._playerUnit targeting - -- _params._searchRadius and _params._markRadius in meters - -- _params._boolRemove = true to remove previous marksIds - if _t == nil then _t = timer.getTime() end +function ctld.reconRefreshTargetsInLosOnF10Map(_params, _t) -- _params._playerUnit targeting + -- _params._searchRadius and _params._markRadius in meters + -- _params._boolRemove = true to remove previous marksIds + if _t == nil then _t = timer.getTime() end - if ctld.reconAutoRefreshLosTargetMarks then -- to follow mobile enemy targets - ctld.reconLastScheduleIdAutoRefresh = timer.scheduleFunction(ctld.reconRefreshTargetsInLosOnF10Map, {_groupId = _params._groupId, - _playerUnit = _params._playerUnit, - _searchRadius = _params._searchRadius, - _markRadius = _params._markRadius, - _boolRemove = _params._boolRemove - }, - timer.getTime() + 10) - end + if ctld.reconAutoRefreshLosTargetMarks then -- to follow mobile enemy targets + ctld.reconLastScheduleIdAutoRefresh = timer.scheduleFunction(ctld.reconRefreshTargetsInLosOnF10Map, {_groupId = _params._groupId, + _playerUnit = _params._playerUnit, + _searchRadius = _params._searchRadius, + _markRadius = _params._markRadius, + _boolRemove = _params._boolRemove + }, + timer.getTime() + 10) + end - if _params._boolRemove == true then - ctld.reconRemoveTargetsInLosOnF10Map(_params._playerUnit) - end + if _params._boolRemove == true then + ctld.reconRemoveTargetsInLosOnF10Map(_params._playerUnit) + end - return ctld.reconShowTargetsInLosOnF10Map(_params._playerUnit, _params._searchRadius, _params._markRadius) -- returns TargetsInLOS table + return ctld.reconShowTargetsInLosOnF10Map(_params._playerUnit, _params._searchRadius, _params._markRadius) -- returns TargetsInLOS table end --- test ------------------------------------------------------ ---local unitName = "uh2-1" --"uh1-1" --"uh2-1" +--local unitName = "uh2-1" --"uh1-1" --"uh2-1" --ctld.reconShowTargetsInLosOnF10Map(Unit.getByName(unitName),2000,200) @@ -7962,438 +7983,438 @@ end -- ***************** SETUP SCRIPT **************** function ctld.initialize() - ctld.logInfo(string.format("Initializing version %s", ctld.Version)) + ctld.logInfo(string.format("Initializing version %s", ctld.Version)) - assert(mist ~= nil, "\n\n** HEY MISSION-DESIGNER! **\n\nMiST has not been loaded!\n\nMake sure MiST 3.6 or higher is running\n*before* running this script!\n") + assert(mist ~= nil, "\n\n** HEY MISSION-DESIGNER! **\n\nMiST has not been loaded!\n\nMake sure MiST 3.6 or higher is running\n*before* running this script!\n") - ctld.addedTo = {} - ctld.spawnedCratesRED = {} -- use to store crates that have been spawned - ctld.spawnedCratesBLUE = {} -- use to store crates that have been spawned + ctld.addedTo = {} + ctld.spawnedCratesRED = {} -- use to store crates that have been spawned + ctld.spawnedCratesBLUE = {} -- use to store crates that have been spawned - ctld.droppedTroopsRED = {} -- stores dropped troop groups - ctld.droppedTroopsBLUE = {} -- stores dropped troop groups + ctld.droppedTroopsRED = {} -- stores dropped troop groups + ctld.droppedTroopsBLUE = {} -- stores dropped troop groups - ctld.droppedVehiclesRED = {} -- stores vehicle groups for c-130 / hercules - ctld.droppedVehiclesBLUE = {} -- stores vehicle groups for c-130 / hercules + ctld.droppedVehiclesRED = {} -- stores vehicle groups for c-130 / hercules + ctld.droppedVehiclesBLUE = {} -- stores vehicle groups for c-130 / hercules - ctld.inTransitTroops = {} + ctld.inTransitTroops = {} - ctld.inTransitFOBCrates = {} + ctld.inTransitFOBCrates = {} - ctld.inTransitSlingLoadCrates = {} -- stores crates that are being transported by helicopters for alternative to real slingload + ctld.inTransitSlingLoadCrates = {} -- stores crates that are being transported by helicopters for alternative to real slingload - ctld.droppedFOBCratesRED = {} - ctld.droppedFOBCratesBLUE = {} + ctld.droppedFOBCratesRED = {} + ctld.droppedFOBCratesBLUE = {} - ctld.builtFOBS = {} -- stores fully built fobs + ctld.builtFOBS = {} -- stores fully built fobs - ctld.completeAASystems = {} -- stores complete spawned groups from multiple crates + ctld.completeAASystems = {} -- stores complete spawned groups from multiple crates - ctld.fobBeacons = {} -- stores FOB radio beacon details, refreshed every 60 seconds + ctld.fobBeacons = {} -- stores FOB radio beacon details, refreshed every 60 seconds - ctld.deployedRadioBeacons = {} -- stores details of deployed radio beacons + ctld.deployedRadioBeacons = {} -- stores details of deployed radio beacons - ctld.beaconCount = 1 + ctld.beaconCount = 1 - ctld.usedUHFFrequencies = {} - ctld.usedVHFFrequencies = {} - ctld.usedFMFrequencies = {} + ctld.usedUHFFrequencies = {} + ctld.usedVHFFrequencies = {} + ctld.usedFMFrequencies = {} - ctld.freeUHFFrequencies = {} - ctld.freeVHFFrequencies = {} - ctld.freeFMFrequencies = {} + ctld.freeUHFFrequencies = {} + ctld.freeVHFFrequencies = {} + ctld.freeFMFrequencies = {} - --used to lookup what the crate will contain - ctld.crateLookupTable = {} + --used to lookup what the crate will contain + ctld.crateLookupTable = {} - ctld.extractZones = {} -- stored extract zones + ctld.extractZones = {} -- stored extract zones - ctld.missionEditorCargoCrates = {} --crates added by mission editor for triggering cratesinzone - ctld.hoverStatus = {} -- tracks status of a helis hover above a crate + ctld.missionEditorCargoCrates = {} --crates added by mission editor for triggering cratesinzone + ctld.hoverStatus = {} -- tracks status of a helis hover above a crate - ctld.callbacks = {} -- function callback + ctld.callbacks = {} -- function callback - -- Remove intransit troops when heli / cargo plane dies - --ctld.eventHandler = {} - --function ctld.eventHandler:onEvent(_event) - -- - -- if _event == nil or _event.initiator == nil then - -- env.info("CTLD null event") - -- elseif _event.id == 9 then - -- -- Pilot dead - -- ctld.inTransitTroops[_event.initiator:getName()] = nil - -- - -- elseif world.event.S_EVENT_EJECTION == _event.id or _event.id == 8 then - -- -- env.info("Event unit - Pilot Ejected or Unit Dead") - -- ctld.inTransitTroops[_event.initiator:getName()] = nil - -- - -- -- env.info(_event.initiator:getName()) - -- end - -- - --end + -- Remove intransit troops when heli / cargo plane dies + --ctld.eventHandler = {} + --function ctld.eventHandler:onEvent(_event) + -- + -- if _event == nil or _event.initiator == nil then + -- env.info("CTLD null event") + -- elseif _event.id == 9 then + -- -- Pilot dead + -- ctld.inTransitTroops[_event.initiator:getName()] = nil + -- + -- elseif world.event.S_EVENT_EJECTION == _event.id or _event.id == 8 then + -- -- env.info("Event unit - Pilot Ejected or Unit Dead") + -- ctld.inTransitTroops[_event.initiator:getName()] = nil + -- + -- -- env.info(_event.initiator:getName()) + -- end + -- + --end - -- create crate lookup table - for _subMenuName, _crates in pairs(ctld.spawnableCrates) do + -- create crate lookup table + for _subMenuName, _crates in pairs(ctld.spawnableCrates) do - for _, _crate in pairs(_crates) do - -- convert number to string otherwise we'll have a pointless giant - -- table. String means 'hashmap' so it will only contain the right number of elements - if _crate.multiple then - local _totalWeight = 0 - for _, _weight in pairs(_crate.multiple) do - _totalWeight = _totalWeight + _weight - end - _crate.weight = _totalWeight - end - ctld.crateLookupTable[tostring(_crate.weight)] = _crate - end - end - - - --sort out pickup zones - for _, _zone in pairs(ctld.pickupZones) do - - local _zoneName = _zone[1] - local _zoneColor = _zone[2] - local _zoneActive = _zone[4] - - if _zoneColor == "green" then - _zone[2] = trigger.smokeColor.Green - elseif _zoneColor == "red" then - _zone[2] = trigger.smokeColor.Red - elseif _zoneColor == "white" then - _zone[2] = trigger.smokeColor.White - elseif _zoneColor == "orange" then - _zone[2] = trigger.smokeColor.Orange - elseif _zoneColor == "blue" then - _zone[2] = trigger.smokeColor.Blue - else - _zone[2] = -1 -- no smoke colour - end - - -- add in counter for troops or units - if _zone[3] == -1 then - _zone[3] = 10000; - end - - -- change active to 1 / 0 - if _zoneActive == "yes" then - _zone[4] = 1 - else - _zone[4] = 0 - end - end - - --sort out dropoff zones - for _, _zone in pairs(ctld.dropOffZones) do - - local _zoneColor = _zone[2] - - if _zoneColor == "green" then - _zone[2] = trigger.smokeColor.Green - elseif _zoneColor == "red" then - _zone[2] = trigger.smokeColor.Red - elseif _zoneColor == "white" then - _zone[2] = trigger.smokeColor.White - elseif _zoneColor == "orange" then - _zone[2] = trigger.smokeColor.Orange - elseif _zoneColor == "blue" then - _zone[2] = trigger.smokeColor.Blue - else - _zone[2] = -1 -- no smoke colour - end - - --mark as active for refresh smoke logic to work - _zone[4] = 1 - end - - --sort out waypoint zones - for _, _zone in pairs(ctld.wpZones) do - - local _zoneColor = _zone[2] - - if _zoneColor == "green" then - _zone[2] = trigger.smokeColor.Green - elseif _zoneColor == "red" then - _zone[2] = trigger.smokeColor.Red - elseif _zoneColor == "white" then - _zone[2] = trigger.smokeColor.White - elseif _zoneColor == "orange" then - _zone[2] = trigger.smokeColor.Orange - elseif _zoneColor == "blue" then - _zone[2] = trigger.smokeColor.Blue - else - _zone[2] = -1 -- no smoke colour - end - - --mark as active for refresh smoke logic to work - -- change active to 1 / 0 - if _zone[3] == "yes" then - _zone[3] = 1 - else - _zone[3] = 0 - end - end - - -- Sort out extractable groups - for _, _groupName in pairs(ctld.extractableGroups) do - - local _group = Group.getByName(_groupName) - - if _group ~= nil then - - if _group:getCoalition() == 1 then - table.insert(ctld.droppedTroopsRED, _group:getName()) - else - table.insert(ctld.droppedTroopsBLUE, _group:getName()) - end - end - end - - - -- Seperate troop teams into red and blue for random AI pickups - if ctld.allowRandomAiTeamPickups == true then - ctld.redTeams = {} - ctld.blueTeams = {} - for _,_loadGroup in pairs(ctld.loadableGroups) do - if not _loadGroup.side then - table.insert(ctld.redTeams, _) - table.insert(ctld.blueTeams, _) - elseif _loadGroup.side == 1 then - table.insert(ctld.redTeams, _) - elseif _loadGroup.side == 2 then - table.insert(ctld.blueTeams, _) - end - end - end - - -- add total count - - for _,_loadGroup in pairs(ctld.loadableGroups) do - - _loadGroup.total = 0 - if _loadGroup.aa then - _loadGroup.total = _loadGroup.aa + _loadGroup.total - end - - if _loadGroup.inf then - _loadGroup.total = _loadGroup.inf + _loadGroup.total - end - - - if _loadGroup.mg then - _loadGroup.total = _loadGroup.mg + _loadGroup.total - end - - if _loadGroup.at then - _loadGroup.total = _loadGroup.at + _loadGroup.total - end - - if _loadGroup.mortar then - _loadGroup.total = _loadGroup.mortar + _loadGroup.total - end - - end - - - -- Scheduled functions (run cyclically) -- but hold execution for a second so we can override parts - - timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 1) - timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 5) - - timer.scheduleFunction(function() - timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 5) - timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 5) - timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 5) - timer.scheduleFunction(ctld.updateDynamicLogisticUnitsZones, nil, timer.getTime() + 5) - if ctld.enableCrates == true and ctld.hoverPickup == true then - timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) + for _, _crate in pairs(_crates) do + -- convert number to string otherwise we'll have a pointless giant + -- table. String means 'hashmap' so it will only contain the right number of elements + if _crate.multiple then + local _totalWeight = 0 + for _, _weight in pairs(_crate.multiple) do + _totalWeight = _totalWeight + _weight end - if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) - end - end,nil, timer.getTime()+1 ) - - --event handler for deaths - --world.addEventHandler(ctld.eventHandler) - - --env.info("CTLD event handler added") - - env.info("Generating Laser Codes") - ctld.generateLaserCode() - env.info("Generated Laser Codes") - - - - env.info("Generating UHF Frequencies") - ctld.generateUHFrequencies() - env.info("Generated UHF Frequencies") - - env.info("Generating VHF Frequencies") - ctld.generateVHFrequencies() - env.info("Generated VHF Frequencies") - - - env.info("Generating FM Frequencies") - ctld.generateFMFrequencies() - env.info("Generated FM Frequencies") - - -- Search for crates - -- Crates are NOT returned by coalition.getStaticObjects() for some reason - -- Search for crates in the mission editor instead - env.info("Searching for Crates") - for _coalitionName, _coalitionData in pairs(env.mission.coalition) do - - if (_coalitionName == 'red' or _coalitionName == 'blue') - and type(_coalitionData) == 'table' then - if _coalitionData.country then --there is a country table - for _, _countryData in pairs(_coalitionData.country) do - - if type(_countryData) == 'table' then - for _objectTypeName, _objectTypeData in pairs(_countryData) do - if _objectTypeName == "static" then - - if ((type(_objectTypeData) == 'table') - and _objectTypeData.group - and (type(_objectTypeData.group) == 'table') - and (#_objectTypeData.group > 0)) then - - for _groupId, _group in pairs(_objectTypeData.group) do - if _group and _group.units and type(_group.units) == 'table' then - for _unitNum, _unit in pairs(_group.units) do - if _unit.canCargo == true then - local _cargoName = env.getValueDictByKey(_unit.name) - ctld.missionEditorCargoCrates[_cargoName] = _cargoName - env.info("Crate Found: " .. _unit.name.." - Unit: ".._cargoName) - end - end - end - end - end + _crate.weight = _totalWeight end - end + ctld.crateLookupTable[tostring(_crate.weight)] = _crate end - end - end end - end - env.info("END search for crates") - -- register event handler - ctld.logInfo("registering event handler") - world.addEventHandler(ctld.eventHandler) - env.info("CTLD READY") + + --sort out pickup zones + for _, _zone in pairs(ctld.pickupZones) do + + local _zoneName = _zone[1] + local _zoneColor = _zone[2] + local _zoneActive = _zone[4] + + if _zoneColor == "green" then + _zone[2] = trigger.smokeColor.Green + elseif _zoneColor == "red" then + _zone[2] = trigger.smokeColor.Red + elseif _zoneColor == "white" then + _zone[2] = trigger.smokeColor.White + elseif _zoneColor == "orange" then + _zone[2] = trigger.smokeColor.Orange + elseif _zoneColor == "blue" then + _zone[2] = trigger.smokeColor.Blue + else + _zone[2] = -1 -- no smoke colour + end + + -- add in counter for troops or units + if _zone[3] == -1 then + _zone[3] = 10000; + end + + -- change active to 1 / 0 + if _zoneActive == "yes" then + _zone[4] = 1 + else + _zone[4] = 0 + end + end + + --sort out dropoff zones + for _, _zone in pairs(ctld.dropOffZones) do + + local _zoneColor = _zone[2] + + if _zoneColor == "green" then + _zone[2] = trigger.smokeColor.Green + elseif _zoneColor == "red" then + _zone[2] = trigger.smokeColor.Red + elseif _zoneColor == "white" then + _zone[2] = trigger.smokeColor.White + elseif _zoneColor == "orange" then + _zone[2] = trigger.smokeColor.Orange + elseif _zoneColor == "blue" then + _zone[2] = trigger.smokeColor.Blue + else + _zone[2] = -1 -- no smoke colour + end + + --mark as active for refresh smoke logic to work + _zone[4] = 1 + end + + --sort out waypoint zones + for _, _zone in pairs(ctld.wpZones) do + + local _zoneColor = _zone[2] + + if _zoneColor == "green" then + _zone[2] = trigger.smokeColor.Green + elseif _zoneColor == "red" then + _zone[2] = trigger.smokeColor.Red + elseif _zoneColor == "white" then + _zone[2] = trigger.smokeColor.White + elseif _zoneColor == "orange" then + _zone[2] = trigger.smokeColor.Orange + elseif _zoneColor == "blue" then + _zone[2] = trigger.smokeColor.Blue + else + _zone[2] = -1 -- no smoke colour + end + + --mark as active for refresh smoke logic to work + -- change active to 1 / 0 + if _zone[3] == "yes" then + _zone[3] = 1 + else + _zone[3] = 0 + end + end + + -- Sort out extractable groups + for _, _groupName in pairs(ctld.extractableGroups) do + + local _group = Group.getByName(_groupName) + + if _group ~= nil then + + if _group:getCoalition() == 1 then + table.insert(ctld.droppedTroopsRED, _group:getName()) + else + table.insert(ctld.droppedTroopsBLUE, _group:getName()) + end + end + end + + + -- Seperate troop teams into red and blue for random AI pickups + if ctld.allowRandomAiTeamPickups == true then + ctld.redTeams = {} + ctld.blueTeams = {} + for _,_loadGroup in pairs(ctld.loadableGroups) do + if not _loadGroup.side then + table.insert(ctld.redTeams, _) + table.insert(ctld.blueTeams, _) + elseif _loadGroup.side == 1 then + table.insert(ctld.redTeams, _) + elseif _loadGroup.side == 2 then + table.insert(ctld.blueTeams, _) + end + end + end + + -- add total count + + for _,_loadGroup in pairs(ctld.loadableGroups) do + + _loadGroup.total = 0 + if _loadGroup.aa then + _loadGroup.total = _loadGroup.aa + _loadGroup.total + end + + if _loadGroup.inf then + _loadGroup.total = _loadGroup.inf + _loadGroup.total + end + + + if _loadGroup.mg then + _loadGroup.total = _loadGroup.mg + _loadGroup.total + end + + if _loadGroup.at then + _loadGroup.total = _loadGroup.at + _loadGroup.total + end + + if _loadGroup.mortar then + _loadGroup.total = _loadGroup.mortar + _loadGroup.total + end + + end + + + -- Scheduled functions (run cyclically) -- but hold execution for a second so we can override parts + + timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 1) + timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 5) + + timer.scheduleFunction(function() + timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.updateDynamicLogisticUnitsZones, nil, timer.getTime() + 5) + if ctld.enableCrates == true and ctld.hoverPickup == true then + timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) + end + if ctld.enableRepackingVehicles == true then + timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) + end + end,nil, timer.getTime()+1 ) + + --event handler for deaths + --world.addEventHandler(ctld.eventHandler) + + --env.info("CTLD event handler added") + + env.info("Generating Laser Codes") + ctld.generateLaserCode() + env.info("Generated Laser Codes") + + + + env.info("Generating UHF Frequencies") + ctld.generateUHFrequencies() + env.info("Generated UHF Frequencies") + + env.info("Generating VHF Frequencies") + ctld.generateVHFrequencies() + env.info("Generated VHF Frequencies") + + + env.info("Generating FM Frequencies") + ctld.generateFMFrequencies() + env.info("Generated FM Frequencies") + + -- Search for crates + -- Crates are NOT returned by coalition.getStaticObjects() for some reason + -- Search for crates in the mission editor instead + env.info("Searching for Crates") + for _coalitionName, _coalitionData in pairs(env.mission.coalition) do + + if (_coalitionName == 'red' or _coalitionName == 'blue') + and type(_coalitionData) == 'table' then + if _coalitionData.country then --there is a country table + for _, _countryData in pairs(_coalitionData.country) do + + if type(_countryData) == 'table' then + for _objectTypeName, _objectTypeData in pairs(_countryData) do + if _objectTypeName == "static" then + + if ((type(_objectTypeData) == 'table') + and _objectTypeData.group + and (type(_objectTypeData.group) == 'table') + and (#_objectTypeData.group > 0)) then + + for _groupId, _group in pairs(_objectTypeData.group) do + if _group and _group.units and type(_group.units) == 'table' then + for _unitNum, _unit in pairs(_group.units) do + if _unit.canCargo == true then + local _cargoName = env.getValueDictByKey(_unit.name) + ctld.missionEditorCargoCrates[_cargoName] = _cargoName + env.info("Crate Found: " .. _unit.name.." - Unit: ".._cargoName) + end + end + end + end + end + end + end + end + end + end + end + end + env.info("END search for crates") + + -- register event handler + ctld.logInfo("registering event handler") + world.addEventHandler(ctld.eventHandler) + env.info("CTLD READY") end --- Handle world events. ctld.eventHandler = {} function ctld.eventHandler:onEvent(event) - ctld.logTrace("ctld.eventHandler:onEvent()") - if event == nil then - ctld.logError("Event handler was called with a nil event!") - return - end - - local eventName = "unknown" - -- check that we know the event - if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then - eventName = "S_EVENT_PLAYER_ENTER_UNIT" - elseif event.id == world.event.S_EVENT_BIRTH then - eventName = "S_EVENT_BIRTH" - else - ctld.logTrace("Ignoring event %s", ctld.p(event)) - return - end - ctld.logDebug("caught event %s: %s", ctld.p(eventName), ctld.p(event)) - - -- find the originator unit - local unitName = nil - if event.initiator ~= nil and event.initiator.getName then - unitName = event.initiator:getName() - ctld.logDebug("unitName = [%s]", ctld.p(unitName)) - end - if not unitName then - ctld.logInfo("no unitname found in event %s", ctld.p(event)) - return - end - - local function processHumanPlayer() - ctld.logTrace("in the 'processHumanPlayer' function") - if mist.DBs.humansByName[unitName] then -- it's a human unit - ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) - local _unit = Unit.getByName(unitName) - if _unit ~= nil then - -- assign transport pilot - ctld.logTrace("_unit = %s", ctld.p(_unit)) - - local playerTypeName = _unit:getTypeName() - ctld.logTrace("playerTypeName = %s", ctld.p(playerTypeName)) - - -- Allow units to CTLD by aircraft type and not by pilot name - if ctld.addPlayerAircraftByType then - for _,aircraftType in pairs(ctld.aircraftTypeTable) do - if aircraftType == playerTypeName then - ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName)) - -- add transport unit to the list - table.insert(ctld.transportPilotNames, unitName) - -- add transport radio menu - ctld.addTransportF10MenuOptions(unitName) - break - end - end - else - for _, _unitName in pairs(ctld.transportPilotNames) do - if _unitName == unitName then - ctld.logTrace("adding by transportPilotNames, unitName = %s", ctld.p(unitName)) - ctld.addTransportF10MenuOptions(unitName) -- add transport radio menu - break - end - end - end - end + ctld.logTrace("ctld.eventHandler:onEvent()") + if event == nil then + ctld.logError("Event handler was called with a nil event!") + return end - end - if not mist.DBs.humansByName[unitName] then - -- give a few milliseconds for MiST to handle the BIRTH event too - ctld.logTrace("give MiST some time to handle the BIRTH event too") - timer.scheduleFunction(function() - ctld.logTrace("calling the 'processHumanPlayer' function in a timer") - processHumanPlayer() - end, nil, timer.getTime()+0.5) - else - ctld.logTrace("calling the 'processHumanPlayer' function immediately") - processHumanPlayer() - end + local eventName = "unknown" + -- check that we know the event + if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then + eventName = "S_EVENT_PLAYER_ENTER_UNIT" + elseif event.id == world.event.S_EVENT_BIRTH then + eventName = "S_EVENT_BIRTH" + else + ctld.logTrace("Ignoring event %s", ctld.p(event)) + return + end + ctld.logDebug("caught event %s: %s", ctld.p(eventName), ctld.p(event)) + + -- find the originator unit + local unitName = nil + if event.initiator ~= nil and event.initiator.getName then + unitName = event.initiator:getName() + ctld.logDebug("unitName = [%s]", ctld.p(unitName)) + end + if not unitName then + ctld.logInfo("no unitname found in event %s", ctld.p(event)) + return + end + + local function processHumanPlayer() + ctld.logTrace("in the 'processHumanPlayer' function") + if mist.DBs.humansByName[unitName] then -- it's a human unit + ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) + local _unit = Unit.getByName(unitName) + if _unit ~= nil then + -- assign transport pilot + ctld.logTrace("_unit = %s", ctld.p(_unit)) + + local playerTypeName = _unit:getTypeName() + ctld.logTrace("playerTypeName = %s", ctld.p(playerTypeName)) + + -- Allow units to CTLD by aircraft type and not by pilot name + if ctld.addPlayerAircraftByType then + for _,aircraftType in pairs(ctld.aircraftTypeTable) do + if aircraftType == playerTypeName then + ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName)) + -- add transport unit to the list + table.insert(ctld.transportPilotNames, unitName) + -- add transport radio menu + ctld.addTransportF10MenuOptions(unitName) + break + end + end + else + for _, _unitName in pairs(ctld.transportPilotNames) do + if _unitName == unitName then + ctld.logTrace("adding by transportPilotNames, unitName = %s", ctld.p(unitName)) + ctld.addTransportF10MenuOptions(unitName) -- add transport radio menu + break + end + end + end + end + end + end + + if not mist.DBs.humansByName[unitName] then + -- give a few milliseconds for MiST to handle the BIRTH event too + ctld.logTrace("give MiST some time to handle the BIRTH event too") + timer.scheduleFunction(function() + ctld.logTrace("calling the 'processHumanPlayer' function in a timer") + processHumanPlayer() + end, nil, timer.getTime()+0.5) + else + ctld.logTrace("calling the 'processHumanPlayer' function immediately") + processHumanPlayer() + end end function ctld.i18n_check(language, verbose) - local english = ctld.i18n["en"] - local tocheck = ctld.i18n[language] - if not tocheck then - ctld.logError(string.format("CTLD.i18n_check: Language %s not found", language)) - return false - end - local englishVersion = english.translation_version - local tocheckVersion = tocheck.translation_version - if englishVersion ~= tocheckVersion then - ctld.logError(string.format("CTLD.i18n_check: Language version mismatch: EN has version %s, %s has version %s", englishVersion, language, tocheckVersion)) - end - --ctld.logTrace(string.format("english = %s", ctld.p(english))) - for textRef, textEnglish in pairs(english) do - if textRef ~= "translation_version" then - local textTocheck = tocheck[textRef] - if not textTocheck then - ctld.logError(string.format( "CTLD.i18n_check: NOT FOUND: checking %s text [%s]", language, textRef)) - elseif textTocheck == textEnglish then - ctld.logWarning(string.format("CTLD.i18n_check: SAME: checking %s text [%s] as in EN", language, textRef)) - elseif verbose then - ctld.logInfo(string.format( "CTLD.i18n_check: OK: checking %s text [%s]", language, textRef)) - end + local english = ctld.i18n["en"] + local tocheck = ctld.i18n[language] + if not tocheck then + ctld.logError(string.format("CTLD.i18n_check: Language %s not found", language)) + return false + end + local englishVersion = english.translation_version + local tocheckVersion = tocheck.translation_version + if englishVersion ~= tocheckVersion then + ctld.logError(string.format("CTLD.i18n_check: Language version mismatch: EN has version %s, %s has version %s", englishVersion, language, tocheckVersion)) + end + --ctld.logTrace(string.format("english = %s", ctld.p(english))) + for textRef, textEnglish in pairs(english) do + if textRef ~= "translation_version" then + local textTocheck = tocheck[textRef] + if not textTocheck then + ctld.logError(string.format( "CTLD.i18n_check: NOT FOUND: checking %s text [%s]", language, textRef)) + elseif textTocheck == textEnglish then + ctld.logWarning(string.format("CTLD.i18n_check: SAME: checking %s text [%s] as in EN", language, textRef)) + elseif verbose then + ctld.logInfo(string.format( "CTLD.i18n_check: OK: checking %s text [%s]", language, textRef)) + end + end end - end end -- example of usage: @@ -8409,7 +8430,7 @@ env.setErrorMessageBoxEnabled(false) -- initialize CTLD -- if you need to have a chance to modify the configuration before initialization in your other scripts, please set ctld.dontInitialize to true and call ctld.initialize() manually if ctld.dontInitialize then - ctld.logInfo(string.format("Skipping initializion of version %s because ctld.dontInitialize is true", ctld.Version)) + ctld.logInfo(string.format("Skipping initializion of version %s because ctld.dontInitialize is true", ctld.Version)) else - ctld.initialize() + ctld.initialize() end \ No newline at end of file From fa1553a83ec28538f91eeaec96dc1375810cf8fb Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 24 Mar 2025 16:09:58 +0100 Subject: [PATCH 026/166] fixes --- CTLD.lua | 12061 ++++++++++++++++++++++++++--------------------------- 1 file changed, 5951 insertions(+), 6110 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index a891dc4..69823c8 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -29,9 +29,11 @@ Send beers (or kind messages) to Ciribob [on Discord](https://discordapp.com/users/204712384747536384), he's the reason we have CTLD ^^ ]] -if not ctld then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here - trigger.action.outText("\n\n** HEY MISSION-DESIGNER! **\n\nCTLD-i18n has not been loaded!\n\nMake sure CTLD-i18n is loaded\n*before* running this script!\n\nIt contains all the translations!\n", 10) - ctld = {} -- DONT REMOVE! +if not ctld then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here + trigger.action.outText( + "\n\n** HEY MISSION-DESIGNER! **\n\nCTLD-i18n has not been loaded!\n\nMake sure CTLD-i18n is loaded\n*before* running this script!\n\nIt contains all the translations!\n", + 10) + ctld = {} -- DONT REMOVE! end --- Identifier. All output in DCS.log will start with this. @@ -61,16 +63,18 @@ ctld.i18n_lang = "en" --====== Korean : 한국어 ==================================================================================== --ctld.i18n_lang = "ko" -if not ctld.i18n then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here - ctld.i18n = {} -- DONT REMOVE! +if not ctld.i18n then -- should be defined first by CTLD-i18n.lua, but just in case it's an old mission, let's keep it here + ctld.i18n = {} -- DONT REMOVE! end -- This is the default language -- If a string is not found in the current language then it will default to this language -- Note that no translation is provided for this language (obviously) but that we'll maintain this table to help the translators. ctld.i18n["en"] = {} -ctld.i18n["en"].translation_version = "1.4" -- make sure that all the translations are compatible with this version of the english language texts -local lang="en";env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) +ctld.i18n["en"].translation_version = +"1.4" -- make sure that all the translations are compatible with this version of the english language texts +local lang = "en"; env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, + tostring(ctld.i18n[lang].translation_version))) --- groups names ctld.i18n["en"]["Standard Group"] = "" @@ -230,7 +234,8 @@ ctld.i18n["en"]["1 FOB Crate oboard (%1 kg)\n"] = "" ctld.i18n["en"]["%1 crate onboard (%2 kg)\n"] = "" ctld.i18n["en"]["Total weight of cargo : %1 kg\n"] = "" ctld.i18n["en"]["No cargo."] = "" -ctld.i18n["en"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "" +ctld.i18n["en"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = +"" ctld.i18n["en"]["Loaded %1 crate!"] = "" ctld.i18n["en"]["Too low to hook %1 crate.\n\nHold hover for %2 seconds"] = "" ctld.i18n["en"]["Too high to hook %1 crate.\n\nHold hover for %2 seconds"] = "" @@ -251,11 +256,16 @@ ctld.i18n["en"]["%1 successfully deployed %2 to the field"] = "" ctld.i18n["en"]["No friendly crates close enough to unpack, or crate too close to aircraft."] = "" ctld.i18n["en"]["Finished building FOB! Crates and Troops can now be picked up."] = "" ctld.i18n["en"]["Finished building FOB! Crates can now be picked up."] = "" -ctld.i18n["en"]["%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke."] = "" -ctld.i18n["en"]["Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other"] = "" -ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands."] = "" -ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate."] = "" -ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."] = "" +ctld.i18n["en"]["%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke."] = +"" +ctld.i18n["en"]["Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other"] = +"" +ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands."] = +"" +ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate."] = +"" +ctld.i18n["en"]["You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."] = +"" ctld.i18n["en"]["%1 crate has been safely unhooked and is at your %2 o'clock"] = "" ctld.i18n["en"]["%1 crate has been safely dropped below you"] = "" ctld.i18n["en"]["You were too high! The crate has been destroyed"] = "" @@ -274,7 +284,8 @@ ctld.i18n["en"]["%1 successfully deployed a full %2 in the field. \n\nAA Active ctld.i18n["en"]["%1 successfully repaired a full %2 in the field."] = "" ctld.i18n["en"]["Cannot repair %1. No damaged %2 within 300m"] = "" ctld.i18n["en"]["%1 successfully deployed %2 to the field using %3 crates."] = "" -ctld.i18n["en"]["Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other"] = "" +ctld.i18n["en"]["Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other"] = +"" ctld.i18n["en"]["%1 dropped %2 smoke."] = "" --- JTAC messages @@ -355,36 +366,37 @@ ctld.i18n["en"]["STOP autoRefresh targets in LOS"] = "" ---@param ... any (list) The parameters to replace in the text, in order (all paremeters will be converted to string) ---@return string the translated and formatted text function ctld.i18n_translate(text, ...) - local _text + local _text - if not ctld.i18n[ctld.i18n_lang] then - env.info(string.format(" E - CTLD.i18n_translate: Language %s not found, defaulting to 'en'", tostring(ctld.i18n_lang))) - _text = ctld.i18n["en"][text] - else - _text = ctld.i18n[ctld.i18n_lang][text] + if not ctld.i18n[ctld.i18n_lang] then + env.info(string.format(" E - CTLD.i18n_translate: Language %s not found, defaulting to 'en'", + tostring(ctld.i18n_lang))) + _text = ctld.i18n["en"][text] + else + _text = ctld.i18n[ctld.i18n_lang][text] + end + + -- default to english + if _text == nil then + _text = ctld.i18n["en"][text] + end + + -- default to the provided text + if _text == nil or _text == "" then + _text = text + end + + if arg and arg.n and arg.n > 0 then + local _args = {} + for i = 1, arg.n do + _args[i] = tostring(arg[i]) or "" end - - -- default to english - if _text == nil then - _text = ctld.i18n["en"][text] + for i = 1, #_args do + _text = string.gsub(_text, "%%" .. i, _args[i]) end + end - -- default to the provided text - if _text == nil or _text == "" then - _text = text - end - - if arg and arg.n and arg.n > 0 then - local _args = {} - for i=1,arg.n do - _args[i] = tostring(arg[i]) or "" - end - for i = 1, #_args do - _text = string.gsub(_text, "%%" .. i, _args[i]) - end - end - - return _text + return _text end -- ************************************************************************ @@ -392,66 +404,68 @@ end -- ************************************************************************ ctld.staticBugWorkaround = false -- DCS had a bug where destroying statics would cause a crash. If this happens again, set this to TRUE -ctld.disableAllSmoke = false -- if true, all smoke is diabled at pickup and drop off zones regardless of settings below. Leave false to respect settings below +ctld.disableAllSmoke = false -- if true, all smoke is diabled at pickup and drop off zones regardless of settings below. Leave false to respect settings below -- Allow units to CTLD by aircraft type and not by pilot name - this is done everytime a player enters a new unit ctld.addPlayerAircraftByType = true -ctld.hoverPickup = true -- if set to false you can load crates with the F10 menu instead of hovering... Only if not using real crates! +ctld.hoverPickup = true -- if set to false you can load crates with the F10 menu instead of hovering... Only if not using real crates! ctld.loadCrateFromMenu = true -- if set to true, you can load crates with the F10 menu OR hovering, in case of using choppers and planes for example. -ctld.enableCrates = true -- if false, Helis will not be able to spawn or unpack crates so will be normal CTTS -ctld.enableAllCrates = true -- if false, the "all crates" menu items will not be displayed -ctld.slingLoad = false -- if false, crates can be used WITHOUT slingloading, by hovering above the crate, simulating slingloading but not the weight... +ctld.enableCrates = true -- if false, Helis will not be able to spawn or unpack crates so will be normal CTTS +ctld.enableAllCrates = true -- if false, the "all crates" menu items will not be displayed +ctld.slingLoad = false -- if false, crates can be used WITHOUT slingloading, by hovering above the crate, simulating slingloading but not the weight... -- There are some bug with Sling-loading that can cause crashes, if these occur set slingLoad to false -- to use the other method. -- Set staticBugFix to FALSE if use set ctld.slingLoad to TRUE -ctld.enableSmokeDrop = true -- if false, helis and c-130 will not be able to drop smoke -ctld.maxExtractDistance = 125 -- max distance from vehicle to troops to allow a group extraction -ctld.maximumDistanceLogistic = 200 -- max distance from vehicle to logistics to allow a loading or spawning operation -ctld.enableRepackingVehicles = true -- if true, vehicles can be repacked into crates -ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units -ctld.maximumSearchDistance = 4000 -- max distance for troops to search for enemy -ctld.maximumMoveDistance = 2000 -- max distance for troops to move from drop point if no enemy is nearby -ctld.minimumDeployDistance = 1000 -- minimum distance from a friendly pickup zone where you can deploy a crate -ctld.numberOfTroops = 10 -- default number of troops to load on a transport heli or C-130 - -- also works as maximum size of group that'll fit into a helicopter unless overridden -ctld.enableFastRopeInsertion = true -- allows you to drop troops by fast rope -ctld.fastRopeMaximumHeight = 18.28 -- in meters which is 60 ft max fast rope (not rappell) safe height -ctld.vehiclesForTransportRED = { "BRDM-2", "BTR_D" } -- vehicles to load onto Il-76 - Alternatives {"Strela-1 9P31","BMP-1"} +ctld.enableSmokeDrop = true -- if false, helis and c-130 will not be able to drop smoke +ctld.maxExtractDistance = 125 -- max distance from vehicle to troops to allow a group extraction +ctld.maximumDistanceLogistic = 200 -- max distance from vehicle to logistics to allow a loading or spawning operation +ctld.enableRepackingVehicles = true -- if true, vehicles can be repacked into crates +ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units +ctld.maximumSearchDistance = 4000 -- max distance for troops to search for enemy +ctld.maximumMoveDistance = 2000 -- max distance for troops to move from drop point if no enemy is nearby +ctld.minimumDeployDistance = 1000 -- minimum distance from a friendly pickup zone where you can deploy a crate +ctld.numberOfTroops = 10 -- default number of troops to load on a transport heli or C-130 +-- also works as maximum size of group that'll fit into a helicopter unless overridden +ctld.enableFastRopeInsertion = true -- allows you to drop troops by fast rope +ctld.fastRopeMaximumHeight = 18.28 -- in meters which is 60 ft max fast rope (not rappell) safe height +ctld.vehiclesForTransportRED = { "BRDM-2", "BTR_D" } -- vehicles to load onto Il-76 - Alternatives {"Strela-1 9P31","BMP-1"} ctld.vehiclesForTransportBLUE = { "M1045 HMMWV TOW", "M1043 HMMWV Armament" } -- vehicles to load onto c130 - Alternatives {"M1128 Stryker MGS","M1097 Avenger"} ctld.vehiclesWeight = { - ["BRDM-2"] = 7000, - ["BTR_D"] = 8000, - ["M1045 HMMWV TOW"] = 3220, - ["M1043 HMMWV Armament"] = 2500 + ["BRDM-2"] = 7000, + ["BTR_D"] = 8000, + ["M1045 HMMWV TOW"] = 3220, + ["M1043 HMMWV Armament"] = 2500 } 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! ctld.enabledFOBBuilding = true -- if true, you can load a crate INTO a C-130 than when unpacked creates a Forward Operating Base (FOB) which is a new place to spawn (crates) and carry crates from - -- In future i'd like it to be a FARP but so far that seems impossible... - -- You can also enable troop Pickup at FOBS +-- In future i'd like it to be a FARP but so far that seems impossible... +-- You can also enable troop Pickup at FOBS ctld.cratesRequiredForFOB = 3 -- The amount of crates required to build a FOB. Once built, helis can spawn crates at this outpost to be carried and deployed in another area. -- The large crates can only be loaded and dropped by large aircraft, like the C-130 and listed in ctld.vehicleTransportEnabled -- Small FOB crates can be moved by helicopter. The FOB will require ctld.cratesRequiredForFOB larges crates and small crates are 1/3 of a large fob crate -- To build the FOB entirely out of small crates you will need ctld.cratesRequiredForFOB * 3 -ctld.troopPickupAtFOB = true -- if true, troops can also be picked up at a created FOB -ctld.buildTimeFOB = 120 --time in seconds for the FOB to be built -ctld.crateWaitTime = 40 -- time in seconds to wait before you can spawn another crate -ctld.forceCrateToBeMoved = true -- a crate must be picked up at least once and moved before it can be unpacked. Helps to reduce crate spam -ctld.radioSound = "beacon.ogg" -- the name of the sound file to use for the FOB radio beacons. If this isnt added to the mission BEACONS WONT WORK! -ctld.radioSoundFC3 = "beaconsilent.ogg" -- name of the second silent radio file, used so FC3 aircraft dont hear ALL the beacon noises... :) -ctld.deployedBeaconBattery = 30 -- the battery on deployed beacons will last for this number minutes before needing to be re-deployed -ctld.enabledRadioBeaconDrop = true -- if its set to false then beacons cannot be dropped by units -ctld.allowRandomAiTeamPickups = false -- Allows the AI to randomize the loading of infantry teams (specified below) at pickup zones +ctld.troopPickupAtFOB = true -- if true, troops can also be picked up at a created FOB +ctld.buildTimeFOB = 120 --time in seconds for the FOB to be built +ctld.crateWaitTime = 40 -- time in seconds to wait before you can spawn another crate +ctld.forceCrateToBeMoved = true -- a crate must be picked up at least once and moved before it can be unpacked. Helps to reduce crate spam +ctld.radioSound = +"beacon.ogg" -- the name of the sound file to use for the FOB radio beacons. If this isnt added to the mission BEACONS WONT WORK! +ctld.radioSoundFC3 = +"beaconsilent.ogg" -- name of the second silent radio file, used so FC3 aircraft dont hear ALL the beacon noises... :) +ctld.deployedBeaconBattery = 30 -- the battery on deployed beacons will last for this number minutes before needing to be re-deployed +ctld.enabledRadioBeaconDrop = true -- if its set to false then beacons cannot be dropped by units +ctld.allowRandomAiTeamPickups = false -- Allows the AI to randomize the loading of infantry teams (specified below) at pickup zones -- Simulated Sling load configuration -ctld.minimumHoverHeight = 7.5 -- Lowest allowable height for crate hover -ctld.maximumHoverHeight = 12.0 -- Highest allowable height for crate hover +ctld.minimumHoverHeight = 7.5 -- Lowest allowable height for crate hover +ctld.maximumHoverHeight = 12.0 -- Highest allowable height for crate hover ctld.maxDistanceFromCrate = 5.5 -- Maximum distance from from crate for hover -ctld.hoverTime = 10 -- Time to hold hover above a crate for loading in seconds +ctld.hoverTime = 10 -- Time to hold hover above a crate for loading in seconds -- end of Simulated Sling load configuration @@ -463,7 +477,7 @@ ctld.aaLaunchers = 3 -- controls how many launchers to add to the AA systems whe -- When this limit is hit, a player will still be able to get crates for an AA system, just unable -- to unpack them -ctld.AASystemLimitRED = 20 -- Red side limit +ctld.AASystemLimitRED = 20 -- Red side limit ctld.AASystemLimitBLUE = 20 -- Blue side limit -- Allows players to create systems using as many crates as they like @@ -473,26 +487,26 @@ ctld.AASystemCrateStacking = false --END AA SYSTEM CONFIG ------------------------------------ -- ***************** JTAC CONFIGURATION ***************** -ctld.JTAC_LIMIT_RED = 10 -- max number of JTAC Crates for the RED 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_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_BLUE = true -- 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_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_smokeOffset_x = 0.0 -- distance in the X direction from target to smoke (meters) -ctld.JTAC_smokeOffset_y = 2.0 -- distance in the Y direction from target to smoke (meters) -ctld.JTAC_smokeOffset_z = 0.0 -- distance in the z direction from target to smoke (meters) -ctld.JTAC_jtacStatusF10 = true -- enables F10 JTAC Status menu -ctld.JTAC_location = true -- shows location of target in JTAC message -ctld.location_DMS = false -- shows coordinates as Degrees Minutes Seconds instead of Degrees Decimal minutes -ctld.JTAC_lock = "all" -- "vehicle" OR "troop" OR "all" forces JTAC to only lock vehicles or troops or all ground units -ctld.JTAC_allowStandbyMode = true -- if true, allow players to toggle lasing on/off +ctld.JTAC_LIMIT_RED = 10 -- max number of JTAC Crates for the RED 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_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_BLUE = true -- 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_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_smokeOffset_x = 0.0 -- distance in the X direction from target to smoke (meters) +ctld.JTAC_smokeOffset_y = 2.0 -- distance in the Y direction from target to smoke (meters) +ctld.JTAC_smokeOffset_z = 0.0 -- distance in the z direction from target to smoke (meters) +ctld.JTAC_jtacStatusF10 = true -- enables F10 JTAC Status menu +ctld.JTAC_location = true -- shows location of target in JTAC message +ctld.location_DMS = false -- shows coordinates as Degrees Minutes Seconds instead of Degrees Decimal minutes +ctld.JTAC_lock = "all" -- "vehicle" OR "troop" OR "all" forces JTAC to only lock vehicles or troops or all ground units +ctld.JTAC_allowStandbyMode = true -- if true, allow players to toggle lasing on/off ctld.JTAC_laseSpotCorrections = true -- if true, each JTAC will have a special option (toggle on/off) available in it's menu to attempt to lead the target, taking into account current wind conditions and the speed of the target (particularily useful against moving heavy armor) -ctld.JTAC_allowSmokeRequest = true -- if true, allow players to request a smoke on target (temporary) -ctld.JTAC_allow9Line = true -- if true, allow players to ask for a 9Line (individual) for a specific JTAC's target +ctld.JTAC_allowSmokeRequest = true -- if true, allow players to request a smoke on target (temporary) +ctld.JTAC_allow9Line = true -- if true, allow players to ask for a 9Line (individual) for a specific JTAC's target -- ***************** Pickup, dropoff and waypoint zones ***************** @@ -507,284 +521,284 @@ ctld.JTAC_allow9Line = true -- if true, allow players to ask for a 9Line (indivi -- Flag Number - Optional last field. If set the current number of groups remaining can be obtained from the flag value --pickupZones = { "Zone name or Ship Unit Name", "smoke color", "limit (-1 unlimited)", "ACTIVE (yes/no)", "side (0 = Both sides / 1 = Red / 2 = Blue )", flag number (optional) } ctld.pickupZones = { - { "pickzone1", "blue", -1, "yes", 0 }, - { "pickzone2", "red", -1, "yes", 0 }, - { "pickzone3", "none", -1, "yes", 0 }, - { "pickzone4", "none", -1, "yes", 0 }, - { "pickzone5", "none", -1, "yes", 0 }, - { "pickzone6", "none", -1, "yes", 0 }, - { "pickzone7", "none", -1, "yes", 0 }, - { "pickzone8", "none", -1, "yes", 0 }, - { "pickzone9", "none", 5, "yes", 1 }, -- limits pickup zone 9 to 5 groups of soldiers or vehicles, only red can pick up - { "pickzone10", "none", 10, "yes", 2 }, -- limits pickup zone 10 to 10 groups of soldiers or vehicles, only blue can pick up + { "pickzone1", "blue", -1, "yes", 0 }, + { "pickzone2", "red", -1, "yes", 0 }, + { "pickzone3", "none", -1, "yes", 0 }, + { "pickzone4", "none", -1, "yes", 0 }, + { "pickzone5", "none", -1, "yes", 0 }, + { "pickzone6", "none", -1, "yes", 0 }, + { "pickzone7", "none", -1, "yes", 0 }, + { "pickzone8", "none", -1, "yes", 0 }, + { "pickzone9", "none", 5, "yes", 1 }, -- limits pickup zone 9 to 5 groups of soldiers or vehicles, only red can pick up + { "pickzone10", "none", 10, "yes", 2 }, -- limits pickup zone 10 to 10 groups of soldiers or vehicles, only blue can pick up - { "pickzone11", "blue", 20, "no", 2 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! - { "pickzone12", "red", 20, "no", 1 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! - { "pickzone13", "none", -1, "yes", 0 }, - { "pickzone14", "none", -1, "yes", 0 }, - { "pickzone15", "none", -1, "yes", 0 }, - { "pickzone16", "none", -1, "yes", 0 }, - { "pickzone17", "none", -1, "yes", 0 }, - { "pickzone18", "none", -1, "yes", 0 }, - { "pickzone19", "none", 5, "yes", 0 }, - { "pickzone20", "none", 10, "yes", 0, 1000 }, -- optional extra flag number to store the current number of groups available in + { "pickzone11", "blue", 20, "no", 2 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! + { "pickzone12", "red", 20, "no", 1 }, -- limits pickup zone 11 to 20 groups of soldiers or vehicles, only blue can pick up. Zone starts inactive! + { "pickzone13", "none", -1, "yes", 0 }, + { "pickzone14", "none", -1, "yes", 0 }, + { "pickzone15", "none", -1, "yes", 0 }, + { "pickzone16", "none", -1, "yes", 0 }, + { "pickzone17", "none", -1, "yes", 0 }, + { "pickzone18", "none", -1, "yes", 0 }, + { "pickzone19", "none", 5, "yes", 0 }, + { "pickzone20", "none", 10, "yes", 0, 1000 }, -- optional extra flag number to store the current number of groups available in - { "USA Carrier", "blue", 10, "yes", 0, 1001 }, -- instead of a Zone Name you can also use the UNIT NAME of a ship + { "USA Carrier", "blue", 10, "yes", 0, 1001 }, -- instead of a Zone Name you can also use the UNIT NAME of a ship } -- dropOffZones = {"name","smoke colour",0,side 1 = Red or 2 = Blue or 0 = Both sides} ctld.dropOffZones = { - { "dropzone1", "green", 2 }, - { "dropzone2", "blue", 2 }, - { "dropzone3", "orange", 2 }, - { "dropzone4", "none", 2 }, - { "dropzone5", "none", 1 }, - { "dropzone6", "none", 1 }, - { "dropzone7", "none", 1 }, - { "dropzone8", "none", 1 }, - { "dropzone9", "none", 1 }, - { "dropzone10", "none", 1 }, + { "dropzone1", "green", 2 }, + { "dropzone2", "blue", 2 }, + { "dropzone3", "orange", 2 }, + { "dropzone4", "none", 2 }, + { "dropzone5", "none", 1 }, + { "dropzone6", "none", 1 }, + { "dropzone7", "none", 1 }, + { "dropzone8", "none", 1 }, + { "dropzone9", "none", 1 }, + { "dropzone10", "none", 1 }, } --wpZones = { "Zone name", "smoke color", "ACTIVE (yes/no)", "side (0 = Both sides / 1 = Red / 2 = Blue )", } ctld.wpZones = { - { "wpzone1", "green","yes", 2 }, - { "wpzone2", "blue","yes", 2 }, - { "wpzone3", "orange","yes", 2 }, - { "wpzone4", "none","yes", 2 }, - { "wpzone5", "none","yes", 2 }, - { "wpzone6", "none","yes", 1 }, - { "wpzone7", "none","yes", 1 }, - { "wpzone8", "none","yes", 1 }, - { "wpzone9", "none","yes", 1 }, - { "wpzone10", "none","no", 0 }, -- Both sides as its set to 0 + { "wpzone1", "green", "yes", 2 }, + { "wpzone2", "blue", "yes", 2 }, + { "wpzone3", "orange", "yes", 2 }, + { "wpzone4", "none", "yes", 2 }, + { "wpzone5", "none", "yes", 2 }, + { "wpzone6", "none", "yes", 1 }, + { "wpzone7", "none", "yes", 1 }, + { "wpzone8", "none", "yes", 1 }, + { "wpzone9", "none", "yes", 1 }, + { "wpzone10", "none", "no", 0 }, -- Both sides as its set to 0 } -- ******************** Transports names ********************** -- If ctld.addPlayerAircraftByType = True, comment or uncomment lines to allow aircraft's type carry CTLD ctld.aircraftTypeTable = { - --%%%%% MODS %%%%% - --"Bronco-OV-10A", - --"Hercules", - --"SK-60", - --"UH-60L", - --"T-45", + --%%%%% MODS %%%%% + --"Bronco-OV-10A", + --"Hercules", + --"SK-60", + --"UH-60L", + --"T-45", - --%%%%% CHOPPERS %%%%% - --"Ka-50", - --"Ka-50_3", - "Mi-8MT", - "Mi-24P", - --"SA342L", - --"SA342M", - --"SA342Mistral", - --"SA342Minigun", - "UH-1H", - "CH-47Fbl1", + --%%%%% CHOPPERS %%%%% + --"Ka-50", + --"Ka-50_3", + "Mi-8MT", + "Mi-24P", + --"SA342L", + --"SA342M", + --"SA342Mistral", + --"SA342Minigun", + "UH-1H", + "CH-47Fbl1", - --%%%%% AIRCRAFTS %%%%% - --"C-101EB", - --"C-101CC", - --"Christen Eagle II", - --"L-39C", - --"L-39ZA", - --"MB-339A", - --"MB-339APAN", - --"Mirage-F1B", - --"Mirage-F1BD", - --"Mirage-F1BE", - --"Mirage-F1BQ", - --"Mirage-F1DDA", - --"Su-25T", - --"Yak-52", + --%%%%% AIRCRAFTS %%%%% + --"C-101EB", + --"C-101CC", + --"Christen Eagle II", + --"L-39C", + --"L-39ZA", + --"MB-339A", + --"MB-339APAN", + --"Mirage-F1B", + --"Mirage-F1BD", + --"Mirage-F1BE", + --"Mirage-F1BQ", + --"Mirage-F1DDA", + --"Su-25T", + --"Yak-52", - --%%%%% WARBIRDS %%%%% - --"Bf-109K-4", - --"Fw 190A8", - --"FW-190D9", - --"I-16", - --"MosquitoFBMkVI", - --"P-47D-30", - --"P-47D-40", - --"P-51D", - --"P-51D-30-NA", - --"SpitfireLFMkIX", - --"SpitfireLFMkIXCW", - --"TF-51D", + --%%%%% WARBIRDS %%%%% + --"Bf-109K-4", + --"Fw 190A8", + --"FW-190D9", + --"I-16", + --"MosquitoFBMkVI", + --"P-47D-30", + --"P-47D-40", + --"P-51D", + --"P-51D-30-NA", + --"SpitfireLFMkIX", + --"SpitfireLFMkIXCW", + --"TF-51D", } -- Use any of the predefined names or set your own ones ctld.transportPilotNames = { - "helicargo1", - "helicargo2", - "helicargo3", - "helicargo4", - "helicargo5", - "helicargo6", - "helicargo7", - "helicargo8", - "helicargo9", - "helicargo10", + "helicargo1", + "helicargo2", + "helicargo3", + "helicargo4", + "helicargo5", + "helicargo6", + "helicargo7", + "helicargo8", + "helicargo9", + "helicargo10", - "helicargo11", - "helicargo12", - "helicargo13", - "helicargo14", - "helicargo15", - "helicargo16", - "helicargo17", - "helicargo18", - "helicargo19", - "helicargo20", + "helicargo11", + "helicargo12", + "helicargo13", + "helicargo14", + "helicargo15", + "helicargo16", + "helicargo17", + "helicargo18", + "helicargo19", + "helicargo20", - "helicargo21", - "helicargo22", - "helicargo23", - "helicargo24", - "helicargo25", + "helicargo21", + "helicargo22", + "helicargo23", + "helicargo24", + "helicargo25", - "MEDEVAC #1", - "MEDEVAC #2", - "MEDEVAC #3", - "MEDEVAC #4", - "MEDEVAC #5", - "MEDEVAC #6", - "MEDEVAC #7", - "MEDEVAC #8", - "MEDEVAC #9", - "MEDEVAC #10", - "MEDEVAC #11", - "MEDEVAC #12", - "MEDEVAC #13", - "MEDEVAC #14", - "MEDEVAC #15", - "MEDEVAC #16", + "MEDEVAC #1", + "MEDEVAC #2", + "MEDEVAC #3", + "MEDEVAC #4", + "MEDEVAC #5", + "MEDEVAC #6", + "MEDEVAC #7", + "MEDEVAC #8", + "MEDEVAC #9", + "MEDEVAC #10", + "MEDEVAC #11", + "MEDEVAC #12", + "MEDEVAC #13", + "MEDEVAC #14", + "MEDEVAC #15", + "MEDEVAC #16", - "MEDEVAC RED #1", - "MEDEVAC RED #2", - "MEDEVAC RED #3", - "MEDEVAC RED #4", - "MEDEVAC RED #5", - "MEDEVAC RED #6", - "MEDEVAC RED #7", - "MEDEVAC RED #8", - "MEDEVAC RED #9", - "MEDEVAC RED #10", - "MEDEVAC RED #11", - "MEDEVAC RED #12", - "MEDEVAC RED #13", - "MEDEVAC RED #14", - "MEDEVAC RED #15", - "MEDEVAC RED #16", - "MEDEVAC RED #17", - "MEDEVAC RED #18", - "MEDEVAC RED #19", - "MEDEVAC RED #20", - "MEDEVAC RED #21", + "MEDEVAC RED #1", + "MEDEVAC RED #2", + "MEDEVAC RED #3", + "MEDEVAC RED #4", + "MEDEVAC RED #5", + "MEDEVAC RED #6", + "MEDEVAC RED #7", + "MEDEVAC RED #8", + "MEDEVAC RED #9", + "MEDEVAC RED #10", + "MEDEVAC RED #11", + "MEDEVAC RED #12", + "MEDEVAC RED #13", + "MEDEVAC RED #14", + "MEDEVAC RED #15", + "MEDEVAC RED #16", + "MEDEVAC RED #17", + "MEDEVAC RED #18", + "MEDEVAC RED #19", + "MEDEVAC RED #20", + "MEDEVAC RED #21", - "MEDEVAC BLUE #1", - "MEDEVAC BLUE #2", - "MEDEVAC BLUE #3", - "MEDEVAC BLUE #4", - "MEDEVAC BLUE #5", - "MEDEVAC BLUE #6", - "MEDEVAC BLUE #7", - "MEDEVAC BLUE #8", - "MEDEVAC BLUE #9", - "MEDEVAC BLUE #10", - "MEDEVAC BLUE #11", - "MEDEVAC BLUE #12", - "MEDEVAC BLUE #13", - "MEDEVAC BLUE #14", - "MEDEVAC BLUE #15", - "MEDEVAC BLUE #16", - "MEDEVAC BLUE #17", - "MEDEVAC BLUE #18", - "MEDEVAC BLUE #19", - "MEDEVAC BLUE #20", - "MEDEVAC BLUE #21", + "MEDEVAC BLUE #1", + "MEDEVAC BLUE #2", + "MEDEVAC BLUE #3", + "MEDEVAC BLUE #4", + "MEDEVAC BLUE #5", + "MEDEVAC BLUE #6", + "MEDEVAC BLUE #7", + "MEDEVAC BLUE #8", + "MEDEVAC BLUE #9", + "MEDEVAC BLUE #10", + "MEDEVAC BLUE #11", + "MEDEVAC BLUE #12", + "MEDEVAC BLUE #13", + "MEDEVAC BLUE #14", + "MEDEVAC BLUE #15", + "MEDEVAC BLUE #16", + "MEDEVAC BLUE #17", + "MEDEVAC BLUE #18", + "MEDEVAC BLUE #19", + "MEDEVAC BLUE #20", + "MEDEVAC BLUE #21", - -- *** AI transports names (different names only to ease identification in mission) *** + -- *** AI transports names (different names only to ease identification in mission) *** - -- Use any of the predefined names or set your own ones - "transport1", - "transport2", - "transport3", - "transport4", - "transport5", - "transport6", - "transport7", - "transport8", - "transport9", - "transport10", + -- Use any of the predefined names or set your own ones + "transport1", + "transport2", + "transport3", + "transport4", + "transport5", + "transport6", + "transport7", + "transport8", + "transport9", + "transport10", - "transport11", - "transport12", - "transport13", - "transport14", - "transport15", - "transport16", - "transport17", - "transport18", - "transport19", - "transport20", + "transport11", + "transport12", + "transport13", + "transport14", + "transport15", + "transport16", + "transport17", + "transport18", + "transport19", + "transport20", - "transport21", - "transport22", - "transport23", - "transport24", - "transport25", + "transport21", + "transport22", + "transport23", + "transport24", + "transport25", } -- *************** Optional Extractable GROUPS ***************** -- Use any of the predefined names or set your own ones ctld.extractableGroups = { - "extract1", - "extract2", - "extract3", - "extract4", - "extract5", - "extract6", - "extract7", - "extract8", - "extract9", - "extract10", + "extract1", + "extract2", + "extract3", + "extract4", + "extract5", + "extract6", + "extract7", + "extract8", + "extract9", + "extract10", - "extract11", - "extract12", - "extract13", - "extract14", - "extract15", - "extract16", - "extract17", - "extract18", - "extract19", - "extract20", + "extract11", + "extract12", + "extract13", + "extract14", + "extract15", + "extract16", + "extract17", + "extract18", + "extract19", + "extract20", - "extract21", - "extract22", - "extract23", - "extract24", - "extract25", + "extract21", + "extract22", + "extract23", + "extract24", + "extract25", } -- ************** Logistics UNITS FOR CRATE SPAWNING ****************** -- Use any of the predefined names or set your own ones -- When a logistic unit is destroyed, you will no longer be able to spawn crates -ctld.dynamicLogisticUnitsIndex = 0 -- This is the unit that will be spawned first and then subsequent units will be from the next in the list +ctld.dynamicLogisticUnitsIndex = 0 -- This is the unit that will be spawned first and then subsequent units will be from the next in the list ctld.logisticUnits = { - "logistic1", - "logistic2", - "logistic3", - "logistic4", - "logistic5", - "logistic6", - "logistic7", - "logistic8", - "logistic9", - "logistic10", + "logistic1", + "logistic2", + "logistic3", + "logistic4", + "logistic5", + "logistic6", + "logistic7", + "logistic8", + "logistic9", + "logistic10", } -- ************** UNITS ABLE TO TRANSPORT VEHICLES ****************** @@ -792,9 +806,9 @@ ctld.logisticUnits = { -- units db has all the names or you can extract a mission.miz file by making it a zip and looking -- in the contained mission file ctld.vehicleTransportEnabled = { - "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR - "Hercules", - "UH-1H", + "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR + "Hercules", + "UH-1H", } -- ************** Units able to use DCS dynamic cargo system ****************** @@ -802,7 +816,7 @@ ctld.vehicleTransportEnabled = { -- Units listed here will spawn a cargo static that can be loaded with the standard DCS cargo system -- We will also use this to make modifications to the menu and other checks and messages ctld.dynamicCargoUnits = { - "CH-47Fbl1", + "CH-47Fbl1", } -- ************** Maximum Units SETUP for UNITS ****************** @@ -814,65 +828,65 @@ ctld.dynamicCargoUnits = { -- Make sure the unit name is exactly right or it wont work ctld.unitLoadLimits = { - -- Remove the -- below to turn on options - -- ["SA342Mistral"] = 4, - -- ["SA342L"] = 4, - -- ["SA342M"] = 4, + -- Remove the -- below to turn on options + -- ["SA342Mistral"] = 4, + -- ["SA342L"] = 4, + -- ["SA342M"] = 4, - --%%%%% MODS %%%%% - --["Bronco-OV-10A"] = 4, - ["Hercules"] = 30, - --["SK-60"] = 1, - ["UH-60L"] = 12, - --["T-45"] = 1, + --%%%%% MODS %%%%% + --["Bronco-OV-10A"] = 4, + ["Hercules"] = 30, + --["SK-60"] = 1, + ["UH-60L"] = 12, + --["T-45"] = 1, - --%%%%% CHOPPERS %%%%% - ["Mi-8MT"] = 16, - ["Mi-24P"] = 10, - --["SA342L"] = 4, - --["SA342M"] = 4, - --["SA342Mistral"] = 4, - --["SA342Minigun"] = 3, - ["UH-1H"] = 8, - ["CH-47Fbl1"] = 33, + --%%%%% CHOPPERS %%%%% + ["Mi-8MT"] = 16, + ["Mi-24P"] = 10, + --["SA342L"] = 4, + --["SA342M"] = 4, + --["SA342Mistral"] = 4, + --["SA342Minigun"] = 3, + ["UH-1H"] = 8, + ["CH-47Fbl1"] = 33, - --%%%%% AIRCRAFTS %%%%% - --["C-101EB"] = 1, - --["C-101CC"] = 1, - --["Christen Eagle II"] = 1, - --["L-39C"] = 1, - --["L-39ZA"] = 1, - --["MB-339A"] = 1, - --["MB-339APAN"] = 1, - --["Mirage-F1B"] = 1, - --["Mirage-F1BD"] = 1, - --["Mirage-F1BE"] = 1, - --["Mirage-F1BQ"] = 1, - --["Mirage-F1DDA"] = 1, - --["Su-25T"] = 1, - --["Yak-52"] = 1, + --%%%%% AIRCRAFTS %%%%% + --["C-101EB"] = 1, + --["C-101CC"] = 1, + --["Christen Eagle II"] = 1, + --["L-39C"] = 1, + --["L-39ZA"] = 1, + --["MB-339A"] = 1, + --["MB-339APAN"] = 1, + --["Mirage-F1B"] = 1, + --["Mirage-F1BD"] = 1, + --["Mirage-F1BE"] = 1, + --["Mirage-F1BQ"] = 1, + --["Mirage-F1DDA"] = 1, + --["Su-25T"] = 1, + --["Yak-52"] = 1, - --%%%%% WARBIRDS %%%%% - --["Bf-109K-4"] = 1, - --["Fw 190A8"] = 1, - --["FW-190D9"] = 1, - --["I-16"] = 1, - --["MosquitoFBMkVI"] = 1, - --["P-47D-30"] = 1, - --["P-47D-40"] = 1, - --["P-51D"] = 1, - --["P-51D-30-NA"] = 1, - --["SpitfireLFMkIX"] = 1, - --["SpitfireLFMkIXCW"] = 1, - --["TF-51D"] = 1, + --%%%%% WARBIRDS %%%%% + --["Bf-109K-4"] = 1, + --["Fw 190A8"] = 1, + --["FW-190D9"] = 1, + --["I-16"] = 1, + --["MosquitoFBMkVI"] = 1, + --["P-47D-30"] = 1, + --["P-47D-40"] = 1, + --["P-51D"] = 1, + --["P-51D-30-NA"] = 1, + --["SpitfireLFMkIX"] = 1, + --["SpitfireLFMkIXCW"] = 1, + --["TF-51D"] = 1, } -- Put the name of the Unit you want to enable loading multiple crates ctld.internalCargoLimits = { - -- Remove the -- below to turn on options - ["Mi-8MT"] = 2, - ["CH-47Fbl1"] = 8, + -- Remove the -- below to turn on options + ["Mi-8MT"] = 2, + ["CH-47Fbl1"] = 8, } @@ -891,59 +905,59 @@ ctld.internalCargoLimits = { ctld.unitActions = { - -- Remove the -- below to turn on options - -- ["SA342Mistral"] = {crates=true, troops=true}, - -- ["SA342L"] = {crates=false, troops=true}, - -- ["SA342M"] = {crates=false, troops=true}, + -- Remove the -- below to turn on options + -- ["SA342Mistral"] = {crates=true, troops=true}, + -- ["SA342L"] = {crates=false, troops=true}, + -- ["SA342M"] = {crates=false, troops=true}, - --%%%%% MODS %%%%% - --["Bronco-OV-10A"] = {crates=true, troops=true}, - ["Hercules"] = {crates=true, troops=true}, - ["SK-60"] = {crates=true, troops=true}, - ["UH-60L"] = {crates=true, troops=true}, - --["T-45"] = {crates=true, troops=true}, + --%%%%% MODS %%%%% + --["Bronco-OV-10A"] = {crates=true, troops=true}, + ["Hercules"] = { crates = true, troops = true }, + ["SK-60"] = { crates = true, troops = true }, + ["UH-60L"] = { crates = true, troops = true }, + --["T-45"] = {crates=true, troops=true}, - --%%%%% CHOPPERS %%%%% - --["Ka-50"] = {crates=true, troops=false}, - --["Ka-50_3"] = {crates=true, troops=false}, - ["Mi-8MT"] = {crates=true, troops=true}, - ["Mi-24P"] = {crates=true, troops=true}, - --["SA342L"] = {crates=false, troops=true}, - --["SA342M"] = {crates=false, troops=true}, - --["SA342Mistral"] = {crates=false, troops=true}, - --["SA342Minigun"] = {crates=false, troops=true}, - ["UH-1H"] = {crates=true, troops=true}, - ["CH-47Fbl1"] = {crates=true, troops=true}, + --%%%%% CHOPPERS %%%%% + --["Ka-50"] = {crates=true, troops=false}, + --["Ka-50_3"] = {crates=true, troops=false}, + ["Mi-8MT"] = { crates = true, troops = true }, + ["Mi-24P"] = { crates = true, troops = true }, + --["SA342L"] = {crates=false, troops=true}, + --["SA342M"] = {crates=false, troops=true}, + --["SA342Mistral"] = {crates=false, troops=true}, + --["SA342Minigun"] = {crates=false, troops=true}, + ["UH-1H"] = { crates = true, troops = true }, + ["CH-47Fbl1"] = { crates = true, troops = true }, - --%%%%% AIRCRAFTS %%%%% - --["C-101EB"] = {crates=true, troops=true}, - --["C-101CC"] = {crates=true, troops=true}, - --["Christen Eagle II"] = {crates=true, troops=true}, - --["L-39C"] = {crates=true, troops=true}, - --["L-39ZA"] = {crates=true, troops=true}, - --["MB-339A"] = {crates=true, troops=true}, - --["MB-339APAN"] = {crates=true, troops=true}, - --["Mirage-F1B"] = {crates=true, troops=true}, - --["Mirage-F1BD"] = {crates=true, troops=true}, - --["Mirage-F1BE"] = {crates=true, troops=true}, - --["Mirage-F1BQ"] = {crates=true, troops=true}, - --["Mirage-F1DDA"] = {crates=true, troops=true}, - --["Su-25T"]= {crates=true, troops=false}, - --["Yak-52"] = {crates=true, troops=true}, + --%%%%% AIRCRAFTS %%%%% + --["C-101EB"] = {crates=true, troops=true}, + --["C-101CC"] = {crates=true, troops=true}, + --["Christen Eagle II"] = {crates=true, troops=true}, + --["L-39C"] = {crates=true, troops=true}, + --["L-39ZA"] = {crates=true, troops=true}, + --["MB-339A"] = {crates=true, troops=true}, + --["MB-339APAN"] = {crates=true, troops=true}, + --["Mirage-F1B"] = {crates=true, troops=true}, + --["Mirage-F1BD"] = {crates=true, troops=true}, + --["Mirage-F1BE"] = {crates=true, troops=true}, + --["Mirage-F1BQ"] = {crates=true, troops=true}, + --["Mirage-F1DDA"] = {crates=true, troops=true}, + --["Su-25T"]= {crates=true, troops=false}, + --["Yak-52"] = {crates=true, troops=true}, - --%%%%% WARBIRDS %%%%% - --["Bf-109K-4"] = {crates=true, troops=false}, - --["Fw 190A8"] = {crates=true, troops=false}, - --["FW-190D9"] = {crates=true, troops=false}, - --["I-16"] = {crates=true, troops=false}, - --["MosquitoFBMkVI"] = {crates=true, troops=true}, - --["P-47D-30"] = {crates=true, troops=false}, - --["P-47D-40"] = {crates=true, troops=false}, - --["P-51D"] = {crates=true, troops=false}, - --["P-51D-30-NA"] = {crates=true, troops=false}, - --["SpitfireLFMkIX"] = {crates=true, troops=false}, - --["SpitfireLFMkIXCW"] = {crates=true, troops=false}, - --["TF-51D"] = {crates=true, troops=true}, + --%%%%% WARBIRDS %%%%% + --["Bf-109K-4"] = {crates=true, troops=false}, + --["Fw 190A8"] = {crates=true, troops=false}, + --["FW-190D9"] = {crates=true, troops=false}, + --["I-16"] = {crates=true, troops=false}, + --["MosquitoFBMkVI"] = {crates=true, troops=true}, + --["P-47D-30"] = {crates=true, troops=false}, + --["P-47D-40"] = {crates=true, troops=false}, + --["P-51D"] = {crates=true, troops=false}, + --["P-51D-30-NA"] = {crates=true, troops=false}, + --["SpitfireLFMkIX"] = {crates=true, troops=false}, + --["SpitfireLFMkIXCW"] = {crates=true, troops=false}, + --["TF-51D"] = {crates=true, troops=true}, } -- ************** WEIGHT CALCULATIONS FOR INFANTRY GROUPS ****************** @@ -958,13 +972,13 @@ ctld.unitActions = { -- Mortar servants carry their tube and a few rounds (ctld.MORTAR_WEIGHT) ctld.SOLDIER_WEIGHT = 80 -- kg, will be randomized between 90% and 120% -ctld.KIT_WEIGHT = 20 -- kg -ctld.RIFLE_WEIGHT = 5 -- kg -ctld.MANPAD_WEIGHT = 18 -- kg -ctld.RPG_WEIGHT = 7.6 -- kg -ctld.MG_WEIGHT = 10 -- kg -ctld.MORTAR_WEIGHT = 26 -- kg -ctld.JTAC_WEIGHT = 15 -- kg +ctld.KIT_WEIGHT = 20 -- kg +ctld.RIFLE_WEIGHT = 5 -- kg +ctld.MANPAD_WEIGHT = 18 -- kg +ctld.RPG_WEIGHT = 7.6 -- kg +ctld.MG_WEIGHT = 10 -- kg +ctld.MORTAR_WEIGHT = 26 -- kg +ctld.JTAC_WEIGHT = 15 -- kg -- ************** INFANTRY GROUPS FOR PICKUP ****************** -- Unit Types @@ -978,22 +992,22 @@ ctld.JTAC_WEIGHT = 15 -- kg -- You can also add an optional coalition side to limit the group to one side -- for the side - 2 is BLUE and 1 is RED ctld.loadableGroups = { - {name = ctld.i18n_translate("Standard Group"), inf = 6, mg = 2, at = 2 }, -- will make a loadable group with 6 infantry, 2 MGs and 2 anti-tank for both coalitions - {name = ctld.i18n_translate("Anti Air"), inf = 2, aa = 3 }, - {name = ctld.i18n_translate("Anti Tank"), inf = 2, at = 6 }, - {name = ctld.i18n_translate("Mortar Squad"), mortar = 6 }, - {name = ctld.i18n_translate("JTAC Group"), inf = 4, jtac = 1 }, -- will make a loadable group with 4 infantry and a JTAC soldier for both coalitions - {name = ctld.i18n_translate("Single JTAC"), jtac = 1 }, -- will make a loadable group witha single JTAC soldier for both coalitions - {name = ctld.i18n_translate("2x - Standard Groups"), inf = 12, mg = 4, at = 4 }, - {name = ctld.i18n_translate("2x - Anti Air"), inf = 4, aa = 6 }, - {name = ctld.i18n_translate("2x - Anti Tank"), inf = 4, at = 12 }, - {name = ctld.i18n_translate("2x - Standard Groups + 2x Mortar"), inf = 12, mg = 4, at = 4, mortar = 12 }, - {name = ctld.i18n_translate("3x - Standard Groups"), inf = 18, mg = 6, at = 6 }, - {name = ctld.i18n_translate("3x - Anti Air"), inf = 6, aa = 9 }, - {name = ctld.i18n_translate("3x - Anti Tank"), inf = 6, at = 18 }, - {name = ctld.i18n_translate("3x - Mortar Squad"), mortar = 18}, - {name = ctld.i18n_translate("5x - Mortar Squad"), mortar = 30}, - -- {name = ctld.i18n_translate("Mortar Squad Red"), inf = 2, mortar = 5, side =1 }, --would make a group loadable by RED only + { name = ctld.i18n_translate("Standard Group"), inf = 6, mg = 2, at = 2 }, -- will make a loadable group with 6 infantry, 2 MGs and 2 anti-tank for both coalitions + { name = ctld.i18n_translate("Anti Air"), inf = 2, aa = 3 }, + { name = ctld.i18n_translate("Anti Tank"), inf = 2, at = 6 }, + { name = ctld.i18n_translate("Mortar Squad"), mortar = 6 }, + { name = ctld.i18n_translate("JTAC Group"), inf = 4, jtac = 1 }, -- will make a loadable group with 4 infantry and a JTAC soldier for both coalitions + { name = ctld.i18n_translate("Single JTAC"), jtac = 1 }, -- will make a loadable group witha single JTAC soldier for both coalitions + { name = ctld.i18n_translate("2x - Standard Groups"), inf = 12, mg = 4, at = 4 }, + { name = ctld.i18n_translate("2x - Anti Air"), inf = 4, aa = 6 }, + { name = ctld.i18n_translate("2x - Anti Tank"), inf = 4, at = 12 }, + { name = ctld.i18n_translate("2x - Standard Groups + 2x Mortar"), inf = 12, mg = 4, at = 4, mortar = 12 }, + { name = ctld.i18n_translate("3x - Standard Groups"), inf = 18, mg = 6, at = 6 }, + { name = ctld.i18n_translate("3x - Anti Air"), inf = 6, aa = 9 }, + { name = ctld.i18n_translate("3x - Anti Tank"), inf = 6, at = 18 }, + { name = ctld.i18n_translate("3x - Mortar Squad"), mortar = 18 }, + { name = ctld.i18n_translate("5x - Mortar Squad"), mortar = 30 }, + -- {name = ctld.i18n_translate("Mortar Squad Red"), inf = 2, mortar = 5, side =1 }, --would make a group loadable by RED only } -- ************** SPAWNABLE CRATES ****************** @@ -1001,182 +1015,182 @@ ctld.loadableGroups = { -- when we unpack -- ctld.spawnableCrates = { - -- name of the sub menu on F10 for spawning crates - ["Combat Vehicles"] = { - --crates you can spawn - -- weight in KG - -- Desc is the description on the F10 MENU - -- unit is the model name of the unit to spawn - -- cratesRequired - if set requires that many crates of the same type within 100m of each other in order build the unit - -- side is optional but 2 is BLUE and 1 is RED + -- name of the sub menu on F10 for spawning crates + ["Combat Vehicles"] = { + --crates you can spawn + -- weight in KG + -- Desc is the description on the F10 MENU + -- unit is the model name of the unit to spawn + -- cratesRequired - if set requires that many crates of the same type within 100m of each other in order build the unit + -- side is optional but 2 is BLUE and 1 is RED - -- Some descriptions are filtered to determine if JTAC or not! + -- Some descriptions are filtered to determine if JTAC or not! - --- BLUE - { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types - { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, - { multiple = {1000.02, 1000.02}, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, - { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit="MaxxPro_MRAP", side = 2, cratesRequired = 2 }, - { multiple = {1000.03, 1000.03}, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, - { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit="LAV-25", side = 2, cratesRequired = 3 }, - { multiple = {1000.04, 1000.04, 1000.04}, desc = ctld.i18n_translate("Med Tank - LAV-25 - All crates"), side = 2 }, - { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit="M1A2C_SEP_V3", side = 2, cratesRequired = 4 }, - { multiple = {1000.05, 1000.05, 1000.05, 1000.05}, desc = ctld.i18n_translate("Heavy Tank - Abrams - All crates"), side = 2 }, + --- BLUE + { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types + { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, + { multiple = { 1000.02, 1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, + { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit = "MaxxPro_MRAP", side = 2, cratesRequired = 2 }, + { multiple = { 1000.03, 1000.03 }, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, + { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit = "LAV-25", side = 2, cratesRequired = 3 }, + { multiple = { 1000.04, 1000.04, 1000.04 }, desc = ctld.i18n_translate("Med Tank - LAV-25 - All crates"), side = 2 }, + { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit = "M1A2C_SEP_V3", side = 2, cratesRequired = 4 }, + { multiple = { 1000.05, 1000.05, 1000.05, 1000.05 }, desc = ctld.i18n_translate("Heavy Tank - Abrams - All crates"), side = 2 }, - --- RED - { weight = 1000.11, desc = ctld.i18n_translate("BTR-D"), unit = "BTR_D", side = 1 }, - { weight = 1000.12, desc = ctld.i18n_translate("BRDM-2"), unit = "BRDM-2", side = 1 }, - -- need more redfor! - }, - ["Support"] = { - --- BLUE - { weight = 1001.01, desc = ctld.i18n_translate("Hummer - JTAC"), unit = "Hummer", side = 2, cratesRequired = 2 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled - { multiple = {1001.01, 1001.01}, desc = ctld.i18n_translate("Hummer - JTAC - All crates"), side = 2 }, - { weight = 1001.02, desc = ctld.i18n_translate("M-818 Ammo Truck"), unit = "M 818", side = 2, cratesRequired = 2 }, - { multiple = {1001.02, 1001.02}, desc = ctld.i18n_translate("M-818 Ammo Truck - All crates"), side = 2 }, - { weight = 1001.03, desc = ctld.i18n_translate("M-978 Tanker"), unit = "M978 HEMTT Tanker", side = 2, cratesRequired = 2 }, - { multiple = {1001.03, 1001.03}, desc = ctld.i18n_translate("M-978 Tanker - All crates"), side = 2 }, + --- RED + { weight = 1000.11, desc = ctld.i18n_translate("BTR-D"), unit = "BTR_D", side = 1 }, + { weight = 1000.12, desc = ctld.i18n_translate("BRDM-2"), unit = "BRDM-2", side = 1 }, + -- need more redfor! + }, + ["Support"] = { + --- BLUE + { weight = 1001.01, desc = ctld.i18n_translate("Hummer - JTAC"), unit = "Hummer", side = 2, cratesRequired = 2 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled + { multiple = { 1001.01, 1001.01 }, desc = ctld.i18n_translate("Hummer - JTAC - All crates"), side = 2 }, + { weight = 1001.02, desc = ctld.i18n_translate("M-818 Ammo Truck"), unit = "M 818", side = 2, cratesRequired = 2 }, + { multiple = { 1001.02, 1001.02 }, desc = ctld.i18n_translate("M-818 Ammo Truck - All crates"), side = 2 }, + { weight = 1001.03, desc = ctld.i18n_translate("M-978 Tanker"), unit = "M978 HEMTT Tanker", side = 2, cratesRequired = 2 }, + { multiple = { 1001.03, 1001.03 }, desc = ctld.i18n_translate("M-978 Tanker - All crates"), side = 2 }, - --- RED - { weight = 1001.11, desc = ctld.i18n_translate("SKP-11 - JTAC"), unit = "SKP-11", side = 1 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled - { weight = 1001.12, desc = ctld.i18n_translate("Ural-375 Ammo Truck"), unit = "Ural-375", side = 1, cratesRequired = 2 }, - { multiple = {1001.12, 1001.12}, desc = ctld.i18n_translate("Ural-375 Ammo Truck - All crates"), side = 1 }, - { weight = 1001.13, desc = ctld.i18n_translate("KAMAZ Ammo Truck"), unit = "KAMAZ Truck", side = 1, cratesRequired = 2 }, + --- RED + { weight = 1001.11, desc = ctld.i18n_translate("SKP-11 - JTAC"), unit = "SKP-11", side = 1 }, -- used as jtac and unarmed, not on the crate list if JTAC is disabled + { weight = 1001.12, desc = ctld.i18n_translate("Ural-375 Ammo Truck"), unit = "Ural-375", side = 1, cratesRequired = 2 }, + { multiple = { 1001.12, 1001.12 }, desc = ctld.i18n_translate("Ural-375 Ammo Truck - All crates"), side = 1 }, + { weight = 1001.13, desc = ctld.i18n_translate("KAMAZ Ammo Truck"), unit = "KAMAZ Truck", side = 1, cratesRequired = 2 }, - --- Both - { weight = 1001.21, desc = ctld.i18n_translate("EWR Radar"), unit="FPS-117", cratesRequired = 3 }, - { multiple = {1001.21, 1001.21, 1001.21}, desc = ctld.i18n_translate("EWR Radar - All crates") }, - { weight = 1001.22, desc = ctld.i18n_translate("FOB Crate - Small"), unit = "FOB-SMALL" }, -- Builds a FOB! - requires 3 * ctld.cratesRequiredForFOB + --- Both + { weight = 1001.21, desc = ctld.i18n_translate("EWR Radar"), unit = "FPS-117", cratesRequired = 3 }, + { multiple = { 1001.21, 1001.21, 1001.21 }, desc = ctld.i18n_translate("EWR Radar - All crates") }, + { weight = 1001.22, desc = ctld.i18n_translate("FOB Crate - Small"), unit = "FOB-SMALL" }, -- Builds a FOB! - requires 3 * ctld.cratesRequiredForFOB - }, - ["Artillery"] = { - --- BLUE - { weight = 1002.01, desc = ctld.i18n_translate("MLRS"), unit = "MLRS", side=2, cratesRequired = 3 }, - { multiple = {1002.01, 1002.01, 1002.01}, desc = ctld.i18n_translate("MLRS - All crates"), side=2 }, - { weight = 1002.02, desc = ctld.i18n_translate("SpGH DANA"), unit = "SpGH_Dana", side=2, cratesRequired = 3 }, - { multiple = {1002.02, 1002.02, 1002.02}, desc = ctld.i18n_translate("SpGH DANA - All crates"), side=2 }, - { weight = 1002.03, desc = ctld.i18n_translate("T155 Firtina"), unit = "T155_Firtina", side=2, cratesRequired = 3 }, - { multiple = {1002.03, 1002.03, 1002.03}, desc = ctld.i18n_translate("T155 Firtina - All crates"), side=2 }, - { weight = 1002.04, desc = ctld.i18n_translate("Howitzer"), unit = "M-109", side=2, cratesRequired = 3 }, - { multiple = {1002.04, 1002.04, 1002.04}, desc = ctld.i18n_translate("Howitzer - All crates"), side=2 }, + }, + ["Artillery"] = { + --- BLUE + { weight = 1002.01, desc = ctld.i18n_translate("MLRS"), unit = "MLRS", side = 2, cratesRequired = 3 }, + { multiple = { 1002.01, 1002.01, 1002.01 }, desc = ctld.i18n_translate("MLRS - All crates"), side = 2 }, + { weight = 1002.02, desc = ctld.i18n_translate("SpGH DANA"), unit = "SpGH_Dana", side = 2, cratesRequired = 3 }, + { multiple = { 1002.02, 1002.02, 1002.02 }, desc = ctld.i18n_translate("SpGH DANA - All crates"), side = 2 }, + { weight = 1002.03, desc = ctld.i18n_translate("T155 Firtina"), unit = "T155_Firtina", side = 2, cratesRequired = 3 }, + { multiple = { 1002.03, 1002.03, 1002.03 }, desc = ctld.i18n_translate("T155 Firtina - All crates"), side = 2 }, + { weight = 1002.04, desc = ctld.i18n_translate("Howitzer"), unit = "M-109", side = 2, cratesRequired = 3 }, + { multiple = { 1002.04, 1002.04, 1002.04 }, desc = ctld.i18n_translate("Howitzer - All crates"), side = 2 }, - --- RED - { weight = 1002.11, desc = ctld.i18n_translate("SPH 2S19 Msta"), unit = "SAU Msta", side = 1, cratesRequired = 3 }, - { multiple = {1002.11, 1002.11, 1002.11}, desc = ctld.i18n_translate("SPH 2S19 Msta - All crates"), side=1 }, + --- RED + { weight = 1002.11, desc = ctld.i18n_translate("SPH 2S19 Msta"), unit = "SAU Msta", side = 1, cratesRequired = 3 }, + { multiple = { 1002.11, 1002.11, 1002.11 }, desc = ctld.i18n_translate("SPH 2S19 Msta - All crates"), side = 1 }, - }, - ["SAM short range"] = { - --- BLUE - { weight = 1003.01, desc = ctld.i18n_translate("M1097 Avenger"), unit = "M1097 Avenger", side = 2, cratesRequired = 3 }, - { multiple = {1003.01, 1003.01, 1003.01}, desc = ctld.i18n_translate("M1097 Avenger - All crates"), side=2 }, - { weight = 1003.02, desc = ctld.i18n_translate("M48 Chaparral"), unit = "M48 Chaparral", side = 2, cratesRequired = 2 }, - { multiple = {1003.02, 1003.02}, desc = ctld.i18n_translate("M48 Chaparral - All crates"), side=2 }, - { weight = 1003.03, desc = ctld.i18n_translate("Roland ADS"), unit = "Roland ADS", side = 2, cratesRequired = 3 }, - { multiple = {1003.03, 1003.03, 1003.03}, desc = ctld.i18n_translate("Roland ADS - All crates"), side=2 }, - { weight = 1003.04, desc = ctld.i18n_translate("Gepard AAA"), unit = "Gepard", side = 2, cratesRequired = 3 }, - { multiple = {1003.04, 1003.04, 1003.04}, desc = ctld.i18n_translate("Gepard AAA - All crates"), side=2 }, - { weight = 1003.05, desc = ctld.i18n_translate("LPWS C-RAM"), unit = "HEMTT_C-RAM_Phalanx", side = 2, cratesRequired = 3 }, - { multiple = {1003.05, 1003.05, 1003.05}, desc = ctld.i18n_translate("LPWS C-RAM - All crates"), side=2 }, + }, + ["SAM short range"] = { + --- BLUE + { weight = 1003.01, desc = ctld.i18n_translate("M1097 Avenger"), unit = "M1097 Avenger", side = 2, cratesRequired = 3 }, + { multiple = { 1003.01, 1003.01, 1003.01 }, desc = ctld.i18n_translate("M1097 Avenger - All crates"), side = 2 }, + { weight = 1003.02, desc = ctld.i18n_translate("M48 Chaparral"), unit = "M48 Chaparral", side = 2, cratesRequired = 2 }, + { multiple = { 1003.02, 1003.02 }, desc = ctld.i18n_translate("M48 Chaparral - All crates"), side = 2 }, + { weight = 1003.03, desc = ctld.i18n_translate("Roland ADS"), unit = "Roland ADS", side = 2, cratesRequired = 3 }, + { multiple = { 1003.03, 1003.03, 1003.03 }, desc = ctld.i18n_translate("Roland ADS - All crates"), side = 2 }, + { weight = 1003.04, desc = ctld.i18n_translate("Gepard AAA"), unit = "Gepard", side = 2, cratesRequired = 3 }, + { multiple = { 1003.04, 1003.04, 1003.04 }, desc = ctld.i18n_translate("Gepard AAA - All crates"), side = 2 }, + { weight = 1003.05, desc = ctld.i18n_translate("LPWS C-RAM"), unit = "HEMTT_C-RAM_Phalanx", side = 2, cratesRequired = 3 }, + { multiple = { 1003.05, 1003.05, 1003.05 }, desc = ctld.i18n_translate("LPWS C-RAM - All crates"), side = 2 }, - --- RED - { weight = 1003.11, desc = ctld.i18n_translate("9K33 Osa"), unit = "Osa 9A33 ln", side = 1, cratesRequired = 3 }, - { multiple = {1003.11, 1003.11, 1003.11}, desc = ctld.i18n_translate("9K33 Osa - All crates"), side=1 }, - { weight = 1003.12, desc = ctld.i18n_translate("9P31 Strela-1"), unit = "Strela-1 9P31", side = 1, cratesRequired = 3 }, - { multiple = {1003.12, 1003.12, 1003.12}, desc = ctld.i18n_translate("9P31 Strela-1 - All crates"), side=1 }, - { weight = 1003.13, desc = ctld.i18n_translate("9K35M Strela-10"), unit = "Strela-10M3", side = 1, cratesRequired = 3 }, - { multiple = {1003.13, 1003.13, 1003.13}, desc = ctld.i18n_translate("9K35M Strela-10 - All crates"), side=1 }, - { weight = 1003.14, desc = ctld.i18n_translate("9K331 Tor"), unit = "Tor 9A331", side = 1, cratesRequired = 3 }, - { multiple = {1003.14, 1003.14, 1003.14}, desc = ctld.i18n_translate("9K331 Tor - All crates"), side=1 }, - { weight = 1003.15, desc = ctld.i18n_translate("2K22 Tunguska"), unit = "2S6 Tunguska", side = 1, cratesRequired = 3 }, - { multiple = {1003.15, 1003.15, 1003.15}, desc = ctld.i18n_translate("2K22 Tunguska - All crates"), side=1 }, - }, - ["SAM mid range"] = { - --- BLUE - -- HAWK System - { weight = 1004.01, desc = ctld.i18n_translate("HAWK Launcher"), unit = "Hawk ln", side = 2}, - { weight = 1004.02, desc = ctld.i18n_translate("HAWK Search Radar"), unit = "Hawk sr", side = 2 }, - { weight = 1004.03, desc = ctld.i18n_translate("HAWK Track Radar"), unit = "Hawk tr", side = 2 }, - { weight = 1004.04, desc = ctld.i18n_translate("HAWK PCP"), unit = "Hawk pcp" , side = 2 }, - { weight = 1004.05, desc = ctld.i18n_translate("HAWK CWAR"), unit = "Hawk cwar" , side = 2 }, - { weight = 1004.06, desc = ctld.i18n_translate("HAWK Repair"), unit = "HAWK Repair" , side = 2 }, - { multiple = {1004.01, 1004.02, 1004.03}, desc = ctld.i18n_translate("HAWK - All crates"), side = 2 }, - -- End of HAWK + --- RED + { weight = 1003.11, desc = ctld.i18n_translate("9K33 Osa"), unit = "Osa 9A33 ln", side = 1, cratesRequired = 3 }, + { multiple = { 1003.11, 1003.11, 1003.11 }, desc = ctld.i18n_translate("9K33 Osa - All crates"), side = 1 }, + { weight = 1003.12, desc = ctld.i18n_translate("9P31 Strela-1"), unit = "Strela-1 9P31", side = 1, cratesRequired = 3 }, + { multiple = { 1003.12, 1003.12, 1003.12 }, desc = ctld.i18n_translate("9P31 Strela-1 - All crates"), side = 1 }, + { weight = 1003.13, desc = ctld.i18n_translate("9K35M Strela-10"), unit = "Strela-10M3", side = 1, cratesRequired = 3 }, + { multiple = { 1003.13, 1003.13, 1003.13 }, desc = ctld.i18n_translate("9K35M Strela-10 - All crates"), side = 1 }, + { weight = 1003.14, desc = ctld.i18n_translate("9K331 Tor"), unit = "Tor 9A331", side = 1, cratesRequired = 3 }, + { multiple = { 1003.14, 1003.14, 1003.14 }, desc = ctld.i18n_translate("9K331 Tor - All crates"), side = 1 }, + { weight = 1003.15, desc = ctld.i18n_translate("2K22 Tunguska"), unit = "2S6 Tunguska", side = 1, cratesRequired = 3 }, + { multiple = { 1003.15, 1003.15, 1003.15 }, desc = ctld.i18n_translate("2K22 Tunguska - All crates"), side = 1 }, + }, + ["SAM mid range"] = { + --- BLUE + -- HAWK System + { weight = 1004.01, desc = ctld.i18n_translate("HAWK Launcher"), unit = "Hawk ln", side = 2 }, + { weight = 1004.02, desc = ctld.i18n_translate("HAWK Search Radar"), unit = "Hawk sr", side = 2 }, + { weight = 1004.03, desc = ctld.i18n_translate("HAWK Track Radar"), unit = "Hawk tr", side = 2 }, + { weight = 1004.04, desc = ctld.i18n_translate("HAWK PCP"), unit = "Hawk pcp", side = 2 }, + { weight = 1004.05, desc = ctld.i18n_translate("HAWK CWAR"), unit = "Hawk cwar", side = 2 }, + { weight = 1004.06, desc = ctld.i18n_translate("HAWK Repair"), unit = "HAWK Repair", side = 2 }, + { multiple = { 1004.01, 1004.02, 1004.03 }, desc = ctld.i18n_translate("HAWK - All crates"), side = 2 }, + -- End of HAWK - -- NASAMS Sysyem - { weight = 1004.11, desc = ctld.i18n_translate("NASAMS Launcher 120C"), unit = "NASAMS_LN_C", side = 2}, - { weight = 1004.12, desc = ctld.i18n_translate("NASAMS Search/Track Radar"), unit = "NASAMS_Radar_MPQ64F1", side = 2 }, - { weight = 1004.13, desc = ctld.i18n_translate("NASAMS Command Post"), unit = "NASAMS_Command_Post", side = 2 }, - { weight = 1004.14, desc = ctld.i18n_translate("NASAMS Repair"), unit = "NASAMS Repair", side = 2 }, - { multiple = {1004.11, 1004.12, 1004.13}, desc = ctld.i18n_translate("NASAMS - All crates"), side = 2 }, - -- End of NASAMS + -- NASAMS Sysyem + { weight = 1004.11, desc = ctld.i18n_translate("NASAMS Launcher 120C"), unit = "NASAMS_LN_C", side = 2 }, + { weight = 1004.12, desc = ctld.i18n_translate("NASAMS Search/Track Radar"), unit = "NASAMS_Radar_MPQ64F1", side = 2 }, + { weight = 1004.13, desc = ctld.i18n_translate("NASAMS Command Post"), unit = "NASAMS_Command_Post", side = 2 }, + { weight = 1004.14, desc = ctld.i18n_translate("NASAMS Repair"), unit = "NASAMS Repair", side = 2 }, + { multiple = { 1004.11, 1004.12, 1004.13 }, desc = ctld.i18n_translate("NASAMS - All crates"), side = 2 }, + -- End of NASAMS - --- RED - -- KUB SYSTEM - { weight = 1004.21, desc = ctld.i18n_translate("KUB Launcher"), unit = "Kub 2P25 ln", side = 1}, - { weight = 1004.22, desc = ctld.i18n_translate("KUB Radar"), unit = "Kub 1S91 str", side = 1 }, - { weight = 1004.23, desc = ctld.i18n_translate("KUB Repair"), unit = "KUB Repair", side = 1}, - { multiple = {1004.21, 1004.22}, desc = ctld.i18n_translate("KUB - All crates"), side = 1 }, - -- End of KUB + --- RED + -- KUB SYSTEM + { weight = 1004.21, desc = ctld.i18n_translate("KUB Launcher"), unit = "Kub 2P25 ln", side = 1 }, + { weight = 1004.22, desc = ctld.i18n_translate("KUB Radar"), unit = "Kub 1S91 str", side = 1 }, + { weight = 1004.23, desc = ctld.i18n_translate("KUB Repair"), unit = "KUB Repair", side = 1 }, + { multiple = { 1004.21, 1004.22 }, desc = ctld.i18n_translate("KUB - All crates"), side = 1 }, + -- End of KUB - -- BUK System - { weight = 1004.31, desc = ctld.i18n_translate("BUK Launcher"), unit = "SA-11 Buk LN 9A310M1", side = 1}, - { weight = 1004.32, desc = ctld.i18n_translate("BUK Search Radar"), unit = "SA-11 Buk SR 9S18M1", side = 1}, - { weight = 1004.33, desc = ctld.i18n_translate("BUK CC Radar"), unit = "SA-11 Buk CC 9S470M1", side = 1}, - { weight = 1004.34, desc = ctld.i18n_translate("BUK Repair"), unit = "BUK Repair", side = 1}, - { multiple = {1004.31, 1004.32, 1004.33}, desc = ctld.i18n_translate("BUK - All crates"), side = 1 }, - -- END of BUK - }, - ["SAM long range"] = { - --- BLUE - -- Patriot System - { weight = 1005.01, desc = ctld.i18n_translate("Patriot Launcher"), unit = "Patriot ln", side = 2 }, - { weight = 1005.02, desc = ctld.i18n_translate("Patriot Radar"), unit = "Patriot str" , side = 2 }, - { weight = 1005.03, desc = ctld.i18n_translate("Patriot ECS"), unit = "Patriot ECS", side = 2 }, - -- { weight = 1005.04, desc = ctld.i18n_translate("Patriot ICC"), unit = "Patriot cp", side = 2 }, - -- { weight = 1005.05, desc = ctld.i18n_translate("Patriot EPP"), unit = "Patriot EPP", side = 2 }, - { weight = 1005.06, desc = ctld.i18n_translate("Patriot AMG (optional)"), unit = "Patriot AMG" , side = 2 }, - { weight = 1005.07, desc = ctld.i18n_translate("Patriot Repair"), unit = "Patriot Repair" , side = 2 }, - { multiple = {1005.01, 1005.02, 1005.03}, desc = ctld.i18n_translate("Patriot - All crates"), side = 2 }, - -- End of Patriot + -- BUK System + { weight = 1004.31, desc = ctld.i18n_translate("BUK Launcher"), unit = "SA-11 Buk LN 9A310M1", side = 1 }, + { weight = 1004.32, desc = ctld.i18n_translate("BUK Search Radar"), unit = "SA-11 Buk SR 9S18M1", side = 1 }, + { weight = 1004.33, desc = ctld.i18n_translate("BUK CC Radar"), unit = "SA-11 Buk CC 9S470M1", side = 1 }, + { weight = 1004.34, desc = ctld.i18n_translate("BUK Repair"), unit = "BUK Repair", side = 1 }, + { multiple = { 1004.31, 1004.32, 1004.33 }, desc = ctld.i18n_translate("BUK - All crates"), side = 1 }, + -- END of BUK + }, + ["SAM long range"] = { + --- BLUE + -- Patriot System + { weight = 1005.01, desc = ctld.i18n_translate("Patriot Launcher"), unit = "Patriot ln", side = 2 }, + { weight = 1005.02, desc = ctld.i18n_translate("Patriot Radar"), unit = "Patriot str", side = 2 }, + { weight = 1005.03, desc = ctld.i18n_translate("Patriot ECS"), unit = "Patriot ECS", side = 2 }, + -- { weight = 1005.04, desc = ctld.i18n_translate("Patriot ICC"), unit = "Patriot cp", side = 2 }, + -- { weight = 1005.05, desc = ctld.i18n_translate("Patriot EPP"), unit = "Patriot EPP", side = 2 }, + { weight = 1005.06, desc = ctld.i18n_translate("Patriot AMG (optional)"), unit = "Patriot AMG", side = 2 }, + { weight = 1005.07, desc = ctld.i18n_translate("Patriot Repair"), unit = "Patriot Repair", side = 2 }, + { multiple = { 1005.01, 1005.02, 1005.03 }, desc = ctld.i18n_translate("Patriot - All crates"), side = 2 }, + -- End of Patriot - -- S-300 SYSTEM - { weight = 1005.11, desc = ctld.i18n_translate("S-300 Grumble TEL C"), unit = "S-300PS 5P85C ln", side = 1 }, - { weight = 1005.12, desc = ctld.i18n_translate("S-300 Grumble Flap Lid-A TR"), unit = "S-300PS 40B6M tr", side = 1 }, - { weight = 1005.13, desc = ctld.i18n_translate("S-300 Grumble Clam Shell SR"), unit = "S-300PS 40B6MD sr", side = 1 }, - { weight = 1005.14, desc = ctld.i18n_translate("S-300 Grumble Big Bird SR"), unit = "S-300PS 64H6E sr", side = 1 }, - { weight = 1005.15, desc = ctld.i18n_translate("S-300 Grumble C2"), unit = "S-300PS 54K6 cp", side = 1 }, - { weight = 1005.16, desc = ctld.i18n_translate("S-300 Repair"), unit = "S-300 Repair", side = 1 }, - { multiple = {1005.11, 1005.12, 1005.13, 1005.14, 1005.15}, desc = ctld.i18n_translate("Patriot - All crates"), side = 1 }, - -- End of S-300 - }, - ["Drone"] = { - --- BLUE MQ-9 Repear - { weight = 1006.01, desc = ctld.i18n_translate("MQ-9 Repear - JTAC"), unit = "MQ-9 Reaper", side = 2 }, - -- End of BLUE MQ-9 Repear + -- S-300 SYSTEM + { weight = 1005.11, desc = ctld.i18n_translate("S-300 Grumble TEL C"), unit = "S-300PS 5P85C ln", side = 1 }, + { weight = 1005.12, desc = ctld.i18n_translate("S-300 Grumble Flap Lid-A TR"), unit = "S-300PS 40B6M tr", side = 1 }, + { weight = 1005.13, desc = ctld.i18n_translate("S-300 Grumble Clam Shell SR"), unit = "S-300PS 40B6MD sr", side = 1 }, + { weight = 1005.14, desc = ctld.i18n_translate("S-300 Grumble Big Bird SR"), unit = "S-300PS 64H6E sr", side = 1 }, + { weight = 1005.15, desc = ctld.i18n_translate("S-300 Grumble C2"), unit = "S-300PS 54K6 cp", side = 1 }, + { weight = 1005.16, desc = ctld.i18n_translate("S-300 Repair"), unit = "S-300 Repair", side = 1 }, + { multiple = { 1005.11, 1005.12, 1005.13, 1005.14, 1005.15 }, desc = ctld.i18n_translate("Patriot - All crates"), side = 1 }, + -- End of S-300 + }, + ["Drone"] = { + --- BLUE MQ-9 Repear + { weight = 1006.01, desc = ctld.i18n_translate("MQ-9 Repear - JTAC"), unit = "MQ-9 Reaper", side = 2 }, + -- End of BLUE MQ-9 Repear - --- RED MQ-1A Predator - { weight = 1006.11, desc = ctld.i18n_translate("MQ-1A Predator - JTAC"), unit = "RQ-1A Predator", side = 1 }, - -- End of RED MQ-1A Predator - }, + --- RED MQ-1A Predator + { weight = 1006.11, desc = ctld.i18n_translate("MQ-1A Predator - JTAC"), unit = "RQ-1A Predator", side = 1 }, + -- End of RED MQ-1A Predator + }, } ctld.spawnableCratesModels = { - ["load"] = { - ["category"] = "Fortifications", - ["type"] = "Cargo04", - ["canCargo"] = false, - }, - ["sling"] = { - ["category"] = "Cargos", - ["shape_name"] = "bw_container_cargo", - ["type"] = "container_cargo", - ["canCargo"] = true - }, - ["dynamic"] = { - ["category"] = "Cargos", - ["type"] = "ammo_cargo", - ["canCargo"] = true - } + ["load"] = { + ["category"] = "Fortifications", + ["type"] = "Cargo04", + ["canCargo"] = false, + }, + ["sling"] = { + ["category"] = "Cargos", + ["shape_name"] = "bw_container_cargo", + ["type"] = "container_cargo", + ["canCargo"] = true + }, + ["dynamic"] = { + ["category"] = "Cargos", + ["type"] = "ammo_cargo", + ["canCargo"] = true + } } @@ -1222,15 +1236,15 @@ ctld.spawnableCratesModels = { ["shape_name"] = "trunks_small_cargo", ["type"] = "trunks_small_cargo", -]]-- +]] -- -- if the unit is on this list, it will be made into a JTAC when deployed -ctld.jtacUnitTypes = { - "SKP", "Hummer", -- there are some wierd encoding issues so if you write SKP-11 it wont match as the - sign is encoded differently... - "MQ", "RQ" --"MQ-9 Repear", "RQ-1A Predator"} +ctld.jtacUnitTypes = { + "SKP", "Hummer", -- there are some wierd encoding issues so if you write SKP-11 it wont match as the - sign is encoded differently... + "MQ", "RQ" --"MQ-9 Repear", "RQ-1A Predator"} } -ctld.jtacDroneRadius = 1000 -- JTAC offset radius in meters for orbiting drones -ctld.jtacDroneAltitude = 7000 -- JTAC altitude in meters for orbiting drones +ctld.jtacDroneRadius = 1000 -- JTAC offset radius in meters for orbiting drones +ctld.jtacDroneAltitude = 7000 -- JTAC altitude in meters for orbiting drones -- *************************************************************** -- **************** Mission Editor Functions ********************* -- *************************************************************** @@ -1254,42 +1268,41 @@ ctld.jtacDroneAltitude = 7000 -- JTAC altitude in meters for orbiting drones -- Spawns 1 machine gun, 2 anti tank, 3 anti air, 4 standard soldiers and 5 mortars -- function ctld.spawnGroupAtTrigger(_groupSide, _number, _triggerName, _searchRadius) - local _spawnTrigger = trigger.misc.getZone(_triggerName) -- trigger to use as reference position + local _spawnTrigger = trigger.misc.getZone(_triggerName) -- trigger to use as reference position - if _spawnTrigger == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find trigger called %1",_triggerName), 10) - return - end + if _spawnTrigger == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find trigger called %1", _triggerName), 10) + return + end - local _country - if _groupSide == "red" then - _groupSide = 1 - _country = 0 - else - _groupSide = 2 - _country = 2 - end + local _country + if _groupSide == "red" then + _groupSide = 1 + _country = 0 + else + _groupSide = 2 + _country = 2 + end - if _searchRadius < 0 then - _searchRadius = 0 - end + if _searchRadius < 0 then + _searchRadius = 0 + end - local _pos2 = { x = _spawnTrigger.point.x, y = _spawnTrigger.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + local _pos2 = { x = _spawnTrigger.point.x, y = _spawnTrigger.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - local _groupDetails = ctld.generateTroopTypes(_groupSide, _number, _country) + local _groupDetails = ctld.generateTroopTypes(_groupSide, _number, _country) - local _droppedTroops = ctld.spawnDroppedGroup(_pos3, _groupDetails, false, _searchRadius); + local _droppedTroops = ctld.spawnDroppedGroup(_pos3, _groupDetails, false, _searchRadius); - if _groupSide == 1 then - table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) - else - table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) - end + if _groupSide == 1 then + table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) + else + table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) + end end - ----------------------------------------------------------------- -- Spawn group at a Vec3 Point and set them as extractable. Usage: -- ctld.spawnGroupAtPoint("groupside", number,Vec3 Point, radius) @@ -1308,48 +1321,43 @@ end -- ctld.spawnGroupAtPoint("blue", {mg=1,at=2,aa=3,inf=4,mortar=5}, {x=1,y=2,z=3}, 2000) -- Spawns 1 machine gun, 2 anti tank, 3 anti air, 4 standard soldiers and 5 mortars function ctld.spawnGroupAtPoint(_groupSide, _number, _point, _searchRadius) + local _country + if _groupSide == "red" then + _groupSide = 1 + _country = 0 + else + _groupSide = 2 + _country = 2 + end - local _country - if _groupSide == "red" then - _groupSide = 1 - _country = 0 - else - _groupSide = 2 - _country = 2 - end + if _searchRadius < 0 then + _searchRadius = 0 + end - if _searchRadius < 0 then - _searchRadius = 0 - end + local _groupDetails = ctld.generateTroopTypes(_groupSide, _number, _country) - local _groupDetails = ctld.generateTroopTypes(_groupSide, _number, _country) + local _droppedTroops = ctld.spawnDroppedGroup(_point, _groupDetails, false, _searchRadius); - local _droppedTroops = ctld.spawnDroppedGroup(_point, _groupDetails, false, _searchRadius); - - if _groupSide == 1 then - table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) - else - table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) - end + if _groupSide == 1 then + table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) + else + table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) + end end - -- Preloads a transport with troops or vehicles -- replaces any troops currently on board function ctld.preLoadTransport(_unitName, _number, _troops) + local _unit = ctld.getTransportUnit(_unitName) - local _unit = ctld.getTransportUnit(_unitName) - - if _unit ~= nil then - - -- will replace any units currently on board - -- if not ctld.troopsOnboard(_unit,_troops) then - ctld.loadTroops(_unit, _troops, _number) - -- end - end + if _unit ~= nil then + -- will replace any units currently on board + -- if not ctld.troopsOnboard(_unit,_troops) then + ctld.loadTroops(_unit, _troops, _number) + -- end + end end - -- Continuously counts the number of crates in a zone and sets the value of the passed in flag -- to the count amount -- This means you can trigger actions based on the count and also trigger messages before the count is reached @@ -1357,50 +1365,46 @@ end -- This will now work for Mission Editor and Spawned Crates -- e.g. ctld.cratesInZone("DropZone1", 5) function ctld.cratesInZone(_zone, _flagNumber) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return + end - local _zonePos = mist.utils.zoneToVec3(_zone) + local _zonePos = mist.utils.zoneToVec3(_zone) - --ignore side, if crate has been used its discounted from the count - local _crateTables = { ctld.spawnedCratesRED, ctld.spawnedCratesBLUE, ctld.missionEditorCargoCrates } + --ignore side, if crate has been used its discounted from the count + local _crateTables = { ctld.spawnedCratesRED, ctld.spawnedCratesBLUE, ctld.missionEditorCargoCrates } - local _crateCount = 0 + local _crateCount = 0 - for _, _crates in pairs(_crateTables) do + for _, _crates in pairs(_crateTables) do + for _crateName, _dontUse in pairs(_crates) do + --get crate + local _crate = ctld.getCrateObject(_crateName) - for _crateName, _dontUse in pairs(_crates) do + --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude + if _crate ~= nil and _crate:getLife() > 0 + and (ctld.inAir(_crate) == false) then + local _dist = ctld.getDistance(_crate:getPoint(), _zonePos) - --get crate - local _crate = ctld.getCrateObject(_crateName) - - --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude - if _crate ~= nil and _crate:getLife() > 0 - and (ctld.inAir(_crate) == false) then - - local _dist = ctld.getDistance(_crate:getPoint(), _zonePos) - - if _dist <= _triggerZone.radius then - _crateCount = _crateCount + 1 - end - end + if _dist <= _triggerZone.radius then + _crateCount = _crateCount + 1 end + end end + end - --set flag stuff - trigger.action.setUserFlag(_flagNumber, _crateCount) + --set flag stuff + trigger.action.setUserFlag(_flagNumber, _crateCount) - -- env.info("FLAG ".._flagNumber.." crates ".._crateCount) + -- env.info("FLAG ".._flagNumber.." crates ".._crateCount) - --retrigger in 5 seconds - timer.scheduleFunction(function(_args) - - ctld.cratesInZone(_args[1], _args[2]) - end, { _zone, _flagNumber }, timer.getTime() + 5) + --retrigger in 5 seconds + timer.scheduleFunction(function(_args) + ctld.cratesInZone(_args[1], _args[2]) + end, { _zone, _flagNumber }, timer.getTime() + 5) end -- Creates an extraction zone @@ -1419,48 +1423,45 @@ end -- -- function ctld.createExtractZone(_zone, _flagNumber, _smoke) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return + end + + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + + trigger.action.setUserFlag(_flagNumber, 0) --start at 0 + + local _details = { point = _pos3, name = _zone, smoke = _smoke, flag = _flagNumber, radius = _triggerZone.radius } + + ctld.extractZones[_zone .. "-" .. _flagNumber] = _details + + if _smoke ~= nil and _smoke > -1 then + local _smokeFunction + + _smokeFunction = function(_args) + local _extractDetails = ctld.extractZones[_zone .. "-" .. _flagNumber] + -- check zone is still active + if _extractDetails == nil then + -- stop refreshing smoke, zone is done return + end + + + trigger.action.smoke(_args.point, _args.smoke) + --refresh in 5 minutes + timer.scheduleFunction(_smokeFunction, _args, timer.getTime() + 300) end - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - - trigger.action.setUserFlag(_flagNumber, 0) --start at 0 - - local _details = { point = _pos3, name = _zone, smoke = _smoke, flag = _flagNumber, radius = _triggerZone.radius} - - ctld.extractZones[_zone.."-".._flagNumber] = _details - - if _smoke ~= nil and _smoke > -1 then - - local _smokeFunction - - _smokeFunction = function(_args) - - local _extractDetails = ctld.extractZones[_zone.."-".._flagNumber] - -- check zone is still active - if _extractDetails == nil then - -- stop refreshing smoke, zone is done - return - end - - - trigger.action.smoke(_args.point, _args.smoke) - --refresh in 5 minutes - timer.scheduleFunction(_smokeFunction, _args, timer.getTime() + 300) - end - - --run local function - _smokeFunction(_details) - end + --run local function + _smokeFunction(_details) + end end - -- Removes an extraction zone -- -- The smoke will take up to 5 minutes to disappear depending on the last time the smoke was activated @@ -1472,15 +1473,13 @@ end -- -- -- -function ctld.removeExtractZone(_zone,_flagNumber) +function ctld.removeExtractZone(_zone, _flagNumber) + local _extractDetails = ctld.extractZones[_zone .. "-" .. _flagNumber] - local _extractDetails = ctld.extractZones[_zone.."-".._flagNumber] - - if _extractDetails ~= nil then - --remove zone - ctld.extractZones[_zone.."-".._flagNumber] = nil - - end + if _extractDetails ~= nil then + --remove zone + ctld.extractZones[_zone .. "-" .. _flagNumber] = nil + end end -- CONTINUOUS TRIGGER FUNCTION @@ -1490,45 +1489,43 @@ end -- is in the zone -- Use: ctld.countDroppedGroupsInZone("Zone Name", flagBlue, flagRed) function ctld.countDroppedGroupsInZone(_zone, _blueFlag, _redFlag) + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return + end - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end + local _zonePos = mist.utils.zoneToVec3(_zone) - local _zonePos = mist.utils.zoneToVec3(_zone) + local _redCount = 0; + local _blueCount = 0; - local _redCount = 0; - local _blueCount = 0; + local _allGroups = { ctld.droppedTroopsRED, ctld.droppedTroopsBLUE, ctld.droppedVehiclesRED, ctld + .droppedVehiclesBLUE } + for _, _extractGroups in pairs(_allGroups) do + for _, _groupName in pairs(_extractGroups) do + local _groupUnits = ctld.getGroup(_groupName) - local _allGroups = {ctld.droppedTroopsRED,ctld.droppedTroopsBLUE,ctld.droppedVehiclesRED,ctld.droppedVehiclesBLUE} - for _, _extractGroups in pairs(_allGroups) do - for _,_groupName in pairs(_extractGroups) do - local _groupUnits = ctld.getGroup(_groupName) + if #_groupUnits > 0 then + local _zonePos = mist.utils.zoneToVec3(_zone) + local _dist = ctld.getDistance(_groupUnits[1]:getPoint(), _zonePos) - if #_groupUnits > 0 then - local _zonePos = mist.utils.zoneToVec3(_zone) - local _dist = ctld.getDistance(_groupUnits[1]:getPoint(), _zonePos) - - if _dist <= _triggerZone.radius then - - if (_groupUnits[1]:getCoalition() == 1) then - _redCount = _redCount + 1; - else - _blueCount = _blueCount + 1; - end - end - end + if _dist <= _triggerZone.radius then + if (_groupUnits[1]:getCoalition() == 1) then + _redCount = _redCount + 1; + else + _blueCount = _blueCount + 1; + end end + end end - --set flag stuff - trigger.action.setUserFlag(_blueFlag, _blueCount) - trigger.action.setUserFlag(_redFlag, _redCount) - - -- env.info("Groups in zone ".._blueCount.." ".._redCount) + end + --set flag stuff + trigger.action.setUserFlag(_blueFlag, _blueCount) + trigger.action.setUserFlag(_redFlag, _redCount) + -- env.info("Groups in zone ".._blueCount.." ".._redCount) end -- CONTINUOUS TRIGGER FUNCTION @@ -1537,55 +1534,54 @@ end -- Use: ctld.countDroppedUnitsInZone("Zone Name", flagBlue, flagRed) function ctld.countDroppedUnitsInZone(_zone, _blueFlag, _redFlag) + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return + end - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end + local _zonePos = mist.utils.zoneToVec3(_zone) - local _zonePos = mist.utils.zoneToVec3(_zone) + local _redCount = 0; + local _blueCount = 0; - local _redCount = 0; - local _blueCount = 0; + local _allGroups = { ctld.droppedTroopsRED, ctld.droppedTroopsBLUE, ctld.droppedVehiclesRED, ctld + .droppedVehiclesBLUE } - local _allGroups = {ctld.droppedTroopsRED,ctld.droppedTroopsBLUE,ctld.droppedVehiclesRED,ctld.droppedVehiclesBLUE} + for _, _extractGroups in pairs(_allGroups) do + for _, _groupName in pairs(_extractGroups) do + local _groupUnits = ctld.getGroup(_groupName) - for _, _extractGroups in pairs(_allGroups) do - for _,_groupName in pairs(_extractGroups) do - local _groupUnits = ctld.getGroup(_groupName) + if #_groupUnits > 0 then + local _zonePos = mist.utils.zoneToVec3(_zone) + for _, _unit in pairs(_groupUnits) do + local _dist = ctld.getDistance(_unit:getPoint(), _zonePos) - if #_groupUnits > 0 then - - local _zonePos = mist.utils.zoneToVec3(_zone) - for _,_unit in pairs(_groupUnits) do - local _dist = ctld.getDistance(_unit:getPoint(), _zonePos) - - if _dist <= _triggerZone.radius then - - if (_unit:getCoalition() == 1) then - _redCount = _redCount + 1; - else - _blueCount = _blueCount + 1; - end - end - end + if _dist <= _triggerZone.radius then + if (_unit:getCoalition() == 1) then + _redCount = _redCount + 1; + else + _blueCount = _blueCount + 1; end + end end + end end + end - --set flag stuff - trigger.action.setUserFlag(_blueFlag, _blueCount) - trigger.action.setUserFlag(_redFlag, _redCount) + --set flag stuff + trigger.action.setUserFlag(_blueFlag, _blueCount) + trigger.action.setUserFlag(_redFlag, _redCount) - -- env.info("Units in zone ".._blueCount.." ".._redCount) + -- env.info("Units in zone ".._blueCount.." ".._redCount) end + --*************************************************************** function ctld.getNextDynamicLogisticUnitIndex() - ctld.dynamicLogisticUnitsIndex = ctld.dynamicLogisticUnitsIndex + 1 - return ctld.dynamicLogisticUnitsIndex + ctld.dynamicLogisticUnitsIndex = ctld.dynamicLogisticUnitsIndex + 1 + return ctld.dynamicLogisticUnitsIndex end -- Creates a radio beacon on a random UHF - VHF and HF/FM frequency for homing @@ -1596,84 +1592,77 @@ end -- e.g. ctld.createRadioBeaconAtZone("beaconZoneBlue","blue", 20) will create a beacon at trigger zone "beaconZoneBlue" for the Blue side -- that will last 20 minutes function ctld.createRadioBeaconAtZone(_zone, _coalition, _batteryLife, _name) - local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return + end - local _zonePos = mist.utils.zoneToVec3(_zone) + local _zonePos = mist.utils.zoneToVec3(_zone) - ctld.beaconCount = ctld.beaconCount + 1 + ctld.beaconCount = ctld.beaconCount + 1 - if _name == nil or _name == "" then - _name = "Beacon #" .. ctld.beaconCount - end + if _name == nil or _name == "" then + _name = "Beacon #" .. ctld.beaconCount + end - if _coalition == "red" then - ctld.createRadioBeacon(_zonePos, 1, 0, _name, _batteryLife) --1440 - else - ctld.createRadioBeacon(_zonePos, 2, 2, _name, _batteryLife) --1440 - end + if _coalition == "red" then + ctld.createRadioBeacon(_zonePos, 1, 0, _name, _batteryLife) --1440 + else + ctld.createRadioBeacon(_zonePos, 2, 2, _name, _batteryLife) --1440 + end end - -- Activates a pickup zone -- Activates a pickup zone when called from a trigger -- EG: ctld.activatePickupZone("pickzone3") -- This is enable pickzone3 to be used as a pickup zone for the team set function ctld.activatePickupZone(_zoneName) + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_triggerZone) - - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point - end + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_triggerZone) + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point end + end - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone or ship called %1", _zoneName), 10) - end - - for _, _zoneDetails in pairs(ctld.pickupZones) do - - if _zoneName == _zoneDetails[1] then - - --smoke could get messy if designer keeps calling this on an active zone, check its not active first - if _zoneDetails[4] == 1 then - -- they might have a continuous trigger so i've hidden the warning - return - end - - _zoneDetails[4] = 1 --activate zone - - if ctld.disableAllSmoke == true then --smoke disabled - return - end - - if _zoneDetails[2] >= 0 then - - -- Trigger smoke marker - -- This will cause an overlapping smoke marker on next refreshsmoke call - -- but will only happen once - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - - trigger.action.smoke(_pos3, _zoneDetails[2]) - end - end + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone or ship called %1", _zoneName), 10) + end + + for _, _zoneDetails in pairs(ctld.pickupZones) do + if _zoneName == _zoneDetails[1] then + --smoke could get messy if designer keeps calling this on an active zone, check its not active first + if _zoneDetails[4] == 1 then + -- they might have a continuous trigger so i've hidden the warning + return + end + + _zoneDetails[4] = 1 --activate zone + + if ctld.disableAllSmoke == true then --smoke disabled + return + end + + if _zoneDetails[2] >= 0 then + -- Trigger smoke marker + -- This will cause an overlapping smoke marker on next refreshsmoke call + -- but will only happen once + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + + trigger.action.smoke(_pos3, _zoneDetails[2]) + end end + end end - -- Deactivates a pickup zone -- Deactivates a pickup zone when called from a trigger -- EG: ctld.deactivatePickupZone("pickzone3") @@ -1681,64 +1670,57 @@ end -- These functions can be called by triggers, like if a set of buildings is used, you can trigger the zone to be 'not operational' -- once they are destroyed function ctld.deactivatePickupZone(_zoneName) + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_triggerZone) - - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point - end + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_triggerZone) + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point end + end - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) - return - end - - for _, _zoneDetails in pairs(ctld.pickupZones) do - - if _zoneName == _zoneDetails[1] then - -- i'd just ignore it if its already been deactivated - _zoneDetails[4] = 0 --deactivate zone - end + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) + return + end + + for _, _zoneDetails in pairs(ctld.pickupZones) do + if _zoneName == _zoneDetails[1] then + -- i'd just ignore it if its already been deactivated + _zoneDetails[4] = 0 --deactivate zone end + end end -- Change the remaining groups currently available for pickup at a zone -- e.g. ctld.changeRemainingGroupsForPickupZone("pickup1", 5) -- adds 5 groups -- ctld.changeRemainingGroupsForPickupZone("pickup1", -3) -- remove 3 groups function ctld.changeRemainingGroupsForPickupZone(_zoneName, _amount) - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_triggerZone) - - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point - end + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_triggerZone) + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point end + end - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) - return + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) + return + end + + for _, _zoneDetails in pairs(ctld.pickupZones) do + if _zoneName == _zoneDetails[1] then + ctld.updateZoneCounter(_zoneName, _amount) end - - for _, _zoneDetails in pairs(ctld.pickupZones) do - - if _zoneName == _zoneDetails[1] then - ctld.updateZoneCounter(_zoneName, _amount) - end - end - - + end end -- Activates a Waypoint zone @@ -1747,146 +1729,124 @@ end -- This means that troops dropped within the radius of the zone will head to the center -- of the zone instead of searching for troops function ctld.activateWaypointZone(_zoneName) - local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position + local _triggerZone = trigger.misc.getZone(_zoneName) -- trigger to use as reference position - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) + return + end + + for _, _zoneDetails in pairs(ctld.wpZones) do + if _zoneName == _zoneDetails[1] then + --smoke could get messy if designer keeps calling this on an active zone, check its not active first + if _zoneDetails[3] == 1 then + -- they might have a continuous trigger so i've hidden the warning return + end + + _zoneDetails[3] = 1 --activate zone + + if ctld.disableAllSmoke == true then --smoke disabled + return + end + + if _zoneDetails[2] >= 0 then + -- Trigger smoke marker + -- This will cause an overlapping smoke marker on next refreshsmoke call + -- but will only happen once + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + + trigger.action.smoke(_pos3, _zoneDetails[2]) + end end - - for _, _zoneDetails in pairs(ctld.wpZones) do - - if _zoneName == _zoneDetails[1] then - - --smoke could get messy if designer keeps calling this on an active zone, check its not active first - if _zoneDetails[3] == 1 then - -- they might have a continuous trigger so i've hidden the warning - return - end - - _zoneDetails[3] = 1 --activate zone - - if ctld.disableAllSmoke == true then --smoke disabled - return - end - - if _zoneDetails[2] >= 0 then - - -- Trigger smoke marker - -- This will cause an overlapping smoke marker on next refreshsmoke call - -- but will only happen once - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - - trigger.action.smoke(_pos3, _zoneDetails[2]) - end - end - end + end end - -- Deactivates a Waypoint zone -- Deactivates a Waypoint zone when called from a trigger -- EG: ctld.deactivateWaypointZone("wpzone3") -- This disables wpzone3 so that troops dropped in this zone will search for troops as normal -- These functions can be called by triggers function ctld.deactivateWaypointZone(_zoneName) + local _triggerZone = trigger.misc.getZone(_zoneName) - local _triggerZone = trigger.misc.getZone(_zoneName) + if _triggerZone == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) + return + end - if _triggerZone == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zoneName), 10) - return - end - - for _, _zoneDetails in pairs(ctld.pickupZones) do - - if _zoneName == _zoneDetails[1] then - - _zoneDetails[3] = 0 --deactivate zone - end + for _, _zoneDetails in pairs(ctld.pickupZones) do + if _zoneName == _zoneDetails[1] then + _zoneDetails[3] = 0 --deactivate zone end + end end -- Continuous Trigger Function -- Causes an AI unit with the specified name to unload troops / vehicles when -- an enemy is detected within a specified distance -- The enemy must have Line or Sight to the unit to be detected -function ctld.unloadInProximityToEnemy(_unitName,_distance) +function ctld.unloadInProximityToEnemy(_unitName, _distance) + local _unit = ctld.getTransportUnit(_unitName) - local _unit = ctld.getTransportUnit(_unitName) + if _unit ~= nil and _unit:getPlayerName() == nil then + -- no player name means AI! + -- the findNearest visible enemy you'd want to modify as it'll find enemies quite far away + -- limited by ctld.JTAC_maxDistance + local _nearestEnemy = ctld.findNearestVisibleEnemy(_unit, "all", _distance) - if _unit ~= nil and _unit:getPlayerName() == nil then + if _nearestEnemy ~= nil then + if ctld.troopsOnboard(_unit, true) then + ctld.deployTroops(_unit, true) + return true + end - -- no player name means AI! - -- the findNearest visible enemy you'd want to modify as it'll find enemies quite far away - -- limited by ctld.JTAC_maxDistance - local _nearestEnemy = ctld.findNearestVisibleEnemy(_unit,"all",_distance) - - if _nearestEnemy ~= nil then - - if ctld.troopsOnboard(_unit, true) then - ctld.deployTroops(_unit, true) - return true - end - - if ctld.unitCanCarryVehicles(_unit) and ctld.troopsOnboard(_unit, false) then - ctld.deployTroops(_unit, false) - return true - end - end + if ctld.unitCanCarryVehicles(_unit) and ctld.troopsOnboard(_unit, false) then + ctld.deployTroops(_unit, false) + return true + end end + end - return false - + return false end - - -- Unit will unload any units onboard if the unit is on the ground -- when this function is called function ctld.unloadTransport(_unitName) + local _unit = ctld.getTransportUnit(_unitName) - local _unit = ctld.getTransportUnit(_unitName) - - if _unit ~= nil then - - if ctld.troopsOnboard(_unit, true) then - ctld.unloadTroops({_unitName,true}) - end - - if ctld.unitCanCarryVehicles(_unit) and ctld.troopsOnboard(_unit, false) then - ctld.unloadTroops({_unitName,false}) - end + if _unit ~= nil then + if ctld.troopsOnboard(_unit, true) then + ctld.unloadTroops({ _unitName, true }) end + if ctld.unitCanCarryVehicles(_unit) and ctld.troopsOnboard(_unit, false) then + ctld.unloadTroops({ _unitName, false }) + end + end end -- Loads Troops and Vehicles from a zone or picks up nearby troops or vehicles function ctld.loadTransport(_unitName) + local _unit = ctld.getTransportUnit(_unitName) - local _unit = ctld.getTransportUnit(_unitName) - - if _unit ~= nil then - - ctld.loadTroopsFromZone({ _unitName, true,"",true }) - - if ctld.unitCanCarryVehicles(_unit) then - ctld.loadTroopsFromZone({ _unitName, false,"",true }) - end + if _unit ~= nil then + ctld.loadTroopsFromZone({ _unitName, true, "", true }) + if ctld.unitCanCarryVehicles(_unit) then + ctld.loadTroopsFromZone({ _unitName, false, "", true }) end - + end end -- adds a callback that will be called for many actions ingame function ctld.addCallback(_callback) - - table.insert(ctld.callbacks,_callback) - + table.insert(ctld.callbacks, _callback) end -- Spawns a sling loadable crate at a Trigger Zone @@ -1895,40 +1855,39 @@ end -- e.g. ctld.spawnCrateAtZone("red", 500,"triggerzone1") -- spawn a humvee at triggerzone 1 for red side -- e.g. ctld.spawnCrateAtZone("blue", 505,"triggerzone1") -- spawn a tow humvee at triggerzone1 for blue side -- -function ctld.spawnCrateAtZone(_side, _weight,_zone) - local _spawnTrigger = trigger.misc.getZone(_zone) -- trigger to use as reference position +function ctld.spawnCrateAtZone(_side, _weight, _zone) + local _spawnTrigger = trigger.misc.getZone(_zone) -- trigger to use as reference position - if _spawnTrigger == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) - return - end + if _spawnTrigger == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find zone called %1", _zone), 10) + return + end - local _crateType = ctld.crateLookupTable[tostring(_weight)] + local _crateType = ctld.crateLookupTable[tostring(_weight)] - if _crateType == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find crate with weight %1", _weight), 10) - return - end + if _crateType == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find crate with weight %1", _weight), 10) + return + end - local _country - if _side == "red" then - _side = 1 - _country = 0 - else - _side = 2 - _country = 2 - end + local _country + if _side == "red" then + _side = 1 + _country = 0 + else + _side = 2 + _country = 2 + end - local _pos2 = { x = _spawnTrigger.point.x, y = _spawnTrigger.point.z } - local _alt = land.getHeight(_pos2) - local _point = { x = _pos2.x, y = _alt, z = _pos2.y } + local _pos2 = { x = _spawnTrigger.point.x, y = _spawnTrigger.point.z } + local _alt = land.getHeight(_pos2) + local _point = { x = _pos2.x, y = _alt, z = _pos2.y } - local _unitId = ctld.getNextUnitId() + local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", _crateType.desc, _unitId) - - ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side) + local _name = string.format("%s #%i", _crateType.desc, _unitId) + ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side) end -- Spawns a sling loadable crate at a Point @@ -1939,158 +1898,162 @@ end -- e.g. ctld.spawnCrateAtZone("blue", 505,{x=1,y=2,z=3}) -- spawn a tow humvee at triggerzone1 for blue side at a specified point -- -- -function ctld.spawnCrateAtPoint(_side, _weight, _point,_hdg) +function ctld.spawnCrateAtPoint(_side, _weight, _point, _hdg) + local _crateType = ctld.crateLookupTable[tostring(_weight)] + if _crateType == nil then + trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find crate with weight %1", _weight), 10) + return + end - local _crateType = ctld.crateLookupTable[tostring(_weight)] + local _country + if _side == "red" then + _side = 1 + _country = 0 + else + _side = 2 + _country = 2 + end - if _crateType == nil then - trigger.action.outText(ctld.i18n_translate("CTLD.lua ERROR: Can't find crate with weight %1", _weight), 10) - return - end + local _unitId = ctld.getNextUnitId() - local _country - if _side == "red" then - _side = 1 - _country = 0 - else - _side = 2 - _country = 2 - end - - local _unitId = ctld.getNextUnitId() - - local _name = string.format("%s #%i", _crateType.desc, _unitId) - - ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side, _hdg) + local _name = string.format("%s #%i", _crateType.desc, _unitId) + ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side, _hdg) end -- *************************************************************** --- Repack vehicules crates functions +-- Repack vehicules crates functions -- *************************************************************** ctld.repackRequestsStack = {} -- table to store the repack request function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) - if _radius == nil then - _radius = ctld.maximumDistanceRepackableUnitsSearch - end + if _radius == nil then + _radius = ctld.maximumDistanceRepackableUnitsSearch + end - local unit = ctld.getTransportUnit(_PlayerTransportUnitName) - if unit == nil then - return - end + local unit = ctld.getTransportUnit(_PlayerTransportUnitName) + if unit == nil then + return + end - local unitsNamesList = ctld.getNearbyUnits(unit:getPoint(), _radius, unit:getCoalition()) - local repackableUnits = {} - for i=1, #unitsNamesList do - local unitObject = Unit.getByName(unitsNamesList[i]) - local repackableUnit = ctld.isRepackableUnit(unitsNamesList[i]) - if repackableUnit then - repackableUnit["repackableUnitGroupID"] = unitObject:getGroup():getID() - table.insert(repackableUnits, mist.utils.deepCopy(repackableUnit)) - end + local unitsNamesList = ctld.getNearbyUnits(unit:getPoint(), _radius, unit:getCoalition()) + local repackableUnits = {} + for i = 1, #unitsNamesList do + local unitObject = Unit.getByName(unitsNamesList[i]) + local repackableUnit = ctld.isRepackableUnit(unitsNamesList[i]) + if repackableUnit then + repackableUnit["repackableUnitGroupID"] = unitObject:getGroup():getID() + table.insert(repackableUnits, mist.utils.deepCopy(repackableUnit)) end - return repackableUnits + end + return repackableUnits end + -- *************************************************************** -function ctld.getNearbyUnits(_point, _radius, _coalition) - if _coalition == nil then - _coalition = 4 -- all coalitions +function ctld.getNearbyUnits(_point, _radius, _coalition) + if _coalition == nil then + _coalition = 4 -- all coalitions + end + local _units = {} + local _unitList = mist.DBs.unitsByName + for k, _unit in pairs(mist.DBs.unitsByName) do + local u = Unit.getByName(k) + if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then + --local _dist = ctld.getDistance(u:getPoint(), _point) + local _dist = mist.utils.get2DDist(u:getPoint(), _point) + if _dist <= _radius then + table.insert(_units, k) -- insert nearby unitName + end end - local _units = {} - local _unitList = mist.DBs.unitsByName - for k, _unit in pairs(mist.DBs.unitsByName) do - local u = Unit.getByName(k) - if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then - --local _dist = ctld.getDistance(u:getPoint(), _point) - local _dist = mist.utils.get2DDist(u:getPoint(), _point) - if _dist <= _radius then - table.insert(_units, k) -- insert nearby unitName - end - end - end - return _units + end + return _units end + -- *************************************************************** function ctld.isRepackableUnit(_unitName) - local unitObject = Unit.getByName(_unitName) - local unitType = unitObject:getTypeName() - for k,v in pairs(ctld.spawnableCrates) do - for i=1, #ctld.spawnableCrates[k] do - if _unitName then - if ctld.spawnableCrates[k][i].unit == unitType then - local repackableUnit = mist.utils.deepCopy(ctld.spawnableCrates[k][i]) - repackableUnit["repackableUnitName"] = _unitName - return repackableUnit - end - end + local unitObject = Unit.getByName(_unitName) + local unitType = unitObject:getTypeName() + for k, v in pairs(ctld.spawnableCrates) do + for i = 1, #ctld.spawnableCrates[k] do + if _unitName then + if ctld.spawnableCrates[k][i].unit == unitType then + local repackableUnit = mist.utils.deepCopy(ctld.spawnableCrates[k][i]) + repackableUnit["repackableUnitName"] = _unitName + return repackableUnit end + end end - return nil + end + return nil end + -- *************************************************************** function ctld.getCrateDesc(_crateWeight) - for k,v in pairs(ctld.spawnableCrates) do - for i=1, #ctld.spawnableCrates[k] do - if _crateWeight then - if ctld.spawnableCrates[k][i].weight == _crateWeight then - return ctld.spawnableCrates[k][i] - end - end + for k, v in pairs(ctld.spawnableCrates) do + for i = 1, #ctld.spawnableCrates[k] do + if _crateWeight then + if ctld.spawnableCrates[k][i].weight == _crateWeight then + return ctld.spawnableCrates[k][i] end + end end - return nil + end + return nil end + -- *************************************************************** -function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request - ctld.repackRequestsStack[#ctld.repackRequestsStack+1] = _params - ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) +function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request + ctld.repackRequestsStack[#ctld.repackRequestsStack + 1] = _params + ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) end + -- *************************************************************** -function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request - trigger.action.outText("Repacking vehicle...", 10) - if t == nil then - t = timer.getTime() - end - ctld.logTrace("FG_ ctld.repackVehicle = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) - for ii, v in ipairs(ctld.repackRequestsStack) do - local repackableUnitName = v[1].repackableUnitName - local crateWeight = v[1].weight - local repackableUnit = Unit.getByName(repackableUnitName) - - local playerUnitName = v[2] - local PlayerTransportUnit = Unit.getByName(playerUnitName) - local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) - local spawnRefPoint = PlayerTransportUnit:getPoint() - local refCountry = PlayerTransportUnit:getCountry() - - if repackableUnit then - if repackableUnit:isExist() then - --ici calculer le heading des spwans à effectuer - for i=1, v[1].cratesRequired do - local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} - -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) - local _unitId = ctld.getNextUnitId() - local _name = string.format("%s_%i", v[1].desc, _unitId) - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) - end - - if ctld.isUnitInALogisticZone(repackableUnitName) == nil then - ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle - end - repackableUnit:destroy() -- destroy repacked unit - end - ctld.repackRequestsStack[ii] = nil +function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request + trigger.action.outText("Repacking vehicle...", 10) + if t == nil then + t = timer.getTime() + end + ctld.logTrace("FG_ ctld.repackVehicle = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) + for ii, v in ipairs(ctld.repackRequestsStack) do + local repackableUnitName = v[1].repackableUnitName + local crateWeight = v[1].weight + local repackableUnit = Unit.getByName(repackableUnitName) + + local playerUnitName = v[2] + local PlayerTransportUnit = Unit.getByName(playerUnitName) + local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) + local spawnRefPoint = PlayerTransportUnit:getPoint() + local refCountry = PlayerTransportUnit:getCountry() + + if repackableUnit then + if repackableUnit:isExist() then + --ici calculer le heading des spwans à effectuer + for i = 1, v[1].cratesRequired do + local _point = { x = spawnRefPoint.x + (i * 5), z = spawnRefPoint.z } + -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s_%i", v[1].desc, _unitId) + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, + PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end - ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units - end - if ctld.enableRepackingVehicles == true then - return t + 3 -- reschedule the function in 3 seconds - else - return nil --stop scheduling + + if ctld.isUnitInALogisticZone(repackableUnitName) == nil then + ctld.addStaticLogisticUnit({ x = spawnRefPoint.x + 5, z = spawnRefPoint.z + 10 }, refCountry) -- create a temporary logistic unit to be able to repack the vehicle + end + repackableUnit:destroy() -- destroy repacked unit + end + ctld.repackRequestsStack[ii] = nil end + ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units + end + if ctld.enableRepackingVehicles == true then + return t + 3 -- reschedule the function in 3 seconds + else + return nil --stop scheduling + end end + --[[ *************************************************************** function ctld.repackVehicle_old(_params) local repackableUnit = _params[1] @@ -2110,82 +2073,86 @@ function ctld.repackVehicle_old(_params) local _name = string.format("%s #%i", repackableUnit.desc, _unitId) ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end - + ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle repackableUnit.vehicleId:destroy() -- destroy repacked unit return end -end ]]-- +end ]] -- -- *************************************************************** function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit to be able to repack the vehicle - local dynamicLogisticUnitName = "%dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) - ctld.logisticUnits[#ctld.logisticUnits+1] = dynamicLogisticUnitName - local LogUnit = { - ["category"] = "Fortifications", - ["shape_name"] = "H-Windsock_RW", - ["type"] = "Windsock", - ["y"] = _point.z, - ["x"] = _point.x, - ["name"] = dynamicLogisticUnitName, - ["canCargo"] = false, - ["heading"] = 0, - } - LogUnit["country"] = _country - mist.dynAddStatic(LogUnit) - return StaticObject.getByName(LogUnit["name"]) + local dynamicLogisticUnitName = "%dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) + ctld.logisticUnits[#ctld.logisticUnits + 1] = dynamicLogisticUnitName + local LogUnit = { + ["category"] = "Fortifications", + ["shape_name"] = "H-Windsock_RW", + ["type"] = "Windsock", + ["y"] = _point.z, + ["x"] = _point.x, + ["name"] = dynamicLogisticUnitName, + ["canCargo"] = false, + ["heading"] = 0, + } + LogUnit["country"] = _country + mist.dynAddStatic(LogUnit) + return StaticObject.getByName(LogUnit["name"]) end + -- *************************************************************** -function ctld.updateDynamicLogisticUnitsZones() -- remove Dynamic Logistic Units if no statics units (crates) are in the zone - local _units = {} - for i, logUnit in ipairs(ctld.logisticUnits) do - if string.sub(logUnit, 1, 17) == "%dynLogisticName_" then -- check if the unit is a dynamic logistic unit - local unitsInLogisticUnitZone = ctld.getUnitsInLogisticZone(logUnit) - if #unitsInLogisticUnitZone == 0 then - local _logUnit = StaticObject.getByName(logUnit) - if _logUnit then - _logUnit:destroy() -- destroy the dynamic Logistic unit object from map - ctld.logisticUnits[i] = nil -- remove the dynamic Logistic unit from the list - end - end +function ctld.updateDynamicLogisticUnitsZones() -- remove Dynamic Logistic Units if no statics units (crates) are in the zone + local _units = {} + for i, logUnit in ipairs(ctld.logisticUnits) do + if string.sub(logUnit, 1, 17) == "%dynLogisticName_" then -- check if the unit is a dynamic logistic unit + local unitsInLogisticUnitZone = ctld.getUnitsInLogisticZone(logUnit) + if #unitsInLogisticUnitZone == 0 then + local _logUnit = StaticObject.getByName(logUnit) + if _logUnit then + _logUnit:destroy() -- destroy the dynamic Logistic unit object from map + ctld.logisticUnits[i] = nil -- remove the dynamic Logistic unit from the list end + end end - return 5 -- reschedule the function in 5 second + end + return 5 -- reschedule the function in 5 second end + -- *************************************************************** function ctld.getUnitsInLogisticZone(_logisticUnitName, _coalition) - local _unit = StaticObject.getByName(_logisticUnitName) - if _unit then - local _point = _unit:getPoint() - local _unitList = ctld.getNearbyUnits(_point, ctld.maximumDistanceLogistic, _coalition) - return _unitList - end - return {} + local _unit = StaticObject.getByName(_logisticUnitName) + if _unit then + local _point = _unit:getPoint() + local _unitList = ctld.getNearbyUnits(_point, ctld.maximumDistanceLogistic, _coalition) + return _unitList + end + return {} end + -- *************************************************************** function ctld.isUnitInNamedLogisticZone(_unitName, _logisticUnitName) -- check if a unit is in the named logistic zone - trigger.action.outText('ctld.isUnitInNamedLogisticZone._logisticUnitName = '..tostring(_logisticUnitName), 10) - local _unit = Unit.getByName(_unitName) - if _unit == nil then - return false - end - local unitPoint = _unit:getPoint() - if StaticObject.getByName(_logisticUnitName) then - local logisticUnitPoint = StaticObject.getByName(_logisticUnitName):getPoint() - local _dist = ctld.getDistance(unitPoint, logisticUnitPoint) - if _dist <= ctld.maximumDistanceLogistic then - return true - end - end + trigger.action.outText('ctld.isUnitInNamedLogisticZone._logisticUnitName = ' .. tostring(_logisticUnitName), 10) + local _unit = Unit.getByName(_unitName) + if _unit == nil then return false + end + local unitPoint = _unit:getPoint() + if StaticObject.getByName(_logisticUnitName) then + local logisticUnitPoint = StaticObject.getByName(_logisticUnitName):getPoint() + local _dist = ctld.getDistance(unitPoint, logisticUnitPoint) + if _dist <= ctld.maximumDistanceLogistic then + return true + end + end + return false end + -- *************************************************************** function ctld.isUnitInALogisticZone(_unitName) -- check if a unit is in a logistic zone if true then return the logisticUnitName of the zone - for i, logUnit in ipairs(ctld.logisticUnits) do - if ctld.isUnitInNamedLogisticZone(_unitName, logUnit) then - return logUnit - end + for i, logUnit in ipairs(ctld.logisticUnits) do + if ctld.isUnitInNamedLogisticZone(_unitName, logUnit) then + return logUnit end - return nil + end + return nil end -- *************************************************************** @@ -2198,73 +2165,73 @@ end -- If a component does not require a crate, it can be specified via the entry "NoCrate" set to true ctld.AASystemTemplate = { - { - name = "HAWK AA System", - count = 5, - parts = { - {name = "Hawk ln", desc = "HAWK Launcher", launcher = true}, - {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", + { + name = "HAWK AA System", + count = 5, + parts = { + { name = "Hawk ln", desc = "HAWK Launcher", launcher = true }, + { 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 }, }, - { - name = "Patriot AA System", - count = 4, - parts = { - {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", amount = 2}, - --{name = "Patriot cp", desc = "Patriot ICC", NoCrate = true}, - --{name = "Patriot EPP", desc = "Patriot EPP", NoCrate = true}, - {name = "Patriot AMG", desc = "Patriot AMG DL relay", NoCrate = true}, - }, - repair = "Patriot Repair", + repair = "HAWK Repair", + }, + { + name = "Patriot AA System", + count = 4, + parts = { + { 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", amount = 2 }, + --{name = "Patriot cp", desc = "Patriot ICC", NoCrate = true}, + --{name = "Patriot EPP", desc = "Patriot EPP", NoCrate = true}, + { name = "Patriot AMG", desc = "Patriot AMG DL relay", NoCrate = true }, }, - { - name = "NASAMS AA System", - count = 3, - parts = { - {name = "NASAMS_LN_C", desc = "NASAMS Launcher 120C", launcher = true}, - {name = "NASAMS_Radar_MPQ64F1", desc = "NASAMS Search/Track Radar"}, - {name = "NASAMS_Command_Post", desc = "NASAMS Command Post"}, - }, - repair = "NASAMS Repair", + repair = "Patriot Repair", + }, + { + name = "NASAMS AA System", + count = 3, + parts = { + { name = "NASAMS_LN_C", desc = "NASAMS Launcher 120C", launcher = true }, + { name = "NASAMS_Radar_MPQ64F1", desc = "NASAMS Search/Track Radar" }, + { name = "NASAMS_Command_Post", desc = "NASAMS Command Post" }, }, - { - name = "BUK AA System", - count = 3, - parts = { - {name = "SA-11 Buk LN 9A310M1", desc = "BUK Launcher" , launcher = true}, - {name = "SA-11 Buk CC 9S470M1", desc = "BUK CC Radar"}, - {name = "SA-11 Buk SR 9S18M1", desc = "BUK Search Radar"}, - }, - repair = "BUK Repair", + repair = "NASAMS Repair", + }, + { + name = "BUK AA System", + count = 3, + parts = { + { name = "SA-11 Buk LN 9A310M1", desc = "BUK Launcher", launcher = true }, + { name = "SA-11 Buk CC 9S470M1", desc = "BUK CC Radar" }, + { name = "SA-11 Buk SR 9S18M1", desc = "BUK Search Radar" }, }, - { - name = "KUB AA System", - count = 2, - parts = { - {name = "Kub 2P25 ln", desc = "KUB Launcher", launcher = true}, - {name = "Kub 1S91 str", desc = "KUB Radar"}, - }, - repair = "KUB Repair", + repair = "BUK Repair", + }, + { + name = "KUB AA System", + count = 2, + parts = { + { name = "Kub 2P25 ln", desc = "KUB Launcher", launcher = true }, + { name = "Kub 1S91 str", desc = "KUB Radar" }, }, - { - name = "S-300 AA System", - count = 6, - parts = { - { desc = "S-300 Grumble TEL C", name = "S-300PS 5P85C ln", launcher = true, amount = 1 }, - { desc = "S-300 Grumble TEL D", name = "S-300PS 5P85D ln", NoCrate = true, amount = 2 }, - { desc = "S-300 Grumble Flap Lid-A TR", name = "S-300PS 40B6M tr"}, - { desc = "S-300 Grumble Clam Shell SR", name = "S-300PS 40B6MD sr"}, - { desc = "S-300 Grumble Big Bird SR", name = "S-300PS 64H6E sr"}, - { desc = "S-300 Grumble C2", name = "S-300PS 54K6 cp"}, - }, - repair = "S-300 Repair", + repair = "KUB Repair", + }, + { + name = "S-300 AA System", + count = 6, + parts = { + { desc = "S-300 Grumble TEL C", name = "S-300PS 5P85C ln", launcher = true, amount = 1 }, + { desc = "S-300 Grumble TEL D", name = "S-300PS 5P85D ln", NoCrate = true, amount = 2 }, + { desc = "S-300 Grumble Flap Lid-A TR", name = "S-300PS 40B6M tr" }, + { desc = "S-300 Grumble Clam Shell SR", name = "S-300PS 40B6MD sr" }, + { desc = "S-300 Grumble Big Bird SR", name = "S-300PS 64H6E sr" }, + { desc = "S-300 Grumble C2", name = "S-300PS 54K6 cp" }, }, + repair = "S-300 Repair", + }, } @@ -2280,4233 +2247,4113 @@ ctld.crateMove = {} --- print an object for a debugging log function ctld.p(o, level) - local MAX_LEVEL = 20 - if level == nil then level = 0 end - if level > MAX_LEVEL then - ctld.logError("max depth reached in ctld.p : "..tostring(MAX_LEVEL)) - return "" + local MAX_LEVEL = 20 + if level == nil then level = 0 end + if level > MAX_LEVEL then + ctld.logError("max depth reached in ctld.p : " .. tostring(MAX_LEVEL)) + return "" + end + local text = "" + if (type(o) == "table") then + text = "\n" + for key, value in pairs(o) do + for i = 0, level do + text = text .. " " + end + text = text .. "." .. key .. "=" .. ctld.p(value, level + 1) .. "\n" end - local text = "" - if (type(o) == "table") then - text = "\n" - for key,value in pairs(o) do - for i=0, level do - text = text .. " " - end - text = text .. ".".. key.."="..ctld.p(value, level+1) .. "\n" - end - elseif (type(o) == "function") then - text = "[function]" - elseif (type(o) == "boolean") then - if o == true then - text = "[true]" - else - text = "[false]" - end + elseif (type(o) == "function") then + text = "[function]" + elseif (type(o) == "boolean") then + if o == true then + text = "[true]" else - if o == nil then - text = "[nil]" - else - text = tostring(o) - end + text = "[false]" end - return text + else + if o == nil then + text = "[nil]" + else + text = tostring(o) + end + end + return text end function ctld.formatText(text, ...) - if not text then - return "" - end - if type(text) ~= 'string' then - text = ctld.p(text) - else - local args = ... - if args and args.n and args.n > 0 then - local pArgs = {} - for i=1,args.n do - pArgs[i] = ctld.p(args[i]) - end - text = text:format(unpack(pArgs)) - end - end - local fName = nil - local cLine = nil - if debug and debug.getinfo then - local dInfo = debug.getinfo(3) - fName = dInfo.name - cLine = dInfo.currentline - end - if fName and cLine then - return fName .. '|' .. cLine .. ': ' .. text - elseif cLine then - return cLine .. ': ' .. text - else - return ' ' .. text + if not text then + return "" + end + if type(text) ~= 'string' then + text = ctld.p(text) + else + local args = ... + if args and args.n and args.n > 0 then + local pArgs = {} + for i = 1, args.n do + pArgs[i] = ctld.p(args[i]) + end + text = text:format(unpack(pArgs)) end + end + local fName = nil + local cLine = nil + if debug and debug.getinfo then + local dInfo = debug.getinfo(3) + fName = dInfo.name + cLine = dInfo.currentline + end + if fName and cLine then + return fName .. '|' .. cLine .. ': ' .. text + elseif cLine then + return cLine .. ': ' .. text + else + return ' ' .. text + end end function ctld.logError(message, ...) - message = ctld.formatText(message, arg) - env.info(" E - " .. ctld.Id .. message) + message = ctld.formatText(message, arg) + env.info(" E - " .. ctld.Id .. message) end - function ctld.logWarning(message, ...) - message = ctld.formatText(message, arg) - env.info(" W - " .. ctld.Id .. message) + message = ctld.formatText(message, arg) + env.info(" W - " .. ctld.Id .. message) end function ctld.logInfo(message, ...) - message = ctld.formatText(message, arg) - env.info(" I - " .. ctld.Id .. message) + message = ctld.formatText(message, arg) + env.info(" I - " .. ctld.Id .. message) end function ctld.logDebug(message, ...) - if message and ctld.Debug then - message = ctld.formatText(message, arg) - env.info(" D - " .. ctld.Id .. message) - end + if message and ctld.Debug then + message = ctld.formatText(message, arg) + env.info(" D - " .. ctld.Id .. message) + end end function ctld.logTrace(message, ...) - if message and ctld.Trace then - message = ctld.formatText(message, arg) - env.info(" T - " .. ctld.Id .. message) - end + if message and ctld.Trace then + message = ctld.formatText(message, arg) + env.info(" T - " .. ctld.Id .. message) + end end ctld.nextUnitId = 1; ctld.getNextUnitId = function() - ctld.nextUnitId = ctld.nextUnitId + 1 + ctld.nextUnitId = ctld.nextUnitId + 1 - return ctld.nextUnitId + return ctld.nextUnitId end ctld.nextGroupId = 1; ctld.getNextGroupId = function() - ctld.nextGroupId = ctld.nextGroupId + 1 + ctld.nextGroupId = ctld.nextGroupId + 1 - return ctld.nextGroupId + return ctld.nextGroupId end function ctld.getTransportUnit(_unitName) - if _unitName == nil then - return nil - end - - local transportUnitObject = Unit.getByName(_unitName) - - if transportUnitObject ~= nil and transportUnitObject:isActive() and transportUnitObject:getLife() > 0 then - return transportUnitObject - end - + if _unitName == nil then return nil + end + + local transportUnitObject = Unit.getByName(_unitName) + + if transportUnitObject ~= nil and transportUnitObject:isActive() and transportUnitObject:getLife() > 0 then + return transportUnitObject + end + return nil end function ctld.spawnCrateStatic(_country, _unitId, _point, _name, _weight, _side, _hdg, _model_type) + local _crate + local _spawnedCrate - local _crate - local _spawnedCrate + local hdg = _hdg or 0 - local hdg = _hdg or 0 + if ctld.staticBugWorkaround and ctld.slingLoad == false then + local _groupId = ctld.getNextGroupId() + local _groupName = "Crate Group #" .. _groupId - if ctld.staticBugWorkaround and ctld.slingLoad == false then - local _groupId = ctld.getNextGroupId() - local _groupName = "Crate Group #".._groupId + local _group = { + ["visible"] = false, + -- ["groupId"] = _groupId, + ["hidden"] = false, + ["units"] = {}, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = _groupName, + ["task"] = {}, + } - local _group = { - ["visible"] = false, - -- ["groupId"] = _groupId, - ["hidden"] = false, - ["units"] = {}, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = _groupName, - ["task"] = {}, - } + _group.units[1] = ctld.createUnit(_point.x, _point.z, hdg, { type = "UAZ-469", name = _name, unitId = _unitId }) - _group.units[1] = ctld.createUnit(_point.x , _point.z , hdg, {type="UAZ-469",name=_name,unitId=_unitId}) + --switch to MIST + _group.category = Group.Category.GROUND; + _group.country = _country; - --switch to MIST - _group.category = Group.Category.GROUND; - _group.country = _country; + local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) - local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) + -- Turn off AI + trigger.action.setGroupAIOff(_spawnedGroup) - -- Turn off AI - trigger.action.setGroupAIOff(_spawnedGroup) - - _spawnedCrate = Unit.getByName(_name) + _spawnedCrate = Unit.getByName(_name) + else + if _model_type ~= nil then + _crate = mist.utils.deepCopy(ctld.spawnableCratesModels[_model_type]) + elseif ctld.slingLoad then + _crate = mist.utils.deepCopy(ctld.spawnableCratesModels["sling"]) else - if _model_type ~= nil then - _crate = mist.utils.deepCopy(ctld.spawnableCratesModels[_model_type]) - elseif ctld.slingLoad then - _crate = mist.utils.deepCopy(ctld.spawnableCratesModels["sling"]) - else - _crate = mist.utils.deepCopy(ctld.spawnableCratesModels["load"]) - end - - _crate["y"] = _point.z - _crate["x"] = _point.x - _crate["mass"] = _weight - _crate["name"] = _name - _crate["heading"] = hdg - _crate["country"] = _country - - mist.dynAddStatic(_crate) - - _spawnedCrate = StaticObject.getByName(_crate["name"]) + _crate = mist.utils.deepCopy(ctld.spawnableCratesModels["load"]) end + _crate["y"] = _point.z + _crate["x"] = _point.x + _crate["mass"] = _weight + _crate["name"] = _name + _crate["heading"] = hdg + _crate["country"] = _country - local _crateType = ctld.crateLookupTable[tostring(_weight)] + mist.dynAddStatic(_crate) - if _side == 1 then - ctld.spawnedCratesRED[_name] =_crateType - else - ctld.spawnedCratesBLUE[_name] = _crateType - end + _spawnedCrate = StaticObject.getByName(_crate["name"]) + end - return _spawnedCrate + + local _crateType = ctld.crateLookupTable[tostring(_weight)] + + if _side == 1 then + ctld.spawnedCratesRED[_name] = _crateType + else + ctld.spawnedCratesBLUE[_name] = _crateType + end + + return _spawnedCrate end function ctld.spawnFOBCrateStatic(_country, _unitId, _point, _name) + local _crate = { + ["category"] = "Fortifications", + ["shape_name"] = "konteiner_red1", + ["type"] = "Container red 1", + -- ["unitId"] = _unitId, + ["y"] = _point.z, + ["x"] = _point.x, + ["name"] = _name, + ["canCargo"] = false, + ["heading"] = 0, + } - local _crate = { - ["category"] = "Fortifications", - ["shape_name"] = "konteiner_red1", - ["type"] = "Container red 1", - -- ["unitId"] = _unitId, - ["y"] = _point.z, - ["x"] = _point.x, - ["name"] = _name, - ["canCargo"] = false, - ["heading"] = 0, - } + _crate["country"] = _country - _crate["country"] = _country + mist.dynAddStatic(_crate) - mist.dynAddStatic(_crate) + local _spawnedCrate = StaticObject.getByName(_crate["name"]) + --local _spawnedCrate = coalition.addStaticObject(_country, _crate) - local _spawnedCrate = StaticObject.getByName(_crate["name"]) - --local _spawnedCrate = coalition.addStaticObject(_country, _crate) - - return _spawnedCrate + return _spawnedCrate end - function ctld.spawnFOB(_country, _unitId, _point, _name) + local _crate = { + ["category"] = "Fortifications", + ["type"] = "outpost", + -- ["unitId"] = _unitId, + ["y"] = _point.z, + ["x"] = _point.x, + ["name"] = _name, + ["canCargo"] = false, + ["heading"] = 0, + } - local _crate = { - ["category"] = "Fortifications", - ["type"] = "outpost", - -- ["unitId"] = _unitId, - ["y"] = _point.z, - ["x"] = _point.x, - ["name"] = _name, - ["canCargo"] = false, - ["heading"] = 0, - } + _crate["country"] = _country + mist.dynAddStatic(_crate) + local _spawnedCrate = StaticObject.getByName(_crate["name"]) + --local _spawnedCrate = coalition.addStaticObject(_country, _crate) - _crate["country"] = _country - mist.dynAddStatic(_crate) - local _spawnedCrate = StaticObject.getByName(_crate["name"]) - --local _spawnedCrate = coalition.addStaticObject(_country, _crate) + local _id = ctld.getNextUnitId() + local _tower = { + ["type"] = "house2arm", + -- ["unitId"] = _id, + ["rate"] = 100, + ["y"] = _point.z + -36.57142857, + ["x"] = _point.x + 14.85714286, + ["name"] = "FOB Watchtower #" .. _id, + ["category"] = "Fortifications", + ["canCargo"] = false, + ["heading"] = 0, + } + --coalition.addStaticObject(_country, _tower) + _tower["country"] = _country - local _id = ctld.getNextUnitId() - local _tower = { - ["type"] = "house2arm", - -- ["unitId"] = _id, - ["rate"] = 100, - ["y"] = _point.z + -36.57142857, - ["x"] = _point.x + 14.85714286, - ["name"] = "FOB Watchtower #" .. _id, - ["category"] = "Fortifications", - ["canCargo"] = false, - ["heading"] = 0, - } - --coalition.addStaticObject(_country, _tower) - _tower["country"] = _country + mist.dynAddStatic(_tower) - mist.dynAddStatic(_tower) - - return _spawnedCrate + return _spawnedCrate end - function ctld.spawnCrate(_arguments, bypassCrateWaitTime) + local _status, _err = pcall(function(_args) + -- use the cargo weight to guess the type of unit as no way to add description :( + local _crateType = ctld.crateLookupTable[tostring(_args[2])] - local _status, _err = pcall(function(_args) - - -- use the cargo weight to guess the type of unit as no way to add description :( - local _crateType = ctld.crateLookupTable[tostring(_args[2])] - - local _heli = ctld.getTransportUnit(_args[1]) - if not _heli then - return - end - - -- check crate spam - if not(bypassCrateWaitTime) and _heli:getPlayerName() ~= nil and ctld.crateWait[_heli:getPlayerName()] and ctld.crateWait[_heli:getPlayerName()] > timer.getTime() then - ctld.displayMessageToGroup(_heli,ctld.i18n_translate("Sorry you must wait %1 seconds before you can get another crate", (ctld.crateWait[_heli:getPlayerName()] - timer.getTime())), 20) - return - end - - if _crateType and _crateType.multiple then - for _, weight in pairs(_crateType.multiple) do - local _aCrateType = ctld.crateLookupTable[tostring(weight)] - if _aCrateType then - ctld.spawnCrate({_args[1], _aCrateType.weight}, true) - end - end - return - end - - if _crateType ~= nil and _heli ~= nil and ctld.inAir(_heli) == false then - - if ctld.inLogisticsZone(_heli) == false then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not close enough to friendly logistics to get a crate!"), 10) - - return - end - - if ctld.isJTACUnitType(_crateType.unit) then - - local _limitHit = false - - if _heli:getCoalition() == 1 then - - if ctld.JTAC_LIMIT_RED == 0 then - _limitHit = true - else - ctld.JTAC_LIMIT_RED = ctld.JTAC_LIMIT_RED - 1 - end - else - if ctld.JTAC_LIMIT_BLUE == 0 then - _limitHit = true - else - ctld.JTAC_LIMIT_BLUE = ctld.JTAC_LIMIT_BLUE - 1 - end - end - - if _limitHit then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No more JTAC Crates Left!"), 10) - return - end - end - - if _heli:getPlayerName() ~= nil then - ctld.crateWait[_heli:getPlayerName()] = timer.getTime() + ctld.crateWaitTime - end - - local _heli = ctld.getTransportUnit(_args[1]) - - local _model_type = nil - - local _point = ctld.getPointAt12Oclock(_heli, 30) - local _position = "12" - - if ctld.unitDynamicCargoCapable(_heli) then - _model_type = "dynamic" - _point = ctld.getPointAt6Oclock(_heli, 15) - _position = "6" - end - - local _unitId = ctld.getNextUnitId() - - local _side = _heli:getCoalition() - - local _name = string.format("%s #%i", _crateType.desc, _unitId) - - ctld.spawnCrateStatic(_heli:getCountry(), _unitId, _point, _name, _crateType.weight, _side, 0, _model_type) - - -- add to move table - ctld.crateMove[_name] = _name - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock ", _crateType.desc, _crateType.weight, _position), 20) - - else - env.info("Couldn't find crate item to spawn") - end - end, _arguments) - - if (not _status) then - env.error(string.format("CTLD ERROR: %s", _err)) + local _heli = ctld.getTransportUnit(_args[1]) + if not _heli then + return end + + -- check crate spam + if not (bypassCrateWaitTime) and _heli:getPlayerName() ~= nil and ctld.crateWait[_heli:getPlayerName()] and ctld.crateWait[_heli:getPlayerName()] > timer.getTime() then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Sorry you must wait %1 seconds before you can get another crate", + (ctld.crateWait[_heli:getPlayerName()] - timer.getTime())), 20) + return + end + + if _crateType and _crateType.multiple then + for _, weight in pairs(_crateType.multiple) do + local _aCrateType = ctld.crateLookupTable[tostring(weight)] + if _aCrateType then + ctld.spawnCrate({ _args[1], _aCrateType.weight }, true) + end + end + return + end + + if _crateType ~= nil and _heli ~= nil and ctld.inAir(_heli) == false then + if ctld.inLogisticsZone(_heli) == false then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("You are not close enough to friendly logistics to get a crate!"), 10) + + return + end + + if ctld.isJTACUnitType(_crateType.unit) then + local _limitHit = false + + if _heli:getCoalition() == 1 then + if ctld.JTAC_LIMIT_RED == 0 then + _limitHit = true + else + ctld.JTAC_LIMIT_RED = ctld.JTAC_LIMIT_RED - 1 + end + else + if ctld.JTAC_LIMIT_BLUE == 0 then + _limitHit = true + else + ctld.JTAC_LIMIT_BLUE = ctld.JTAC_LIMIT_BLUE - 1 + end + end + + if _limitHit then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No more JTAC Crates Left!"), 10) + return + end + end + + if _heli:getPlayerName() ~= nil then + ctld.crateWait[_heli:getPlayerName()] = timer.getTime() + ctld.crateWaitTime + end + + local _heli = ctld.getTransportUnit(_args[1]) + + local _model_type = nil + + local _point = ctld.getPointAt12Oclock(_heli, 30) + local _position = "12" + + if ctld.unitDynamicCargoCapable(_heli) then + _model_type = "dynamic" + _point = ctld.getPointAt6Oclock(_heli, 15) + _position = "6" + end + + local _unitId = ctld.getNextUnitId() + + local _side = _heli:getCoalition() + + local _name = string.format("%s #%i", _crateType.desc, _unitId) + + ctld.spawnCrateStatic(_heli:getCountry(), _unitId, _point, _name, _crateType.weight, _side, 0, _model_type) + + -- add to move table + ctld.crateMove[_name] = _name + + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock ", + _crateType.desc, _crateType.weight, _position), 20) + else + env.info("Couldn't find crate item to spawn") + end + end, _arguments) + + if (not _status) then + env.error(string.format("CTLD ERROR: %s", _err)) + end end + --*************************************************************** ctld.randomCrateSpacing = 12 -- meters function ctld.getPointAt12Oclock(_unit, _offset) - return ctld.getPointAtDirection(_unit, _offset, 0) + return ctld.getPointAtDirection(_unit, _offset, 0) end function ctld.getPointAt6Oclock(_unit, _offset) - return ctld.getPointAtDirection(_unit, _offset, math.pi) + return ctld.getPointAtDirection(_unit, _offset, math.pi) end function ctld.getPointAtDirection(_unit, _offset, _directionInRadian) + ctld.logTrace("_offset = %s", ctld.p(_offset)) + local _randomOffsetX = math.random(ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing + local _randomOffsetZ = math.random(0, ctld.randomCrateSpacing) + ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) + ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) + local _position = _unit:getPosition() + --local _angle = math.atan2(_position.x.z, _position.x.x) + _directionInRadian + local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian + local _xOffset = math.cos(_angle) * _offset + _randomOffsetX + local _zOffset = math.sin(_angle) * _offset + _randomOffsetZ - ctld.logTrace("_offset = %s", ctld.p(_offset)) - local _randomOffsetX = math.random(ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing - local _randomOffsetZ = math.random(0, ctld.randomCrateSpacing) - ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) - ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) - local _position = _unit:getPosition() - --local _angle = math.atan2(_position.x.z, _position.x.x) + _directionInRadian - local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian - local _xOffset = math.cos(_angle) * _offset + _randomOffsetX - local _zOffset = math.sin(_angle) * _offset + _randomOffsetZ - - local _point = _unit:getPoint() - return { x = _point.x + _xOffset, z = _point.z + _zOffset, y = _point.y } + local _point = _unit:getPoint() + return { x = _point.x + _xOffset, z = _point.z + _zOffset, y = _point.y } end function ctld.troopsOnboard(_heli, _troops) + if ctld.inTransitTroops[_heli:getName()] ~= nil then + local _onboard = ctld.inTransitTroops[_heli:getName()] - if ctld.inTransitTroops[_heli:getName()] ~= nil then - - local _onboard = ctld.inTransitTroops[_heli:getName()] - - if _troops then - - if _onboard.troops ~= nil and _onboard.troops.units ~= nil and #_onboard.troops.units > 0 then - return true - else - return false - end - else - - if _onboard.vehicles ~= nil and _onboard.vehicles.units ~= nil and #_onboard.vehicles.units > 0 then - return true - else - return false - end - end - - else + if _troops then + if _onboard.troops ~= nil and _onboard.troops.units ~= nil and #_onboard.troops.units > 0 then + return true + else return false + end + else + if _onboard.vehicles ~= nil and _onboard.vehicles.units ~= nil and #_onboard.vehicles.units > 0 then + return true + else + return false + end end + else + return false + end end -- if its dropped by AI then there is no player name so return the type of unit function ctld.getPlayerNameOrType(_heli) - - if _heli:getPlayerName() == nil then - - return _heli:getTypeName() - else - return _heli:getPlayerName() - end + if _heli:getPlayerName() == nil then + return _heli:getTypeName() + else + return _heli:getPlayerName() + end end function ctld.inExtractZone(_heli) + local _heliPoint = _heli:getPoint() - local _heliPoint = _heli:getPoint() + for _, _zoneDetails in pairs(ctld.extractZones) do + --get distance to center + local _dist = ctld.getDistance(_heliPoint, _zoneDetails.point) - for _, _zoneDetails in pairs(ctld.extractZones) do - - --get distance to center - local _dist = ctld.getDistance(_heliPoint, _zoneDetails.point) - - if _dist <= _zoneDetails.radius then - return _zoneDetails - end + if _dist <= _zoneDetails.radius then + return _zoneDetails end + end - return false + return false end -- safe to fast rope if speed is less than 0.5 Meters per second function ctld.safeToFastRope(_heli) + if ctld.enableFastRopeInsertion == false then + return false + end - if ctld.enableFastRopeInsertion == false then - return false - end - - --landed or speed is less than 8 km/h and height is less than fast rope height - if (ctld.inAir(_heli) == false or (ctld.heightDiff(_heli) <= ctld.fastRopeMaximumHeight + 3.0 and mist.vec.mag(_heli:getVelocity()) < 2.2)) then - return true - end + --landed or speed is less than 8 km/h and height is less than fast rope height + if (ctld.inAir(_heli) == false or (ctld.heightDiff(_heli) <= ctld.fastRopeMaximumHeight + 3.0 and mist.vec.mag(_heli:getVelocity()) < 2.2)) then + return true + end end function ctld.metersToFeet(_meters) + local _feet = _meters * 3.2808399 - local _feet = _meters * 3.2808399 - - return mist.utils.round(_feet) + return mist.utils.round(_feet) end function ctld.inAir(_heli) + if _heli:inAir() == false then + return false + end - if _heli:inAir() == false then - return false - end - - -- less than 5 cm/s a second so landed - -- BUT AI can hold a perfect hover so ignore AI - if mist.vec.mag(_heli:getVelocity()) < 0.05 and _heli:getPlayerName() ~= nil then - return false - end - return true + -- less than 5 cm/s a second so landed + -- BUT AI can hold a perfect hover so ignore AI + if mist.vec.mag(_heli:getVelocity()) < 0.05 and _heli:getPlayerName() ~= nil then + return false + end + return true end function ctld.deployTroops(_heli, _troops) + local _onboard = ctld.inTransitTroops[_heli:getName()] - local _onboard = ctld.inTransitTroops[_heli:getName()] + -- deloy troops + if _troops then + if _onboard.troops ~= nil and #_onboard.troops.units > 0 then + if ctld.inAir(_heli) == false or ctld.safeToFastRope(_heli) then + -- check we're not in extract zone + local _extractZone = ctld.inExtractZone(_heli) - -- deloy troops - if _troops then - if _onboard.troops ~= nil and #_onboard.troops.units > 0 then - if ctld.inAir(_heli) == false or ctld.safeToFastRope(_heli) then + if _extractZone == false then + local _droppedTroops = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.troops, false) + if _onboard.troops.jtac or _droppedTroops:getName():lower():find("jtac") then + local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) + table.insert(ctld.jtacGeneratedLaserCodes, _code) + ctld.JTACStart(_droppedTroops:getName(), _code) + end - -- check we're not in extract zone - local _extractZone = ctld.inExtractZone(_heli) + if _heli:getCoalition() == 1 then + table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) + else + table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) + end - if _extractZone == false then + ctld.inTransitTroops[_heli:getName()].troops = nil + ctld.adaptWeightToCargo(_heli:getName()) - local _droppedTroops = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.troops, false) - if _onboard.troops.jtac or _droppedTroops:getName():lower():find("jtac") then - local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) - table.insert(ctld.jtacGeneratedLaserCodes, _code) - ctld.JTACStart(_droppedTroops:getName(), _code) - end + if ctld.inAir(_heli) then + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 fast-ropped troops from %2 into combat", + ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + else + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 dropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName()), 10) + end - if _heli:getCoalition() == 1 then + ctld.processCallback({ unit = _heli, unloaded = _droppedTroops, action = "dropped_troops" }) + else + --extract zone! + local _droppedCount = trigger.misc.getUserFlag(_extractZone.flag) - table.insert(ctld.droppedTroopsRED, _droppedTroops:getName()) - else + _droppedCount = (#_onboard.troops.units) + _droppedCount - table.insert(ctld.droppedTroopsBLUE, _droppedTroops:getName()) - end + trigger.action.setUserFlag(_extractZone.flag, _droppedCount) - ctld.inTransitTroops[_heli:getName()].troops = nil - ctld.adaptWeightToCargo(_heli:getName()) + ctld.inTransitTroops[_heli:getName()].troops = nil + ctld.adaptWeightToCargo(_heli:getName()) - if ctld.inAir(_heli) then - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 fast-ropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - else - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped troops from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - end + if ctld.inAir(_heli) then + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 fast-ropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName(), _extractZone.name), 10) + else + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 dropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName(), _extractZone.name), 10) + end + end + else + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Too high or too fast to drop troops into combat! Hover below %1 feet or land.", + ctld.metersToFeet(ctld.fastRopeMaximumHeight)), 10) + end + end + else + if ctld.inAir(_heli) == false then + if _onboard.vehicles ~= nil and #_onboard.vehicles.units > 0 then + local _droppedVehicles = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.vehicles, true) - ctld.processCallback({unit = _heli, unloaded = _droppedTroops, action = "dropped_troops"}) - - - else - --extract zone! - local _droppedCount = trigger.misc.getUserFlag(_extractZone.flag) - - _droppedCount = (#_onboard.troops.units) + _droppedCount - - trigger.action.setUserFlag(_extractZone.flag, _droppedCount) - - ctld.inTransitTroops[_heli:getName()].troops = nil - ctld.adaptWeightToCargo(_heli:getName()) - - if ctld.inAir(_heli) then - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 fast-ropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), _heli:getTypeName(), _extractZone.name), 10) - else - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped troops from %2 into %3", ctld.getPlayerNameOrType(_heli), _heli:getTypeName(), _extractZone.name), 10) - end - end - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Too high or too fast to drop troops into combat! Hover below %1 feet or land.", ctld.metersToFeet(ctld.fastRopeMaximumHeight)), 10) - end + if _heli:getCoalition() == 1 then + table.insert(ctld.droppedVehiclesRED, _droppedVehicles:getName()) + else + table.insert(ctld.droppedVehiclesBLUE, _droppedVehicles:getName()) end - else - if ctld.inAir(_heli) == false then - if _onboard.vehicles ~= nil and #_onboard.vehicles.units > 0 then + ctld.inTransitTroops[_heli:getName()].vehicles = nil + ctld.adaptWeightToCargo(_heli:getName()) - local _droppedVehicles = ctld.spawnDroppedGroup(_heli:getPoint(), _onboard.vehicles, true) + ctld.processCallback({ unit = _heli, unloaded = _droppedVehicles, action = "dropped_vehicles" }) - if _heli:getCoalition() == 1 then - - table.insert(ctld.droppedVehiclesRED, _droppedVehicles:getName()) - else - - table.insert(ctld.droppedVehiclesBLUE, _droppedVehicles:getName()) - end - - ctld.inTransitTroops[_heli:getName()].vehicles = nil - ctld.adaptWeightToCargo(_heli:getName()) - - ctld.processCallback({unit = _heli, unloaded = _droppedVehicles, action = "dropped_vehicles"}) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped vehicles from %2 into combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - end - end + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 dropped vehicles from %2 into combat", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName()), 10) + end end + end end -function ctld.insertIntoTroopsArray(_troopType,_count,_troopArray,_troopName) - - for _i = 1, _count do - local _unitId = ctld.getNextUnitId() - table.insert(_troopArray, { type = _troopType, unitId = _unitId, name = string.format("Dropped %s #%i", _troopName or _troopType, _unitId) }) - end - - return _troopArray +function ctld.insertIntoTroopsArray(_troopType, _count, _troopArray, _troopName) + for _i = 1, _count do + local _unitId = ctld.getNextUnitId() + table.insert(_troopArray, + { type = _troopType, unitId = _unitId, name = string.format("Dropped %s #%i", _troopName or _troopType, + _unitId) }) + end + return _troopArray end - function ctld.generateTroopTypes(_side, _countOrTemplate, _country) - local _troops = {} + local _troops = {} + local _weight = 0 + local _hasJTAC = false + + local function getSoldiersWeight(count, additionalWeight) local _weight = 0 - local _hasJTAC = false + for i = 1, count do + local _soldierWeight = math.random(90, 120) * ctld.SOLDIER_WEIGHT / 100 + _weight = _weight + _soldierWeight + ctld.KIT_WEIGHT + additionalWeight + end + return _weight + end - local function getSoldiersWeight(count, additionalWeight) - local _weight = 0 - for i = 1, count do - local _soldierWeight = math.random(90, 120) * ctld.SOLDIER_WEIGHT / 100 - _weight = _weight + _soldierWeight + ctld.KIT_WEIGHT + additionalWeight - end - return _weight + if type(_countOrTemplate) == "table" then + if _countOrTemplate.aa then + if _side == 2 then + _troops = ctld.insertIntoTroopsArray("Soldier stinger", _countOrTemplate.aa, _troops) + else + _troops = ctld.insertIntoTroopsArray("SA-18 Igla manpad", _countOrTemplate.aa, _troops) + end + _weight = _weight + getSoldiersWeight(_countOrTemplate.aa, ctld.MANPAD_WEIGHT) end - if type(_countOrTemplate) == "table" then - - if _countOrTemplate.aa then - if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier stinger",_countOrTemplate.aa,_troops) - else - _troops = ctld.insertIntoTroopsArray("SA-18 Igla manpad",_countOrTemplate.aa,_troops) - end - _weight = _weight + getSoldiersWeight(_countOrTemplate.aa, ctld.MANPAD_WEIGHT) - end - - if _countOrTemplate.inf then - if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.inf,_troops) - else - _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.inf,_troops) - end - _weight = _weight + getSoldiersWeight(_countOrTemplate.inf, ctld.RIFLE_WEIGHT) - end - - if _countOrTemplate.mg then - if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M249",_countOrTemplate.mg,_troops) - else - _troops = ctld.insertIntoTroopsArray("Paratrooper AKS-74",_countOrTemplate.mg,_troops) - end - _weight = _weight + getSoldiersWeight(_countOrTemplate.mg, ctld.MG_WEIGHT) - end - - if _countOrTemplate.at then - _troops = ctld.insertIntoTroopsArray("Paratrooper RPG-16",_countOrTemplate.at,_troops) - _weight = _weight + getSoldiersWeight(_countOrTemplate.at, ctld.RPG_WEIGHT) - end - - if _countOrTemplate.mortar then - _troops = ctld.insertIntoTroopsArray("2B11 mortar",_countOrTemplate.mortar,_troops) - _weight = _weight + getSoldiersWeight(_countOrTemplate.mortar, ctld.MORTAR_WEIGHT) - end - - if _countOrTemplate.jtac then - if _side == 2 then - _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG",_countOrTemplate.jtac,_troops, "JTAC") - else - _troops = ctld.insertIntoTroopsArray("Infantry AK",_countOrTemplate.jtac,_troops, "JTAC") - end - _hasJTAC = true - _weight = _weight + getSoldiersWeight(_countOrTemplate.jtac, ctld.JTAC_WEIGHT + ctld.RIFLE_WEIGHT) - end - - else - for _i = 1, _countOrTemplate do - - local _unitType = "Infantry AK" - - if _side == 2 then - if _i <=2 then - _unitType = "Soldier M249" - _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) - elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then - _unitType = "Paratrooper RPG-16" - _weight = _weight + getSoldiersWeight(1, ctld.RPG_WEIGHT) - elseif ctld.spawnStinger and _i > 4 and _i <= 5 then - _unitType = "Soldier stinger" - _weight = _weight + getSoldiersWeight(1, ctld.MANPAD_WEIGHT) - else - _unitType = "Soldier M4 GRG" - _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) - end - else - if _i <=2 then - _unitType = "Paratrooper AKS-74" - _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) - elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then - _unitType = "Paratrooper RPG-16" - _weight = _weight + getSoldiersWeight(1, ctld.RPG_WEIGHT) - elseif ctld.spawnStinger and _i > 4 and _i <= 5 then - _unitType = "SA-18 Igla manpad" - _weight = _weight + getSoldiersWeight(1, ctld.MANPAD_WEIGHT) - else - _unitType = "Infantry AK" - _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) - end - end - - local _unitId = ctld.getNextUnitId() - - _troops[_i] = { type = _unitType, unitId = _unitId, name = string.format("Dropped %s #%i", _unitType, _unitId) } - end + if _countOrTemplate.inf then + if _side == 2 then + _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG", _countOrTemplate.inf, _troops) + else + _troops = ctld.insertIntoTroopsArray("Infantry AK", _countOrTemplate.inf, _troops) + end + _weight = _weight + getSoldiersWeight(_countOrTemplate.inf, ctld.RIFLE_WEIGHT) end - local _groupId = ctld.getNextGroupId() - local _groupName = "Dropped Group" - if _hasJTAC then - _groupName = "Dropped JTAC Group" + if _countOrTemplate.mg then + if _side == 2 then + _troops = ctld.insertIntoTroopsArray("Soldier M249", _countOrTemplate.mg, _troops) + else + _troops = ctld.insertIntoTroopsArray("Paratrooper AKS-74", _countOrTemplate.mg, _troops) + end + _weight = _weight + getSoldiersWeight(_countOrTemplate.mg, ctld.MG_WEIGHT) end - local _details = { units = _troops, groupId = _groupId, groupName = string.format("%s %i", _groupName, _groupId), side = _side, country = _country, weight = _weight, jtac = _hasJTAC } - return _details + if _countOrTemplate.at then + _troops = ctld.insertIntoTroopsArray("Paratrooper RPG-16", _countOrTemplate.at, _troops) + _weight = _weight + getSoldiersWeight(_countOrTemplate.at, ctld.RPG_WEIGHT) + end + + if _countOrTemplate.mortar then + _troops = ctld.insertIntoTroopsArray("2B11 mortar", _countOrTemplate.mortar, _troops) + _weight = _weight + getSoldiersWeight(_countOrTemplate.mortar, ctld.MORTAR_WEIGHT) + end + + if _countOrTemplate.jtac then + if _side == 2 then + _troops = ctld.insertIntoTroopsArray("Soldier M4 GRG", _countOrTemplate.jtac, _troops, "JTAC") + else + _troops = ctld.insertIntoTroopsArray("Infantry AK", _countOrTemplate.jtac, _troops, "JTAC") + end + _hasJTAC = true + _weight = _weight + getSoldiersWeight(_countOrTemplate.jtac, ctld.JTAC_WEIGHT + ctld.RIFLE_WEIGHT) + end + else + for _i = 1, _countOrTemplate do + local _unitType = "Infantry AK" + + if _side == 2 then + if _i <= 2 then + _unitType = "Soldier M249" + _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) + elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then + _unitType = "Paratrooper RPG-16" + _weight = _weight + getSoldiersWeight(1, ctld.RPG_WEIGHT) + elseif ctld.spawnStinger and _i > 4 and _i <= 5 then + _unitType = "Soldier stinger" + _weight = _weight + getSoldiersWeight(1, ctld.MANPAD_WEIGHT) + else + _unitType = "Soldier M4 GRG" + _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) + end + else + if _i <= 2 then + _unitType = "Paratrooper AKS-74" + _weight = _weight + getSoldiersWeight(1, ctld.MG_WEIGHT) + elseif ctld.spawnRPGWithCoalition and _i > 2 and _i <= 4 then + _unitType = "Paratrooper RPG-16" + _weight = _weight + getSoldiersWeight(1, ctld.RPG_WEIGHT) + elseif ctld.spawnStinger and _i > 4 and _i <= 5 then + _unitType = "SA-18 Igla manpad" + _weight = _weight + getSoldiersWeight(1, ctld.MANPAD_WEIGHT) + else + _unitType = "Infantry AK" + _weight = _weight + getSoldiersWeight(1, ctld.RIFLE_WEIGHT) + end + end + + local _unitId = ctld.getNextUnitId() + + _troops[_i] = { type = _unitType, unitId = _unitId, name = string.format("Dropped %s #%i", _unitType, _unitId) } + end + end + + local _groupId = ctld.getNextGroupId() + local _groupName = "Dropped Group" + if _hasJTAC then + _groupName = "Dropped JTAC Group" + end + local _details = { units = _troops, groupId = _groupId, groupName = string.format("%s %i", _groupName, _groupId), side = + _side, country = _country, weight = _weight, jtac = _hasJTAC } + + return _details end --Special F10 function for players for troops function ctld.unloadExtractTroops(_args) + local _heli = ctld.getTransportUnit(_args[1]) - local _heli = ctld.getTransportUnit(_args[1]) - - if _heli == nil then - return false - end + if _heli == nil then + return false + end - local _extract = nil - if not ctld.inAir(_heli) then - if _heli:getCoalition() == 1 then - _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) - else - _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) - end - - end - - if _extract ~= nil and not ctld.troopsOnboard(_heli, true) then - -- search for nearest troops to pickup - return ctld.extractTroops({_heli:getName(), true}) + local _extract = nil + if not ctld.inAir(_heli) then + if _heli:getCoalition() == 1 then + _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) else - return ctld.unloadTroops({_heli:getName(),true,true}) + _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) end + end - + if _extract ~= nil and not ctld.troopsOnboard(_heli, true) then + -- search for nearest troops to pickup + return ctld.extractTroops({ _heli:getName(), true }) + else + return ctld.unloadTroops({ _heli:getName(), true, true }) + end end -- load troops onto vehicle function ctld.loadTroops(_heli, _troops, _numberOrTemplate) + -- load troops + vehicles if c130 or herc + -- "M1045 HMMWV TOW" + -- "M1043 HMMWV Armament" + local _onboard = ctld.inTransitTroops[_heli:getName()] - -- load troops + vehicles if c130 or herc - -- "M1045 HMMWV TOW" - -- "M1043 HMMWV Armament" - local _onboard = ctld.inTransitTroops[_heli:getName()] + --number doesnt apply to vehicles + if _numberOrTemplate == nil or (type(_numberOrTemplate) ~= "table" and type(_numberOrTemplate) ~= "number") then + _numberOrTemplate = ctld.getTransportLimit(_heli:getTypeName()) + end - --number doesnt apply to vehicles - if _numberOrTemplate == nil or (type(_numberOrTemplate) ~= "table" and type(_numberOrTemplate) ~= "number") then - _numberOrTemplate = ctld.getTransportLimit(_heli:getTypeName()) - end + if _onboard == nil then + _onboard = { troops = {}, vehicles = {} } + end - if _onboard == nil then - _onboard = { troops = {}, vehicles = {} } - end + local _list + if _heli:getCoalition() == 1 then + _list = ctld.vehiclesForTransportRED + else + _list = ctld.vehiclesForTransportBLUE + end - local _list - if _heli:getCoalition() == 1 then - _list = ctld.vehiclesForTransportRED - else - _list = ctld.vehiclesForTransportBLUE - end + if _troops then + _onboard.troops = ctld.generateTroopTypes(_heli:getCoalition(), _numberOrTemplate, _heli:getCountry()) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 loaded troops into %2", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - if _troops then - _onboard.troops = ctld.generateTroopTypes(_heli:getCoalition(), _numberOrTemplate, _heli:getCountry()) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded troops into %2", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) + ctld.processCallback({ unit = _heli, onboard = _onboard.troops, action = "load_troops" }) + else + _onboard.vehicles = ctld.generateVehiclesForTransport(_heli:getCoalition(), _heli:getCountry()) - ctld.processCallback({unit = _heli, onboard = _onboard.troops, action = "load_troops"}) - else + local _count = #_list - _onboard.vehicles = ctld.generateVehiclesForTransport(_heli:getCoalition(), _heli:getCountry()) + ctld.processCallback({ unit = _heli, onboard = _onboard.vehicles, action = "load_vehicles" }) - local _count = #_list + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 loaded %2 vehicles into %3", ctld.getPlayerNameOrType(_heli), _count, + _heli:getTypeName()), 10) + end - ctld.processCallback({unit = _heli, onboard = _onboard.vehicles, action = "load_vehicles"}) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded %2 vehicles into %3", ctld.getPlayerNameOrType(_heli), _count, _heli:getTypeName()), 10) - end - - ctld.inTransitTroops[_heli:getName()] = _onboard - ctld.adaptWeightToCargo(_heli:getName()) + ctld.inTransitTroops[_heli:getName()] = _onboard + ctld.adaptWeightToCargo(_heli:getName()) end function ctld.generateVehiclesForTransport(_side, _country) - - local _vehicles = {} - local _list - if _side == 1 then - _list = ctld.vehiclesForTransportRED - else - _list = ctld.vehiclesForTransportBLUE - end + local _vehicles = {} + local _list + if _side == 1 then + _list = ctld.vehiclesForTransportRED + else + _list = ctld.vehiclesForTransportBLUE + end - for _i, _type in ipairs(_list) do - - local _unitId = ctld.getNextUnitId() - local _weight = ctld.vehiclesWeight[_type] or 2500 - _vehicles[_i] = { type = _type, unitId = _unitId, name = string.format("Dropped %s #%i", _type, _unitId), weight = _weight } - end + for _i, _type in ipairs(_list) do + local _unitId = ctld.getNextUnitId() + local _weight = ctld.vehiclesWeight[_type] or 2500 + _vehicles[_i] = { type = _type, unitId = _unitId, name = string.format("Dropped %s #%i", _type, _unitId), weight = + _weight } + end - local _groupId = ctld.getNextGroupId() - local _details = { units = _vehicles, groupId = _groupId, groupName = string.format("Dropped Group %i", _groupId), side = _side, country = _country } + local _groupId = ctld.getNextGroupId() + local _details = { units = _vehicles, groupId = _groupId, groupName = string.format("Dropped Group %i", _groupId), side = + _side, country = _country } - return _details + return _details end function ctld.loadUnloadFOBCrate(_args) + local _heli = ctld.getTransportUnit(_args[1]) + local _troops = _args[2] - local _heli = ctld.getTransportUnit(_args[1]) - local _troops = _args[2] + if _heli == nil then + return + end - if _heli == nil then - return - end + if ctld.inAir(_heli) == true then + return + end - if ctld.inAir(_heli) == true then - return - end + local _side = _heli:getCoalition() + + local _inZone = ctld.inLogisticsZone(_heli) + local _crateOnboard = ctld.inTransitFOBCrates[_heli:getName()] ~= nil + + if _inZone == false and _crateOnboard == true then + ctld.inTransitFOBCrates[_heli:getName()] = nil + + local _position = _heli:getPosition() + + --try to spawn at 6 oclock to us + local _angle = math.atan2(_position.x.z, _position.x.x) + local _xOffset = math.cos(_angle) * -60 + local _yOffset = math.sin(_angle) * -60 + + local _point = _heli:getPoint() local _side = _heli:getCoalition() - local _inZone = ctld.inLogisticsZone(_heli) - local _crateOnboard = ctld.inTransitFOBCrates[_heli:getName()] ~= nil + local _unitId = ctld.getNextUnitId() - if _inZone == false and _crateOnboard == true then + local _name = string.format("FOB Crate #%i", _unitId) - ctld.inTransitFOBCrates[_heli:getName()] = nil - - local _position = _heli:getPosition() - - --try to spawn at 6 oclock to us - local _angle = math.atan2(_position.x.z, _position.x.x) - local _xOffset = math.cos(_angle) * -60 - local _yOffset = math.sin(_angle) * -60 - - local _point = _heli:getPoint() - - local _side = _heli:getCoalition() - - local _unitId = ctld.getNextUnitId() - - local _name = string.format("FOB Crate #%i", _unitId) - - local _spawnedCrate = ctld.spawnFOBCrateStatic(_heli:getCountry(), ctld.getNextUnitId(), { x = _point.x + _xOffset, z = _point.z + _yOffset }, _name) - - if _side == 1 then - ctld.droppedFOBCratesRED[_name] = _name - else - ctld.droppedFOBCratesBLUE[_name] = _name - end - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 delivered a FOB Crate", ctld.getPlayerNameOrType(_heli)), 10) - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Delivered FOB Crate 60m at 6'oclock to you"), 10) - - elseif _inZone == true and _crateOnboard == true then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate dropped back to base"), 10) - - ctld.inTransitFOBCrates[_heli:getName()] = nil - - elseif _inZone == true and _crateOnboard == false then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) - - ctld.inTransitFOBCrates[_heli:getName()] = true - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) + local _spawnedCrate = ctld.spawnFOBCrateStatic(_heli:getCountry(), ctld.getNextUnitId(), + { x = _point.x + _xOffset, z = _point.z + _yOffset }, _name) + if _side == 1 then + ctld.droppedFOBCratesRED[_name] = _name else - - -- nearest Crate - local _crates = ctld.getCratesAndDistance(_heli) - local _nearestCrate = ctld.getClosestCrate(_heli, _crates, "FOB") - - if _nearestCrate ~= nil and _nearestCrate.dist < 150 then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) - ctld.inTransitFOBCrates[_heli:getName()] = true - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) - - if _side == 1 then - ctld.droppedFOBCratesRED[_nearestCrate.crateUnit:getName()] = nil - else - ctld.droppedFOBCratesBLUE[_nearestCrate.crateUnit:getName()] = nil - end - - --remove - _nearestCrate.crateUnit:destroy() - - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("There are no friendly logistic units nearby to load a FOB crate from!"), 10) - end + ctld.droppedFOBCratesBLUE[_name] = _name end + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 delivered a FOB Crate", ctld.getPlayerNameOrType(_heli)), 10) + + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Delivered FOB Crate 60m at 6'oclock to you"), 10) + elseif _inZone == true and _crateOnboard == true then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate dropped back to base"), 10) + + ctld.inTransitFOBCrates[_heli:getName()] = nil + elseif _inZone == true and _crateOnboard == false then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) + + ctld.inTransitFOBCrates[_heli:getName()] = true + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) + else + -- nearest Crate + local _crates = ctld.getCratesAndDistance(_heli) + local _nearestCrate = ctld.getClosestCrate(_heli, _crates, "FOB") + + if _nearestCrate ~= nil and _nearestCrate.dist < 150 then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("FOB Crate Loaded"), 10) + ctld.inTransitFOBCrates[_heli:getName()] = true + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 loaded a FOB Crate ready for delivery!", ctld.getPlayerNameOrType(_heli)), 10) + + if _side == 1 then + ctld.droppedFOBCratesRED[_nearestCrate.crateUnit:getName()] = nil + else + ctld.droppedFOBCratesBLUE[_nearestCrate.crateUnit:getName()] = nil + end + + --remove + _nearestCrate.crateUnit:destroy() + else + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("There are no friendly logistic units nearby to load a FOB crate from!"), 10) + end + end end function ctld.loadTroopsFromZone(_args) + local _heli = ctld.getTransportUnit(_args[1]) + local _troops = _args[2] + local _groupTemplate = _args[3] or "" + local _allowExtract = _args[4] - local _heli = ctld.getTransportUnit(_args[1]) - local _troops = _args[2] - local _groupTemplate = _args[3] or "" - local _allowExtract = _args[4] + if _heli == nil then + return false + end - if _heli == nil then - return false - end - - local _zone = ctld.inPickupZone(_heli) - - if ctld.troopsOnboard(_heli, _troops) then - - if _troops then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) - end - - return false - end - - local _extract - - if _allowExtract then - -- first check for extractable troops regardless of if we're in a zone or not - if _troops then - if _heli:getCoalition() == 1 then - _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) - else - _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) - end - else - - if _heli:getCoalition() == 1 then - _extract = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) - else - _extract = ctld.findNearestGroup(_heli, ctld.droppedVehiclesBLUE) - end - end - end - - if _extract ~= nil then - -- search for nearest troops to pickup - return ctld.extractTroops({_heli:getName(), _troops}) - elseif _zone.inZone == true then - - if _zone.limit - 1 >= 0 then - -- decrease zone counter by 1 - ctld.updateZoneCounter(_zone.index, -1) - - ctld.loadTroops(_heli, _troops,_groupTemplate) - - return true - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("This area has no more reinforcements available!"), 20) - - return false - end + local _zone = ctld.inPickupZone(_heli) + if ctld.troopsOnboard(_heli, _troops) then + if _troops then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) else - if _allowExtract then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone and no one is nearby to extract"), 10) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone"), 10) - end - - return false + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) end + + return false + end + + local _extract + + if _allowExtract then + -- first check for extractable troops regardless of if we're in a zone or not + if _troops then + if _heli:getCoalition() == 1 then + _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) + else + _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) + end + else + if _heli:getCoalition() == 1 then + _extract = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) + else + _extract = ctld.findNearestGroup(_heli, ctld.droppedVehiclesBLUE) + end + end + end + + if _extract ~= nil then + -- search for nearest troops to pickup + return ctld.extractTroops({ _heli:getName(), _troops }) + elseif _zone.inZone == true then + if _zone.limit - 1 >= 0 then + -- decrease zone counter by 1 + ctld.updateZoneCounter(_zone.index, -1) + + ctld.loadTroops(_heli, _troops, _groupTemplate) + + return true + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("This area has no more reinforcements available!"), 20) + + return false + end + else + if _allowExtract then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("You are not in a pickup zone and no one is nearby to extract"), 10) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone"), 10) + end + + return false + end end - - function ctld.unloadTroops(_args) + local _heli = ctld.getTransportUnit(_args[1]) + local _troops = _args[2] - local _heli = ctld.getTransportUnit(_args[1]) - local _troops = _args[2] + if _heli == nil then + return false + end - if _heli == nil then - return false + local _zone = ctld.inPickupZone(_heli) + if not ctld.troopsOnboard(_heli, _troops) then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No one to unload"), 10) + + return false + else + -- troops must be onboard to get here + if _zone.inZone == true then + if _troops then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped troops back to base"), 20) + + ctld.processCallback({ unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].troops, action = + "unload_troops_zone" }) + + ctld.inTransitTroops[_heli:getName()].troops = nil + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped vehicles back to base"), 20) + + ctld.processCallback({ unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].vehicles, action = + "unload_vehicles_zone" }) + + ctld.inTransitTroops[_heli:getName()].vehicles = nil + end + + ctld.adaptWeightToCargo(_heli:getName()) + + -- increase zone counter by 1 + ctld.updateZoneCounter(_zone.index, 1) + + return true + elseif ctld.troopsOnboard(_heli, _troops) then + return ctld.deployTroops(_heli, _troops) end - - local _zone = ctld.inPickupZone(_heli) - if not ctld.troopsOnboard(_heli, _troops) then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No one to unload"), 10) - - return false - else - - -- troops must be onboard to get here - if _zone.inZone == true then - - if _troops then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped troops back to base"), 20) - - ctld.processCallback({unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].troops, action = "unload_troops_zone"}) - - ctld.inTransitTroops[_heli:getName()].troops = nil - - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Dropped vehicles back to base"), 20) - - ctld.processCallback({unit = _heli, unloaded = ctld.inTransitTroops[_heli:getName()].vehicles, action = "unload_vehicles_zone"}) - - ctld.inTransitTroops[_heli:getName()].vehicles = nil - end - - ctld.adaptWeightToCargo(_heli:getName()) - - -- increase zone counter by 1 - ctld.updateZoneCounter(_zone.index, 1) - - return true - - elseif ctld.troopsOnboard(_heli, _troops) then - - return ctld.deployTroops(_heli, _troops) - end - end - + end end function ctld.extractTroops(_args) + local _heli = ctld.getTransportUnit(_args[1]) + local _troops = _args[2] - local _heli = ctld.getTransportUnit(_args[1]) - local _troops = _args[2] + if _heli == nil then + return false + end - if _heli == nil then - return false - end - - if ctld.inAir(_heli) then - return false - end - - if ctld.troopsOnboard(_heli, _troops) then - if _troops then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) - end - - return false - end - - local _onboard = ctld.inTransitTroops[_heli:getName()] - - if _onboard == nil then - _onboard = { troops = nil, vehicles = nil } - end - - local _extracted = false + if ctld.inAir(_heli) then + return false + end + if ctld.troopsOnboard(_heli, _troops) then if _troops then - - local _extractTroops - - if _heli:getCoalition() == 1 then - _extractTroops = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) - else - _extractTroops = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) - end - - - if _extractTroops ~= nil then - - local _limit = ctld.getTransportLimit(_heli:getTypeName()) - - local _size = #_extractTroops.group:getUnits() - - if _limit < #_extractTroops.group:getUnits() then - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3", _size, _limit, _heli:getTypeName()), 20) - - return - end - - _onboard.troops = _extractTroops.details - _onboard.troops.weight = #_extractTroops.group:getUnits() * 130 -- default to 130kg per soldier - - if _extractTroops.group:getName():lower():find("jtac") then - _onboard.troops.jtac = true - end - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 extracted troops in %2 from combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - - if _heli:getCoalition() == 1 then - ctld.droppedTroopsRED[_extractTroops.group:getName()] = nil - else - ctld.droppedTroopsBLUE[_extractTroops.group:getName()] = nil - end - - ctld.processCallback({unit = _heli, extracted = _extractTroops, action = "extract_troops"}) - - --remove - _extractTroops.group:destroy() - - _extracted = true - else - _onboard.troops = nil - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable troops nearby!"), 20) - end - + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have troops onboard."), 10) else - - local _extractVehicles - - - if _heli:getCoalition() == 1 then - - _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) - else - - _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesBLUE) - end - - if _extractVehicles ~= nil then - _onboard.vehicles = _extractVehicles.details - - if _heli:getCoalition() == 1 then - - ctld.droppedVehiclesRED[_extractVehicles.group:getName()] = nil - else - - ctld.droppedVehiclesBLUE[_extractVehicles.group:getName()] = nil - end - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 extracted vehicles in %2 from combat", ctld.getPlayerNameOrType(_heli), _heli:getTypeName()), 10) - - ctld.processCallback({unit = _heli, extracted = _extractVehicles, action = "extract_vehicles"}) - --remove - _extractVehicles.group:destroy() - _extracted = true - - else - _onboard.vehicles = nil - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable vehicles nearby!"), 20) - end + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) end - ctld.inTransitTroops[_heli:getName()] = _onboard - ctld.adaptWeightToCargo(_heli:getName()) + return false + end - return _extracted + local _onboard = ctld.inTransitTroops[_heli:getName()] + + if _onboard == nil then + _onboard = { troops = nil, vehicles = nil } + end + + local _extracted = false + + if _troops then + local _extractTroops + + if _heli:getCoalition() == 1 then + _extractTroops = ctld.findNearestGroup(_heli, ctld.droppedTroopsRED) + else + _extractTroops = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) + end + + + if _extractTroops ~= nil then + local _limit = ctld.getTransportLimit(_heli:getTypeName()) + + local _size = #_extractTroops.group:getUnits() + + if _limit < #_extractTroops.group:getUnits() then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3", _size, + _limit, _heli:getTypeName()), 20) + + return + end + + _onboard.troops = _extractTroops.details + _onboard.troops.weight = #_extractTroops.group:getUnits() * 130 -- default to 130kg per soldier + + if _extractTroops.group:getName():lower():find("jtac") then + _onboard.troops.jtac = true + end + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 extracted troops in %2 from combat", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName()), 10) + + if _heli:getCoalition() == 1 then + ctld.droppedTroopsRED[_extractTroops.group:getName()] = nil + else + ctld.droppedTroopsBLUE[_extractTroops.group:getName()] = nil + end + + ctld.processCallback({ unit = _heli, extracted = _extractTroops, action = "extract_troops" }) + + --remove + _extractTroops.group:destroy() + + _extracted = true + else + _onboard.troops = nil + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable troops nearby!"), 20) + end + else + local _extractVehicles + + + if _heli:getCoalition() == 1 then + _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) + else + _extractVehicles = ctld.findNearestGroup(_heli, ctld.droppedVehiclesBLUE) + end + + if _extractVehicles ~= nil then + _onboard.vehicles = _extractVehicles.details + + if _heli:getCoalition() == 1 then + ctld.droppedVehiclesRED[_extractVehicles.group:getName()] = nil + else + ctld.droppedVehiclesBLUE[_extractVehicles.group:getName()] = nil + end + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 extracted vehicles in %2 from combat", ctld.getPlayerNameOrType(_heli), + _heli:getTypeName()), 10) + + ctld.processCallback({ unit = _heli, extracted = _extractVehicles, action = "extract_vehicles" }) + --remove + _extractVehicles.group:destroy() + _extracted = true + else + _onboard.vehicles = nil + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No extractable vehicles nearby!"), 20) + end + end + + ctld.inTransitTroops[_heli:getName()] = _onboard + ctld.adaptWeightToCargo(_heli:getName()) + + return _extracted end - function ctld.checkTroopStatus(_args) - local _unitName = _args[1] - --list onboard troops, if c130 - local _heli = ctld.getTransportUnit(_unitName) + local _unitName = _args[1] + --list onboard troops, if c130 + local _heli = ctld.getTransportUnit(_unitName) - if _heli == nil then - return - end + if _heli == nil then + return + end - local _, _message = ctld.getWeightOfCargo(_unitName) - if _message and _message ~= "" then - ctld.displayMessageToGroup(_heli, _message, 10) - end + local _, _message = ctld.getWeightOfCargo(_unitName) + if _message and _message ~= "" then + ctld.displayMessageToGroup(_heli, _message, 10) + end end -- Removes troops from transport when it dies function ctld.checkTransportStatus() + timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 3) - timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 3) + for _, _name in ipairs(ctld.transportPilotNames) do + local _transUnit = ctld.getTransportUnit(_name) - for _, _name in ipairs(ctld.transportPilotNames) do - - local _transUnit = ctld.getTransportUnit(_name) - - if _transUnit == nil then - --env.info("CTLD Transport Unit Dead event") - ctld.inTransitTroops[_name] = nil - ctld.inTransitFOBCrates[_name] = nil - ctld.inTransitSlingLoadCrates[_name] = nil - end + if _transUnit == nil then + --env.info("CTLD Transport Unit Dead event") + ctld.inTransitTroops[_name] = nil + ctld.inTransitFOBCrates[_name] = nil + ctld.inTransitSlingLoadCrates[_name] = nil end + end end function ctld.adaptWeightToCargo(unitName) - local _weight = ctld.getWeightOfCargo(unitName) - trigger.action.setUnitInternalCargo(unitName, _weight) + local _weight = ctld.getWeightOfCargo(unitName) + trigger.action.setUnitInternalCargo(unitName, _weight) end function ctld.getWeightOfCargo(unitName) + local FOB_CRATE_WEIGHT = 800 + local _weight = 0 + local _description = "" - local FOB_CRATE_WEIGHT = 800 - local _weight = 0 - local _description = "" + ctld.inTransitSlingLoadCrates[unitName] = ctld.inTransitSlingLoadCrates[unitName] or {} - ctld.inTransitSlingLoadCrates[unitName] = ctld.inTransitSlingLoadCrates[unitName] or {} - - -- add troops weight - if ctld.inTransitTroops[unitName] then - local _inTransit = ctld.inTransitTroops[unitName] - if _inTransit then - local _troops = _inTransit.troops - if _troops and _troops.units then - _description = _description .. ctld.i18n_translate("%1 troops onboard (%2 kg)\n", #_troops.units, _troops.weight) - _weight = _weight + _troops.weight - end - local _vehicles = _inTransit.vehicles - if _vehicles and _vehicles.units then - for _, _unit in pairs(_vehicles.units) do - _weight = _weight + _unit.weight - end - _description = _description .. ctld.i18n_translate("%1 vehicles onboard (%2)\n", #_vehicles.units, _weight) - end + -- add troops weight + if ctld.inTransitTroops[unitName] then + local _inTransit = ctld.inTransitTroops[unitName] + if _inTransit then + local _troops = _inTransit.troops + if _troops and _troops.units then + _description = _description .. + ctld.i18n_translate("%1 troops onboard (%2 kg)\n", #_troops.units, _troops.weight) + _weight = _weight + _troops.weight + end + local _vehicles = _inTransit.vehicles + if _vehicles and _vehicles.units then + for _, _unit in pairs(_vehicles.units) do + _weight = _weight + _unit.weight end + _description = _description .. + ctld.i18n_translate("%1 vehicles onboard (%2)\n", #_vehicles.units, _weight) + end end + end - -- add FOB crates weight - if ctld.inTransitFOBCrates[unitName] then - _weight = _weight + FOB_CRATE_WEIGHT - _description = _description .. ctld.i18n_translate("1 FOB Crate oboard (%1 kg)\n", FOB_CRATE_WEIGHT) - end + -- add FOB crates weight + if ctld.inTransitFOBCrates[unitName] then + _weight = _weight + FOB_CRATE_WEIGHT + _description = _description .. ctld.i18n_translate("1 FOB Crate oboard (%1 kg)\n", FOB_CRATE_WEIGHT) + end - -- add simulated slingload crates weight - for i = 1, #ctld.inTransitSlingLoadCrates[unitName] do - local _crate = ctld.inTransitSlingLoadCrates[unitName][i] - if _crate and _crate.simulatedSlingload then - _weight = _weight + _crate.weight - _description = _description .. ctld.i18n_translate("%1 crate onboard (%2 kg)\n", _crate.desc, _crate.weight) - end - end - if _description ~= "" then - _description = _description .. ctld.i18n_translate("Total weight of cargo : %1 kg\n", _weight) - else - _description = ctld.i18n_translate("No cargo.") + -- add simulated slingload crates weight + for i = 1, #ctld.inTransitSlingLoadCrates[unitName] do + local _crate = ctld.inTransitSlingLoadCrates[unitName][i] + if _crate and _crate.simulatedSlingload then + _weight = _weight + _crate.weight + _description = _description .. ctld.i18n_translate("%1 crate onboard (%2 kg)\n", _crate.desc, _crate.weight) end + end + if _description ~= "" then + _description = _description .. ctld.i18n_translate("Total weight of cargo : %1 kg\n", _weight) + else + _description = ctld.i18n_translate("No cargo.") + end - return _weight, _description + return _weight, _description end function ctld.checkHoverStatus() - timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1.0) + timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1.0) - local _status, _result = pcall(function() + local _status, _result = pcall(function() + for _, _name in ipairs(ctld.transportPilotNames) do + local _reset = true + local _transUnit = ctld.getTransportUnit(_name) + local _transUnitTypeName = _transUnit and _transUnit:getTypeName() + local _cargoCapacity = ctld.internalCargoLimits[_transUnitTypeName] or 1 + ctld.inTransitSlingLoadCrates[_name] = ctld.inTransitSlingLoadCrates[_name] or {} - for _, _name in ipairs(ctld.transportPilotNames) do + --only check transports that are hovering and not planes + if _transUnit ~= nil and #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity and ctld.inAir(_transUnit) and ctld.unitCanCarryVehicles(_transUnit) == false and not ctld.unitDynamicCargoCapable(_transUnit) then + local _crates = ctld.getCratesAndDistance(_transUnit) - local _reset = true - local _transUnit = ctld.getTransportUnit(_name) - local _transUnitTypeName = _transUnit and _transUnit:getTypeName() - local _cargoCapacity = ctld.internalCargoLimits[_transUnitTypeName] or 1 - ctld.inTransitSlingLoadCrates[_name] = ctld.inTransitSlingLoadCrates[_name] or {} + for _, _crate in pairs(_crates) do + local _crateUnitName = _crate.crateUnit:getName() + if _crate.dist < ctld.maxDistanceFromCrate and _crate.details.unit ~= "FOB" then + --check height! + local _height = _transUnit:getPoint().y - _crate.crateUnit:getPoint().y + if _height > ctld.minimumHoverHeight and _height <= ctld.maximumHoverHeight then + local _time = ctld.hoverStatus[_name] - --only check transports that are hovering and not planes - if _transUnit ~= nil and #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity and ctld.inAir(_transUnit) and ctld.unitCanCarryVehicles(_transUnit) == false and not ctld.unitDynamicCargoCapable(_transUnit) then + if _time == nil then + ctld.hoverStatus[_name] = ctld.hoverTime + _time = ctld.hoverTime + else + _time = ctld.hoverStatus[_name] - 1 + ctld.hoverStatus[_name] = _time + end - - local _crates = ctld.getCratesAndDistance(_transUnit) - - for _, _crate in pairs(_crates) do - local _crateUnitName = _crate.crateUnit:getName() - if _crate.dist < ctld.maxDistanceFromCrate and _crate.details.unit ~= "FOB" then - - --check height! - local _height = _transUnit:getPoint().y - _crate.crateUnit:getPoint().y - if _height > ctld.minimumHoverHeight and _height <= ctld.maximumHoverHeight then - - local _time = ctld.hoverStatus[_name] - - if _time == nil then - ctld.hoverStatus[_name] = ctld.hoverTime - _time = ctld.hoverTime - else - _time = ctld.hoverStatus[_name] - 1 - ctld.hoverStatus[_name] = _time - end - - if _time > 0 then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!", _crate.details.desc, _time), 10,true) - else - ctld.hoverStatus[_name] = nil - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10,true) - - --crates been moved once! - ctld.crateMove[_crateUnitName] = nil - - if _transUnit:getCoalition() == 1 then - ctld.spawnedCratesRED[_crateUnitName] = nil - else - ctld.spawnedCratesBLUE[_crateUnitName] = nil - end - - _crate.crateUnit:destroy() - - local _copiedCrate = mist.utils.deepCopy(_crate.details) - _copiedCrate.simulatedSlingload = true - table.insert(ctld.inTransitSlingLoadCrates[_name], _copiedCrate) - ctld.adaptWeightToCargo(_name) - end - - _reset = false - - break - elseif _height <= ctld.minimumHoverHeight then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Too low to hook %1 crate.\n\nHold hover for %2 seconds", _crate.details.desc, ctld.hoverTime), 5,true) - break - else - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Too high to hook %1 crate.\n\nHold hover for %2 seconds", _crate.details.desc, ctld.hoverTime), 5,true) - break - end - end - end - end - - if _reset then + if _time > 0 then + ctld.displayMessageToGroup(_transUnit, + ctld.i18n_translate( + "Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!", + _crate.details.desc, _time), 10, true) + else ctld.hoverStatus[_name] = nil - end - end - end) + ctld.displayMessageToGroup(_transUnit, + ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10, true) - if (not _status) then - env.error(string.format("CTLD ERROR: %s", _result)) + --crates been moved once! + ctld.crateMove[_crateUnitName] = nil + + if _transUnit:getCoalition() == 1 then + ctld.spawnedCratesRED[_crateUnitName] = nil + else + ctld.spawnedCratesBLUE[_crateUnitName] = nil + end + + _crate.crateUnit:destroy() + + local _copiedCrate = mist.utils.deepCopy(_crate.details) + _copiedCrate.simulatedSlingload = true + table.insert(ctld.inTransitSlingLoadCrates[_name], _copiedCrate) + ctld.adaptWeightToCargo(_name) + end + + _reset = false + + break + elseif _height <= ctld.minimumHoverHeight then + ctld.displayMessageToGroup(_transUnit, + ctld.i18n_translate("Too low to hook %1 crate.\n\nHold hover for %2 seconds", + _crate.details.desc, ctld.hoverTime), 5, true) + break + else + ctld.displayMessageToGroup(_transUnit, + ctld.i18n_translate("Too high to hook %1 crate.\n\nHold hover for %2 seconds", + _crate.details.desc, ctld.hoverTime), 5, true) + break + end + end + end + end + + if _reset then + ctld.hoverStatus[_name] = nil + end end + end) + + if (not _status) then + env.error(string.format("CTLD ERROR: %s", _result)) + end end function ctld.loadNearbyCrate(_name) - local _transUnit = ctld.getTransportUnit(_name) + local _transUnit = ctld.getTransportUnit(_name) - if _transUnit ~= nil then + if _transUnit ~= nil then + local _cargoCapacity = ctld.internalCargoLimits[_transUnit:getTypeName()] or 1 + ctld.inTransitSlingLoadCrates[_name] = ctld.inTransitSlingLoadCrates[_name] or {} - local _cargoCapacity = ctld.internalCargoLimits[_transUnit:getTypeName()] or 1 - ctld.inTransitSlingLoadCrates[_name] = ctld.inTransitSlingLoadCrates[_name] or {} - - if ctld.inAir(_transUnit) then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("You must land before you can load a crate!"), 10,true) - return - end - - local _crates = ctld.getCratesAndDistance(_transUnit) - local loaded = false - for _, _crate in pairs(_crates) do - - if _crate.dist < 50.0 then - if #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), 10) - - if _transUnit:getCoalition() == 1 then - ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil - else - ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil - end - - ctld.crateMove[_crate.crateUnit:getName()] = nil - - _crate.crateUnit:destroy() - - local _copiedCrate = mist.utils.deepCopy(_crate.details) - _copiedCrate.simulatedSlingload = true - table.insert(ctld.inTransitSlingLoadCrates[_name], _copiedCrate) - loaded = true - ctld.adaptWeightToCargo(_name) - else - -- Max crates onboard - local outputMsg = ctld.i18n_translate("Maximum number of crates are on board!") - for i = 1, _cargoCapacity do - outputMsg = outputMsg .. "\n" .. ctld.inTransitSlingLoadCrates[_name][i].desc - end - ctld.displayMessageToGroup(_transUnit, outputMsg, 10,true) - return - end - end - end - if not loaded then - ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("No Crates within 50m to load!"), 10,true) - end + if ctld.inAir(_transUnit) then + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("You must land before you can load a crate!"), 10, + true) + return end + + local _crates = ctld.getCratesAndDistance(_transUnit) + local loaded = false + for _, _crate in pairs(_crates) do + if _crate.dist < 50.0 then + if #ctld.inTransitSlingLoadCrates[_name] < _cargoCapacity then + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("Loaded %1 crate!", _crate.details.desc), + 10) + + if _transUnit:getCoalition() == 1 then + ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil + else + ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil + end + + ctld.crateMove[_crate.crateUnit:getName()] = nil + + _crate.crateUnit:destroy() + + local _copiedCrate = mist.utils.deepCopy(_crate.details) + _copiedCrate.simulatedSlingload = true + table.insert(ctld.inTransitSlingLoadCrates[_name], _copiedCrate) + loaded = true + ctld.adaptWeightToCargo(_name) + else + -- Max crates onboard + local outputMsg = ctld.i18n_translate("Maximum number of crates are on board!") + for i = 1, _cargoCapacity do + outputMsg = outputMsg .. "\n" .. ctld.inTransitSlingLoadCrates[_name][i].desc + end + ctld.displayMessageToGroup(_transUnit, outputMsg, 10, true) + return + end + end + end + if not loaded then + ctld.displayMessageToGroup(_transUnit, ctld.i18n_translate("No Crates within 50m to load!"), 10, true) + end + end end --check each minute if the beacons' batteries have failed, and stop them accordingly --there's no more need to actually refresh the beacons, since we set "loop" to true. function ctld.refreshRadioBeacons() - - timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 60) + timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 60) - for _index, _beaconDetails in ipairs(ctld.deployedRadioBeacons) do + for _index, _beaconDetails in ipairs(ctld.deployedRadioBeacons) do + if ctld.updateRadioBeacon(_beaconDetails) == false then + --search used frequencies + remove, add back to unused - if ctld.updateRadioBeacon(_beaconDetails) == false then - - --search used frequencies + remove, add back to unused - - for _i, _freq in ipairs(ctld.usedUHFFrequencies) do - if _freq == _beaconDetails.uhf then - - table.insert(ctld.freeUHFFrequencies, _freq) - table.remove(ctld.usedUHFFrequencies, _i) - end - end - - for _i, _freq in ipairs(ctld.usedVHFFrequencies) do - if _freq == _beaconDetails.vhf then - - table.insert(ctld.freeVHFFrequencies, _freq) - table.remove(ctld.usedVHFFrequencies, _i) - end - end - - for _i, _freq in ipairs(ctld.usedFMFrequencies) do - if _freq == _beaconDetails.fm then - - table.insert(ctld.freeFMFrequencies, _freq) - table.remove(ctld.usedFMFrequencies, _i) - end - end - - --clean up beacon table - table.remove(ctld.deployedRadioBeacons, _index) + for _i, _freq in ipairs(ctld.usedUHFFrequencies) do + if _freq == _beaconDetails.uhf then + table.insert(ctld.freeUHFFrequencies, _freq) + table.remove(ctld.usedUHFFrequencies, _i) end + end + + for _i, _freq in ipairs(ctld.usedVHFFrequencies) do + if _freq == _beaconDetails.vhf then + table.insert(ctld.freeVHFFrequencies, _freq) + table.remove(ctld.usedVHFFrequencies, _i) + end + end + + for _i, _freq in ipairs(ctld.usedFMFrequencies) do + if _freq == _beaconDetails.fm then + table.insert(ctld.freeFMFrequencies, _freq) + table.remove(ctld.usedFMFrequencies, _i) + end + end + + --clean up beacon table + table.remove(ctld.deployedRadioBeacons, _index) end + end end function ctld.getClockDirection(_heli, _crate) + -- Source: Helicopter Script - Thanks! - -- Source: Helicopter Script - Thanks! + local _position = _crate:getPosition().p -- get position of crate + local _playerPosition = _heli:getPosition().p -- get position of helicopter + local _relativePosition = mist.vec.sub(_position, _playerPosition) - local _position = _crate:getPosition().p -- get position of crate - local _playerPosition = _heli:getPosition().p -- get position of helicopter - local _relativePosition = mist.vec.sub(_position, _playerPosition) + local _playerHeading = mist.getHeading(_heli) -- the rest of the code determines the 'o'clock' bearing of the missile relative to the helicopter - local _playerHeading = mist.getHeading(_heli) -- the rest of the code determines the 'o'clock' bearing of the missile relative to the helicopter + local _headingVector = { x = math.cos(_playerHeading), y = 0, z = math.sin(_playerHeading) } - local _headingVector = { x = math.cos(_playerHeading), y = 0, z = math.sin(_playerHeading) } + local _headingVectorPerpendicular = { x = math.cos(_playerHeading + math.pi / 2), y = 0, z = math.sin(_playerHeading + + math.pi / 2) } - local _headingVectorPerpendicular = { x = math.cos(_playerHeading + math.pi / 2), y = 0, z = math.sin(_playerHeading + math.pi / 2) } + local _forwardDistance = mist.vec.dp(_relativePosition, _headingVector) - local _forwardDistance = mist.vec.dp(_relativePosition, _headingVector) + local _rightDistance = mist.vec.dp(_relativePosition, _headingVectorPerpendicular) - local _rightDistance = mist.vec.dp(_relativePosition, _headingVectorPerpendicular) + local _angle = math.atan2(_rightDistance, _forwardDistance) * 180 / math.pi - local _angle = math.atan2(_rightDistance, _forwardDistance) * 180 / math.pi + if _angle < 0 then + _angle = 360 + _angle + end + _angle = math.floor(_angle * 12 / 360 + 0.5) + if _angle == 0 then + _angle = 12 + end - if _angle < 0 then - _angle = 360 + _angle - end - _angle = math.floor(_angle * 12 / 360 + 0.5) - if _angle == 0 then - _angle = 12 - end - - return _angle + return _angle end - function ctld.getCompassBearing(_ref, _unitPos) + _ref = mist.utils.makeVec3(_ref, 0) -- turn it into Vec3 if it is not already. + _unitPos = mist.utils.makeVec3(_unitPos, 0) -- turn it into Vec3 if it is not already. - _ref = mist.utils.makeVec3(_ref, 0) -- turn it into Vec3 if it is not already. - _unitPos = mist.utils.makeVec3(_unitPos, 0) -- turn it into Vec3 if it is not already. + local _vec = { x = _unitPos.x - _ref.x, y = _unitPos.y - _ref.y, z = _unitPos.z - _ref.z } - local _vec = { x = _unitPos.x - _ref.x, y = _unitPos.y - _ref.y, z = _unitPos.z - _ref.z } + local _dir = mist.utils.getDir(_vec, _ref) - local _dir = mist.utils.getDir(_vec, _ref) + local _bearing = mist.utils.round(mist.utils.toDegree(_dir), 0) - local _bearing = mist.utils.round(mist.utils.toDegree(_dir), 0) - - return _bearing + return _bearing end function ctld.listNearbyCrates(_args) + local _message = "" - local _message = "" + local _heli = ctld.getTransportUnit(_args[1]) - local _heli = ctld.getTransportUnit(_args[1]) + if _heli == nil then + return -- no heli! + end - if _heli == nil then + local _crates = ctld.getCratesAndDistance(_heli) - return -- no heli! + --sort + local _sort = function(a, b) return a.dist < b.dist end + table.sort(_crates, _sort) + + for _, _crate in pairs(_crates) do + if _crate.dist < 1000 and _crate.details.unit ~= "FOB" then + _message = ctld.i18n_translate("%1\n%2 crate - kg %3 - %4 m - %5 o'clock", _message, _crate.details.desc, + _crate.details.weight, _crate.dist, ctld.getClockDirection(_heli, _crate.crateUnit)) + end + end + + + local _fobMsg = "" + for _, _fobCrate in pairs(_crates) do + if _fobCrate.dist < 1000 and _fobCrate.details.unit == "FOB" then + _fobMsg = _fobMsg .. + ctld.i18n_translate("FOB Crate - %1 m - %2 o'clock\n", _fobCrate.dist, + ctld.getClockDirection(_heli, _fobCrate.crateUnit)) + end + end + + local _txt = ctld.i18n_translate("No Nearby Crates") + if _message ~= "" or _fobMsg ~= "" then + _txt = "" + + if _message ~= "" then + _txt = ctld.i18n_translate("Nearby Crates:\n%1", _message) end - local _crates = ctld.getCratesAndDistance(_heli) + if _fobMsg ~= "" then + if _txt ~= "" then + _txt = _txt .. "\n\n" + end - --sort - local _sort = function( a,b ) return a.dist < b.dist end - table.sort(_crates,_sort) - - for _, _crate in pairs(_crates) do - - if _crate.dist < 1000 and _crate.details.unit ~= "FOB" then - _message = ctld.i18n_translate("%1\n%2 crate - kg %3 - %4 m - %5 o'clock", _message, _crate.details.desc, _crate.details.weight, _crate.dist, ctld.getClockDirection(_heli, _crate.crateUnit)) - end + _txt = _txt .. ctld.i18n_translate("Nearby FOB Crates (Not Slingloadable):\n%1", _fobMsg) end - - - local _fobMsg = "" - for _, _fobCrate in pairs(_crates) do - - if _fobCrate.dist < 1000 and _fobCrate.details.unit == "FOB" then - _fobMsg = _fobMsg .. ctld.i18n_translate("FOB Crate - %1 m - %2 o'clock\n", _fobCrate.dist, ctld.getClockDirection(_heli, _fobCrate.crateUnit)) - end - end - - local _txt = ctld.i18n_translate("No Nearby Crates") - if _message ~= "" or _fobMsg ~= "" then - - _txt = "" - - if _message ~= "" then - _txt = ctld.i18n_translate("Nearby Crates:\n%1", _message) - end - - if _fobMsg ~= "" then - - if _txt ~= "" then - _txt = _txt .. "\n\n" - end - - _txt = _txt .. ctld.i18n_translate("Nearby FOB Crates (Not Slingloadable):\n%1", _fobMsg) - end - end - ctld.displayMessageToGroup(_heli, _txt, 20) + end + ctld.displayMessageToGroup(_heli, _txt, 20) end - function ctld.listFOBS(_args) + local _msg = ctld.i18n_translate("FOB Positions:") - local _msg = ctld.i18n_translate("FOB Positions:") + local _heli = ctld.getTransportUnit(_args[1]) - local _heli = ctld.getTransportUnit(_args[1]) + if _heli == nil then + return -- no heli! + end - if _heli == nil then + -- get fob positions + local _fobs = ctld.getSpawnedFobs(_heli) - return -- no heli! + if _fobs and #_fobs > 0 then + -- now check spawned fobs + for _, _fob in ipairs(_fobs) do + _msg = ctld.i18n_translate("%1\nFOB @ %2", _msg, ctld.getFOBPositionString(_fob)) end - - -- get fob positions - local _fobs = ctld.getSpawnedFobs(_heli) - - if _fobs and #_fobs > 0 then - -- now check spawned fobs - for _, _fob in ipairs(_fobs) do - _msg = ctld.i18n_translate("%1\nFOB @ %2", _msg, ctld.getFOBPositionString(_fob)) - end - else - _msg = ctld.i18n_translate("Sorry, there are no active FOBs!") - end - ctld.displayMessageToGroup(_heli, _msg, 20) + else + _msg = ctld.i18n_translate("Sorry, there are no active FOBs!") + end + ctld.displayMessageToGroup(_heli, _msg, 20) end function ctld.getFOBPositionString(_fob) + local _lat, _lon = coord.LOtoLL(_fob:getPosition().p) - local _lat, _lon = coord.LOtoLL(_fob:getPosition().p) + local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) - local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) + -- local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_fob:getPosition().p)), 5) - -- local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_fob:getPosition().p)), 5) + local _message = _latLngStr - local _message = _latLngStr + local _beaconInfo = ctld.fobBeacons[_fob:getName()] - local _beaconInfo = ctld.fobBeacons[_fob:getName()] + if _beaconInfo ~= nil then + _message = string.format("%s - %.2f KHz ", _message, _beaconInfo.vhf / 1000) + _message = string.format("%s - %.2f MHz ", _message, _beaconInfo.uhf / 1000000) + _message = string.format("%s - %.2f MHz ", _message, _beaconInfo.fm / 1000000) + end - if _beaconInfo ~= nil then - _message = string.format("%s - %.2f KHz ", _message, _beaconInfo.vhf / 1000) - _message = string.format("%s - %.2f MHz ", _message, _beaconInfo.uhf / 1000000) - _message = string.format("%s - %.2f MHz ", _message, _beaconInfo.fm / 1000000) - end - - return _message + return _message end - -function ctld.displayMessageToGroup(_unit, _text, _time,_clear) - - local _groupId = ctld.getGroupId(_unit) - if _groupId then - if _clear == true then - trigger.action.outTextForGroup(_groupId, _text, _time,_clear) - else - trigger.action.outTextForGroup(_groupId, _text, _time) - end +function ctld.displayMessageToGroup(_unit, _text, _time, _clear) + local _groupId = ctld.getGroupId(_unit) + if _groupId then + if _clear == true then + trigger.action.outTextForGroup(_groupId, _text, _time, _clear) + else + trigger.action.outTextForGroup(_groupId, _text, _time) end + end end function ctld.heightDiff(_unit) + local _point = _unit:getPoint() - local _point = _unit:getPoint() + -- env.info("heightunit " .. _point.y) + --env.info("heightland " .. land.getHeight({ x = _point.x, y = _point.z })) - -- env.info("heightunit " .. _point.y) - --env.info("heightland " .. land.getHeight({ x = _point.x, y = _point.z })) - - return _point.y - land.getHeight({ x = _point.x, y = _point.z }) + return _point.y - land.getHeight({ x = _point.x, y = _point.z }) end --includes fob crates! function ctld.getCratesAndDistance(_heli) + local _crates = {} - local _crates = {} + local _allCrates + if _heli:getCoalition() == 1 then + _allCrates = ctld.spawnedCratesRED + else + _allCrates = ctld.spawnedCratesBLUE + end - local _allCrates - if _heli:getCoalition() == 1 then - _allCrates = ctld.spawnedCratesRED - else - _allCrates = ctld.spawnedCratesBLUE + for _crateName, _details in pairs(_allCrates) do + --get crate + local _crate = ctld.getCrateObject(_crateName) + + --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude + if _crate ~= nil and _crate:getLife() > 0 + and (ctld.inAir(_crate) == false) then + local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) + + local _crateDetails = { crateUnit = _crate, dist = _dist, details = _details } + + table.insert(_crates, _crateDetails) end + end - for _crateName, _details in pairs(_allCrates) do + local _fobCrates + if _heli:getCoalition() == 1 then + _fobCrates = ctld.droppedFOBCratesRED + else + _fobCrates = ctld.droppedFOBCratesBLUE + end - --get crate - local _crate = ctld.getCrateObject(_crateName) + for _crateName, _details in pairs(_fobCrates) do + --get crate + local _crate = ctld.getCrateObject(_crateName) - --in air seems buggy with crates so if in air is true, get the height above ground and the speed magnitude - if _crate ~= nil and _crate:getLife() > 0 - and (ctld.inAir(_crate) == false) then + if _crate ~= nil and _crate:getLife() > 0 then + local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) - local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) + local _crateDetails = { crateUnit = _crate, dist = _dist, details = { unit = "FOB" }, } - local _crateDetails = { crateUnit = _crate, dist = _dist, details = _details } - - table.insert(_crates, _crateDetails) - end + table.insert(_crates, _crateDetails) end + end - local _fobCrates - if _heli:getCoalition() == 1 then - _fobCrates = ctld.droppedFOBCratesRED - else - _fobCrates = ctld.droppedFOBCratesBLUE - end - - for _crateName, _details in pairs(_fobCrates) do - - --get crate - local _crate = ctld.getCrateObject(_crateName) - - if _crate ~= nil and _crate:getLife() > 0 then - - local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) - - local _crateDetails = { crateUnit = _crate, dist = _dist, details = { unit = "FOB" }, } - - table.insert(_crates, _crateDetails) - end - end - - return _crates + return _crates end - function ctld.getClosestCrate(_heli, _crates, _type) + local _closetCrate = nil + local _shortestDistance = -1 + local _distance = 0 + local _minimumDistance = 5 -- prevents dynamic cargo crates from unpacking while in cargo hold - local _closetCrate = nil - local _shortestDistance = -1 - local _distance = 0 - local _minimumDistance = 5 -- prevents dynamic cargo crates from unpacking while in cargo hold + for _, _crate in pairs(_crates) do + if (_crate.details.unit == _type or _type == nil) then + _distance = _crate.dist - for _, _crate in pairs(_crates) do - - if (_crate.details.unit == _type or _type == nil) then - _distance = _crate.dist - - if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) and _distance > _minimumDistance then - _shortestDistance = _distance - _closetCrate = _crate - end - end + if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) and _distance > _minimumDistance then + _shortestDistance = _distance + _closetCrate = _crate + end end + end - return _closetCrate + return _closetCrate end -function ctld.findNearestAASystem(_heli,_aaSystem) +function ctld.findNearestAASystem(_heli, _aaSystem) + local _closestHawkGroup = nil + local _shortestDistance = -1 + local _distance = 0 - local _closestHawkGroup = nil - local _shortestDistance = -1 - local _distance = 0 + for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do + local _hawkGroup = Group.getByName(_groupName) - for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do + -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) + if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() and _hawkDetails[1].system.name == _aaSystem.name then + local _units = _hawkGroup:getUnits() - local _hawkGroup = Group.getByName(_groupName) + for _, _leader in pairs(_units) do + if _leader ~= nil and _leader:getLife() > 0 then + _distance = ctld.getDistance(_leader:getPoint(), _heli:getPoint()) - -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) - if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() and _hawkDetails[1].system.name == _aaSystem.name then + if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then + _shortestDistance = _distance + _closestHawkGroup = _hawkGroup + end - local _units = _hawkGroup:getUnits() - - for _, _leader in pairs(_units) do - - if _leader ~= nil and _leader:getLife() > 0 then - - _distance = ctld.getDistance(_leader:getPoint(), _heli:getPoint()) - - if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then - _shortestDistance = _distance - _closestHawkGroup = _hawkGroup - end - - break - end - end + break end + end end + end - if _closestHawkGroup ~= nil then - - return { group = _closestHawkGroup, dist = _shortestDistance } - end - return nil + if _closestHawkGroup ~= nil then + return { group = _closestHawkGroup, dist = _shortestDistance } + end + return nil end function ctld.getCrateObject(_name) - local _crate + local _crate - if ctld.staticBugWorkaround then - _crate = Unit.getByName(_name) - else - _crate = StaticObject.getByName(_name) - end - return _crate + if ctld.staticBugWorkaround then + _crate = Unit.getByName(_name) + else + _crate = StaticObject.getByName(_name) + end + return _crate end - - function ctld.unpackCrates(_arguments) + local _status, _err = pcall(function(_args) + local _heli = ctld.getTransportUnit(_args[1]) - local _status, _err = pcall(function(_args) - - local _heli = ctld.getTransportUnit(_args[1]) - - if _heli ~= nil and ctld.inAir(_heli) == false then - - local _crates = ctld.getCratesAndDistance(_heli) - local _crate = ctld.getClosestCrate(_heli, _crates) + if _heli ~= nil and ctld.inAir(_heli) == false then + local _crates = ctld.getCratesAndDistance(_heli) + local _crate = ctld.getClosestCrate(_heli, _crates) - 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.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) - - return - end + return + end - if _crate ~= nil and _crate.dist < 750 - and (_crate.details.unit == "FOB" or _crate.details.unit == "FOB-SMALL") then + if _crate ~= nil and _crate.dist < 750 + and (_crate.details.unit == "FOB" or _crate.details.unit == "FOB-SMALL") then + ctld.unpackFOBCrates(_crates, _heli) - ctld.unpackFOBCrates(_crates, _heli) - - return - - elseif _crate ~= nil and _crate.dist < 200 then - - if ctld.forceCrateToBeMoved and ctld.crateMove[_crate.crateUnit:getName()] and not ctld.unitDynamicCargoCapable(_heli) then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Sorry you must move this crate before you unpack it!"), 20) - return - end - - - local _aaTemplate = ctld.getAATemplate(_crate.details.unit) - - if _aaTemplate then - - if _crate.details.unit == _aaTemplate.repair then - ctld.repairAASystem(_heli, _crate,_aaTemplate) - else - ctld.unpackAASystem(_heli, _crate, _crates,_aaTemplate) - end - - return -- stop processing - -- is multi crate? - elseif _crate.details.cratesRequired ~= nil and _crate.details.cratesRequired > 1 then - -- multicrate - - ctld.unpackMultiCrate(_heli, _crate, _crates) - - return - - else - -- single crate - local _cratePoint = _crate.crateUnit:getPoint() - local _crateName = _crate.crateUnit:getName() - local _crateHdg = mist.getHeading(_crate.crateUnit, true) - - --remove crate - -- if ctld.slingLoad == false then - _crate.crateUnit:destroy() - -- end - - local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }, { _crateHdg }) - - if _heli:getCoalition() == 1 then - ctld.spawnedCratesRED[_crateName] = nil - else - ctld.spawnedCratesBLUE[_crateName] = nil - end - - ctld.processCallback({unit = _heli, crate = _crate , spawnedGroup = _spawnedGroups, action = "unpack"}) - - if _crate.details.unit == "1L13 EWR" then - ctld.addEWRTask(_spawnedGroups) - - -- env.info("Added EWR") - end - - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed %2 to the field", ctld.getPlayerNameOrType(_heli), _crate.details.desc), 10) - - if ctld.isJTACUnitType(_crate.details.unit) and ctld.JTAC_dropEnabled then - - local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) - --put to the end - table.insert(ctld.jtacGeneratedLaserCodes, _code) - - ctld.JTACStart(_spawnedGroups:getName(), _code) --(_jtacGroupName, _laserCode, _smoke, _lock, _colour) - end - end - - else - - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No friendly crates close enough to unpack, or crate too close to aircraft."), 20) - end + return + elseif _crate ~= nil and _crate.dist < 200 then + if ctld.forceCrateToBeMoved and ctld.crateMove[_crate.crateUnit:getName()] and not ctld.unitDynamicCargoCapable(_heli) then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Sorry you must move this crate before you unpack it!"), 20) + return end - end, _arguments) - if (not _status) then - env.error(string.format("CTLD ERROR: %s", _err)) + + local _aaTemplate = ctld.getAATemplate(_crate.details.unit) + + if _aaTemplate then + if _crate.details.unit == _aaTemplate.repair then + ctld.repairAASystem(_heli, _crate, _aaTemplate) + else + ctld.unpackAASystem(_heli, _crate, _crates, _aaTemplate) + end + + return -- stop processing + -- is multi crate? + elseif _crate.details.cratesRequired ~= nil and _crate.details.cratesRequired > 1 then + -- multicrate + + ctld.unpackMultiCrate(_heli, _crate, _crates) + + return + else + -- single crate + local _cratePoint = _crate.crateUnit:getPoint() + local _crateName = _crate.crateUnit:getName() + local _crateHdg = mist.getHeading(_crate.crateUnit, true) + + --remove crate + -- if ctld.slingLoad == false then + _crate.crateUnit:destroy() + -- end + + local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }, + { _crateHdg }) + + if _heli:getCoalition() == 1 then + ctld.spawnedCratesRED[_crateName] = nil + else + ctld.spawnedCratesBLUE[_crateName] = nil + end + + ctld.processCallback({ unit = _heli, crate = _crate, spawnedGroup = _spawnedGroups, action = "unpack" }) + + if _crate.details.unit == "1L13 EWR" then + ctld.addEWRTask(_spawnedGroups) + + -- env.info("Added EWR") + end + + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 successfully deployed %2 to the field", ctld.getPlayerNameOrType(_heli), + _crate.details.desc), 10) + + if ctld.isJTACUnitType(_crate.details.unit) and ctld.JTAC_dropEnabled then + local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) + --put to the end + table.insert(ctld.jtacGeneratedLaserCodes, _code) + + ctld.JTACStart(_spawnedGroups:getName(), _code) --(_jtacGroupName, _laserCode, _smoke, _lock, _colour) + end + end + else + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("No friendly crates close enough to unpack, or crate too close to aircraft."), 20) + end end -end + end, _arguments) + if (not _status) then + env.error(string.format("CTLD ERROR: %s", _err)) + end +end -- builds a fob! function ctld.unpackFOBCrates(_crates, _heli) + if ctld.inLogisticsZone(_heli) == true then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) - if ctld.inLogisticsZone(_heli) == true then + return + end - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) + -- unpack multi crate + local _nearbyMultiCrates = {} - return + local _bigFobCrates = 0 + local _smallFobCrates = 0 + local _totalCrates = 0 + + for _, _nearbyCrate in pairs(_crates) do + if _nearbyCrate.dist < 750 then + if _nearbyCrate.details.unit == "FOB" then + _bigFobCrates = _bigFobCrates + 1 + table.insert(_nearbyMultiCrates, _nearbyCrate) + elseif _nearbyCrate.details.unit == "FOB-SMALL" then + _smallFobCrates = _smallFobCrates + 1 + table.insert(_nearbyMultiCrates, _nearbyCrate) + end + + --catch divide by 0 + if _smallFobCrates > 0 then + _totalCrates = _bigFobCrates + (_smallFobCrates / 3.0) + else + _totalCrates = _bigFobCrates + end + + if _totalCrates >= ctld.cratesRequiredForFOB then + break + end + end + end + + --- check crate count + if _totalCrates >= ctld.cratesRequiredForFOB then + -- destroy crates + + local _points = {} + + for _, _crate in pairs(_nearbyMultiCrates) do + if _heli:getCoalition() == 1 then + ctld.droppedFOBCratesRED[_crate.crateUnit:getName()] = nil + ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil + else + ctld.droppedFOBCratesBLUE[_crate.crateUnit:getName()] = nil + ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil + end + + table.insert(_points, _crate.crateUnit:getPoint()) + + --destroy + _crate.crateUnit:destroy() end - -- unpack multi crate - local _nearbyMultiCrates = {} + local _centroid = ctld.getCentroid(_points) - local _bigFobCrates = 0 - local _smallFobCrates = 0 - local _totalCrates = 0 + timer.scheduleFunction(function(_args) + local _unitId = ctld.getNextUnitId() + local _name = "Deployed FOB #" .. _unitId - for _, _nearbyCrate in pairs(_crates) do + local _fob = ctld.spawnFOB(_args[2], _unitId, _args[1], _name) - if _nearbyCrate.dist < 750 then + --make it able to deploy crates + table.insert(ctld.logisticUnits, _fob:getName()) - if _nearbyCrate.details.unit == "FOB" then - _bigFobCrates = _bigFobCrates + 1 - table.insert(_nearbyMultiCrates, _nearbyCrate) - elseif _nearbyCrate.details.unit == "FOB-SMALL" then - _smallFobCrates = _smallFobCrates + 1 - table.insert(_nearbyMultiCrates, _nearbyCrate) - end + ctld.beaconCount = ctld.beaconCount + 1 - --catch divide by 0 - if _smallFobCrates > 0 then - _totalCrates = _bigFobCrates + (_smallFobCrates/3.0) - else - _totalCrates = _bigFobCrates - end + local _radioBeaconName = "FOB Beacon #" .. ctld.beaconCount - if _totalCrates >= ctld.cratesRequiredForFOB then - break - end - end - end + local _radioBeaconDetails = ctld.createRadioBeacon(_args[1], _args[3], _args[2], _radioBeaconName, nil, true) - --- check crate count - if _totalCrates >= ctld.cratesRequiredForFOB then + ctld.fobBeacons[_name] = { vhf = _radioBeaconDetails.vhf, uhf = _radioBeaconDetails.uhf, fm = + _radioBeaconDetails.fm } - -- destroy crates + if ctld.troopPickupAtFOB == true then + table.insert(ctld.builtFOBS, _fob:getName()) - local _points = {} + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("Finished building FOB! Crates and Troops can now be picked up."), 10) + else + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("Finished building FOB! Crates can now be picked up."), 10) + end + end, { _centroid, _heli:getCountry(), _heli:getCoalition() }, timer.getTime() + ctld.buildTimeFOB) - for _, _crate in pairs(_nearbyMultiCrates) do + ctld.processCallback({ unit = _heli, position = _centroid, action = "fob" }) - if _heli:getCoalition() == 1 then - ctld.droppedFOBCratesRED[_crate.crateUnit:getName()] = nil - ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil - else - ctld.droppedFOBCratesBLUE[_crate.crateUnit:getName()] = nil - ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil - end + trigger.action.smoke(_centroid, trigger.smokeColor.Green) - table.insert(_points, _crate.crateUnit:getPoint()) - - --destroy - _crate.crateUnit:destroy() - end - - local _centroid = ctld.getCentroid(_points) - - timer.scheduleFunction(function(_args) - - local _unitId = ctld.getNextUnitId() - local _name = "Deployed FOB #" .. _unitId - - local _fob = ctld.spawnFOB(_args[2], _unitId, _args[1], _name) - - --make it able to deploy crates - table.insert(ctld.logisticUnits, _fob:getName()) - - ctld.beaconCount = ctld.beaconCount + 1 - - local _radioBeaconName = "FOB Beacon #" .. ctld.beaconCount - - local _radioBeaconDetails = ctld.createRadioBeacon(_args[1], _args[3], _args[2], _radioBeaconName, nil, true) - - ctld.fobBeacons[_name] = { vhf = _radioBeaconDetails.vhf, uhf = _radioBeaconDetails.uhf, fm = _radioBeaconDetails.fm } - - if ctld.troopPickupAtFOB == true then - table.insert(ctld.builtFOBS, _fob:getName()) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Finished building FOB! Crates and Troops can now be picked up."), 10) - else - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Finished building FOB! Crates can now be picked up."), 10) - end - end, { _centroid, _heli:getCountry(), _heli:getCoalition() }, timer.getTime() + ctld.buildTimeFOB) - - ctld.processCallback({unit = _heli, position = _centroid, action = "fob"}) - - trigger.action.smoke(_centroid, trigger.smokeColor.Green) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke.", ctld.getPlayerNameOrType(_heli), _totalCrates, ctld.buildTimeFOB, 10)) - else - local _txt = ctld.i18n_translate("Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other", ctld.cratesRequiredForFOB, _totalCrates) - ctld.displayMessageToGroup(_heli, _txt, 20) - end + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate( + "%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke.", + ctld.getPlayerNameOrType(_heli), _totalCrates, ctld.buildTimeFOB, 10)) + else + local _txt = ctld.i18n_translate( + "Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other", + ctld.cratesRequiredForFOB, _totalCrates) + ctld.displayMessageToGroup(_heli, _txt, 20) + end end --unloads the sling crate when the helicopter is on the ground or between 4.5 - 10 meters function ctld.dropSlingCrate(_args) - local _unitName = _args[1] - local _heli = ctld.getTransportUnit(_unitName) - ctld.inTransitSlingLoadCrates[_unitName] = ctld.inTransitSlingLoadCrates[_unitName] or {} + local _unitName = _args[1] + local _heli = ctld.getTransportUnit(_unitName) + ctld.inTransitSlingLoadCrates[_unitName] = ctld.inTransitSlingLoadCrates[_unitName] or {} - if _heli == nil then - return -- no heli! - end + if _heli == nil then + return -- no heli! + end - local _currentCrate = ctld.inTransitSlingLoadCrates[_unitName][#ctld.inTransitSlingLoadCrates[_unitName]] + local _currentCrate = ctld.inTransitSlingLoadCrates[_unitName][#ctld.inTransitSlingLoadCrates[_unitName]] - if _currentCrate == nil then - if ctld.hoverPickup and ctld.loadCrateFromMenu then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands.", ctld.hoverTime), 10) - elseif ctld.hoverPickup then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate.", ctld.hoverTime), 10) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."), 10) - end + if _currentCrate == nil then + if ctld.hoverPickup and ctld.loadCrateFromMenu then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate( + "You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands.", + ctld.hoverTime), 10) + elseif ctld.hoverPickup then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate( + "You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate.", + ctld.hoverTime), 10) else - local _point = _heli:getPoint() - local _side = _heli:getCoalition() - local _hdg = mist.getHeading(_heli, true) - local _heightDiff = ctld.heightDiff(_heli) - - if _heightDiff > 40.0 then - table.remove(ctld.inTransitSlingLoadCrates[_unitName],#ctld.inTransitSlingLoadCrates[_unitName]) - ctld.adaptWeightToCargo(_unitName) - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You were too high! The crate has been destroyed"), 10) - return - end - local _loadedCratesCopy = mist.utils.deepCopy(ctld.inTransitSlingLoadCrates[_unitName]) - ctld.logTrace("_loadedCratesCopy = %s", ctld.p(_loadedCratesCopy)) - for _, _crate in pairs(_loadedCratesCopy) do - ctld.logTrace("_crate = %s", ctld.p(_crate)) - ctld.logTrace("ctld.inAir(_heli) = %s", ctld.p(ctld.inAir(_heli))) - ctld.logTrace("_heightDiff = %s", ctld.p(_heightDiff)) - local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", _crate.desc, _unitId) - local _model_type = nil - if ctld.inAir(_heli) == false or _heightDiff <= 7.5 then - _point = ctld.getPointAt12Oclock(_heli, 30) - local _position = "12" - if ctld.unitDynamicCargoCapable(_heli) then - _model_type = "dynamic" - _point = ctld.getPointAt6Oclock(_heli, 15) - _position = "6" - end - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("%1 crate has been safely unhooked and is at your %2 o'clock", _crate.desc, _position), 10) - elseif _heightDiff > 7.5 and _heightDiff <= 40.0 then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("%1 crate has been safely dropped below you", _crate.desc), 10) - end - --remove crate from cargo - table.remove(ctld.inTransitSlingLoadCrates[_unitName],#ctld.inTransitSlingLoadCrates[_unitName]) - ctld.spawnCrateStatic(_heli:getCountry(), _unitId, _point, _name, _crate.weight, _side, _hdg, _model_type) - end - ctld.adaptWeightToCargo(_unitName) + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate( + "You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."), + 10) end + else + local _point = _heli:getPoint() + local _side = _heli:getCoalition() + local _hdg = mist.getHeading(_heli, true) + local _heightDiff = ctld.heightDiff(_heli) + + if _heightDiff > 40.0 then + table.remove(ctld.inTransitSlingLoadCrates[_unitName], #ctld.inTransitSlingLoadCrates[_unitName]) + ctld.adaptWeightToCargo(_unitName) + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You were too high! The crate has been destroyed"), 10) + return + end + local _loadedCratesCopy = mist.utils.deepCopy(ctld.inTransitSlingLoadCrates[_unitName]) + ctld.logTrace("_loadedCratesCopy = %s", ctld.p(_loadedCratesCopy)) + for _, _crate in pairs(_loadedCratesCopy) do + ctld.logTrace("_crate = %s", ctld.p(_crate)) + ctld.logTrace("ctld.inAir(_heli) = %s", ctld.p(ctld.inAir(_heli))) + ctld.logTrace("_heightDiff = %s", ctld.p(_heightDiff)) + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s #%i", _crate.desc, _unitId) + local _model_type = nil + if ctld.inAir(_heli) == false or _heightDiff <= 7.5 then + _point = ctld.getPointAt12Oclock(_heli, 30) + local _position = "12" + if ctld.unitDynamicCargoCapable(_heli) then + _model_type = "dynamic" + _point = ctld.getPointAt6Oclock(_heli, 15) + _position = "6" + end + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("%1 crate has been safely unhooked and is at your %2 o'clock", _crate.desc, + _position), 10) + elseif _heightDiff > 7.5 and _heightDiff <= 40.0 then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("%1 crate has been safely dropped below you", _crate.desc), 10) + end + --remove crate from cargo + table.remove(ctld.inTransitSlingLoadCrates[_unitName], #ctld.inTransitSlingLoadCrates[_unitName]) + ctld.spawnCrateStatic(_heli:getCountry(), _unitId, _point, _name, _crate.weight, _side, _hdg, _model_type) + end + ctld.adaptWeightToCargo(_unitName) + end end --spawns a radio beacon made up of two units, -- one for VHF and one for UHF -- The units are set to to NOT engage function ctld.createRadioBeacon(_point, _coalition, _country, _name, _batteryTime, _isFOB) + local _freq = ctld.generateADFFrequencies() - local _freq = ctld.generateADFFrequencies() + --create timeout + local _battery - --create timeout - local _battery + if _batteryTime == nil then + _battery = timer.getTime() + (ctld.deployedBeaconBattery * 60) + else + _battery = timer.getTime() + (_batteryTime * 60) + end - if _batteryTime == nil then - _battery = timer.getTime() + (ctld.deployedBeaconBattery * 60) - else - _battery = timer.getTime() + (_batteryTime * 60) - end + local _lat, _lon = coord.LOtoLL(_point) - local _lat, _lon = coord.LOtoLL(_point) + local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) - local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) + --local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_point)), 5) - --local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_point)), 5) + local _freqsText = _name - local _freqsText = _name + if _isFOB then + -- _message = "FOB " .. _message + _battery = -1 --never run out of power! + end - if _isFOB then - -- _message = "FOB " .. _message - _battery = -1 --never run out of power! - end - - _freqsText = _freqsText .. " - " .. _latLngStr + _freqsText = _freqsText .. " - " .. _latLngStr - _freqsText = string.format("%.2f kHz - %.2f / %.2f MHz", _freq.vhf / 1000, _freq.uhf / 1000000, _freq.fm / 1000000) + _freqsText = string.format("%.2f kHz - %.2f / %.2f MHz", _freq.vhf / 1000, _freq.uhf / 1000000, _freq.fm / 1000000) - local _uhfGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) - local _vhfGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) - local _fmGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) + local _uhfGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) + local _vhfGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) + local _fmGroup = ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) - local _beaconDetails = { - vhf = _freq.vhf, - vhfGroup = _vhfGroup:getName(), - uhf = _freq.uhf, - uhfGroup = _uhfGroup:getName(), - fm = _freq.fm, - fmGroup = _fmGroup:getName(), - text = _freqsText, - battery = _battery, - coalition = _coalition, - } + local _beaconDetails = { + vhf = _freq.vhf, + vhfGroup = _vhfGroup:getName(), + uhf = _freq.uhf, + uhfGroup = _uhfGroup:getName(), + fm = _freq.fm, + fmGroup = _fmGroup:getName(), + text = _freqsText, + battery = _battery, + coalition = _coalition, + } - ctld.updateRadioBeacon(_beaconDetails) + ctld.updateRadioBeacon(_beaconDetails) - table.insert(ctld.deployedRadioBeacons, _beaconDetails) + table.insert(ctld.deployedRadioBeacons, _beaconDetails) - return _beaconDetails + return _beaconDetails end function ctld.generateADFFrequencies() + if #ctld.freeUHFFrequencies <= 3 then + ctld.freeUHFFrequencies = ctld.usedUHFFrequencies + ctld.usedUHFFrequencies = {} + end - if #ctld.freeUHFFrequencies <= 3 then - ctld.freeUHFFrequencies = ctld.usedUHFFrequencies - ctld.usedUHFFrequencies = {} - end - - --remove frequency at RANDOM - local _uhf = table.remove(ctld.freeUHFFrequencies, math.random(#ctld.freeUHFFrequencies)) - table.insert(ctld.usedUHFFrequencies, _uhf) + --remove frequency at RANDOM + local _uhf = table.remove(ctld.freeUHFFrequencies, math.random(#ctld.freeUHFFrequencies)) + table.insert(ctld.usedUHFFrequencies, _uhf) - if #ctld.freeVHFFrequencies <= 3 then - ctld.freeVHFFrequencies = ctld.usedVHFFrequencies - ctld.usedVHFFrequencies = {} - end + if #ctld.freeVHFFrequencies <= 3 then + ctld.freeVHFFrequencies = ctld.usedVHFFrequencies + ctld.usedVHFFrequencies = {} + end - local _vhf = table.remove(ctld.freeVHFFrequencies, math.random(#ctld.freeVHFFrequencies)) - table.insert(ctld.usedVHFFrequencies, _vhf) + local _vhf = table.remove(ctld.freeVHFFrequencies, math.random(#ctld.freeVHFFrequencies)) + table.insert(ctld.usedVHFFrequencies, _vhf) - if #ctld.freeFMFrequencies <= 3 then - ctld.freeFMFrequencies = ctld.usedFMFrequencies - ctld.usedFMFrequencies = {} - end + if #ctld.freeFMFrequencies <= 3 then + ctld.freeFMFrequencies = ctld.usedFMFrequencies + ctld.usedFMFrequencies = {} + end - local _fm = table.remove(ctld.freeFMFrequencies, math.random(#ctld.freeFMFrequencies)) - table.insert(ctld.usedFMFrequencies, _fm) + local _fm = table.remove(ctld.freeFMFrequencies, math.random(#ctld.freeFMFrequencies)) + table.insert(ctld.usedFMFrequencies, _fm) - return { uhf = _uhf, vhf = _vhf, fm = _fm } - --- return {uhf=_uhf,vhf=_vhf} + return { uhf = _uhf, vhf = _vhf, fm = _fm } + --- return {uhf=_uhf,vhf=_vhf} end - - function ctld.spawnRadioBeaconUnit(_point, _country, _name, _freqsText) + local _groupId = ctld.getNextGroupId() - local _groupId = ctld.getNextGroupId() + local _unitId = ctld.getNextUnitId() - local _unitId = ctld.getNextUnitId() + local _radioGroup = { + ["visible"] = false, + -- ["groupId"] = _groupId, + ["hidden"] = false, + ["units"] = { + [1] = { + ["y"] = _point.z, + ["type"] = "TACAN_beacon", + ["name"] = "Unit #" .. _unitId .. " - " .. _name .. " [" .. _freqsText .. "]", + -- ["unitId"] = _unitId, + ["heading"] = 0, + ["playerCanDrive"] = true, + ["skill"] = "Excellent", + ["x"] = _point.x, + } + }, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = "Group #" .. _groupId .. " - " .. _name, + ["task"] = {}, + --added two fields below for MIST + ["category"] = Group.Category.GROUND, + ["country"] = _country + } - local _radioGroup = { - ["visible"] = false, - -- ["groupId"] = _groupId, - ["hidden"] = false, - ["units"] = { - [1] = { - ["y"] = _point.z, - ["type"] = "TACAN_beacon", - ["name"] = "Unit #" .. _unitId .. " - " .. _name .. " [" .. _freqsText .. "]", - -- ["unitId"] = _unitId, - ["heading"] = 0, - ["playerCanDrive"] = true, - ["skill"] = "Excellent", - ["x"] = _point.x, - } - }, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = "Group #" .. _groupId .. " - " .. _name, - ["task"] = {}, - --added two fields below for MIST - ["category"] = Group.Category.GROUND, - ["country"] = _country - } - - -- return coalition.addGroup(_country, Group.Category.GROUND, _radioGroup) - return Group.getByName(mist.dynAdd(_radioGroup).name) + -- return coalition.addGroup(_country, Group.Category.GROUND, _radioGroup) + return Group.getByName(mist.dynAdd(_radioGroup).name) end function ctld.updateRadioBeacon(_beaconDetails) + local _vhfGroup = Group.getByName(_beaconDetails.vhfGroup) - local _vhfGroup = Group.getByName(_beaconDetails.vhfGroup) + local _uhfGroup = Group.getByName(_beaconDetails.uhfGroup) - local _uhfGroup = Group.getByName(_beaconDetails.uhfGroup) + local _fmGroup = Group.getByName(_beaconDetails.fmGroup) - local _fmGroup = Group.getByName(_beaconDetails.fmGroup) + local _radioLoop = {} - local _radioLoop = {} + if _vhfGroup ~= nil and _vhfGroup:getUnits() ~= nil and #_vhfGroup:getUnits() == 1 then + table.insert(_radioLoop, { group = _vhfGroup, freq = _beaconDetails.vhf, silent = false, mode = 0 }) + end - if _vhfGroup ~= nil and _vhfGroup:getUnits() ~= nil and #_vhfGroup:getUnits() == 1 then - table.insert(_radioLoop, { group = _vhfGroup, freq = _beaconDetails.vhf, silent = false, mode = 0 }) + if _uhfGroup ~= nil and _uhfGroup:getUnits() ~= nil and #_uhfGroup:getUnits() == 1 then + table.insert(_radioLoop, { group = _uhfGroup, freq = _beaconDetails.uhf, silent = true, mode = 0 }) + end + + if _fmGroup ~= nil and _fmGroup:getUnits() ~= nil and #_fmGroup:getUnits() == 1 then + table.insert(_radioLoop, { group = _fmGroup, freq = _beaconDetails.fm, silent = false, mode = 1 }) + end + + local _batLife = _beaconDetails.battery - timer.getTime() + + if (_batLife <= 0 and _beaconDetails.battery ~= -1) or #_radioLoop ~= 3 then + -- ran out of batteries + if _vhfGroup ~= nil then + trigger.action.stopRadioTransmission(_vhfGroup:getName()) + _vhfGroup:destroy() + end + if _uhfGroup ~= nil then + trigger.action.stopRadioTransmission(_uhfGroup:getName()) + _uhfGroup:destroy() + end + if _fmGroup ~= nil then + trigger.action.stopRadioTransmission(_fmGroup:getName()) + _fmGroup:destroy() end - if _uhfGroup ~= nil and _uhfGroup:getUnits() ~= nil and #_uhfGroup:getUnits() == 1 then - table.insert(_radioLoop, { group = _uhfGroup, freq = _beaconDetails.uhf, silent = true, mode = 0 }) + return false + end + + --fobs have unlimited battery life + -- if _battery ~= -1 then + -- _text = _text.." "..mist.utils.round(_batLife).." seconds of battery" + -- end + + for _, _radio in pairs(_radioLoop) do + local _groupController = _radio.group:getController() + + local _sound = ctld.radioSound + if _radio.silent then + _sound = ctld.radioSoundFC3 end - if _fmGroup ~= nil and _fmGroup:getUnits() ~= nil and #_fmGroup:getUnits() == 1 then - table.insert(_radioLoop, { group = _fmGroup, freq = _beaconDetails.fm, silent = false, mode = 1 }) - end + _sound = "l10n/DEFAULT/" .. _sound - local _batLife = _beaconDetails.battery - timer.getTime() - - if (_batLife <= 0 and _beaconDetails.battery ~= -1) or #_radioLoop ~= 3 then - -- ran out of batteries - if _vhfGroup ~= nil then - trigger.action.stopRadioTransmission(_vhfGroup:getName()) - _vhfGroup:destroy() - end - if _uhfGroup ~= nil then - trigger.action.stopRadioTransmission(_uhfGroup:getName()) - _uhfGroup:destroy() - end - if _fmGroup ~= nil then - trigger.action.stopRadioTransmission(_fmGroup:getName()) - _fmGroup:destroy() - end - - return false - end - - --fobs have unlimited battery life - -- if _battery ~= -1 then - -- _text = _text.." "..mist.utils.round(_batLife).." seconds of battery" - -- end - - for _, _radio in pairs(_radioLoop) do - - local _groupController = _radio.group:getController() - - local _sound = ctld.radioSound - if _radio.silent then - _sound = ctld.radioSoundFC3 - end - - _sound = "l10n/DEFAULT/".._sound - - _groupController:setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD) + _groupController:setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD) - -- stop the transmission at each call to the ctld.updateRadioBeacon method (default each minute) - trigger.action.stopRadioTransmission(_radio.group:getName()) + -- stop the transmission at each call to the ctld.updateRadioBeacon method (default each minute) + trigger.action.stopRadioTransmission(_radio.group:getName()) - -- restart it as the battery is still up - -- the transmission is set to loop and has the name of the transmitting DCS group (that includes the type - i.e. FM, UHF, VHF) - trigger.action.radioTransmission(_sound, _radio.group:getUnit(1):getPoint(), _radio.mode, true, _radio.freq, 1000, _radio.group:getName()) - end + -- restart it as the battery is still up + -- the transmission is set to loop and has the name of the transmitting DCS group (that includes the type - i.e. FM, UHF, VHF) + trigger.action.radioTransmission(_sound, _radio.group:getUnit(1):getPoint(), _radio.mode, true, _radio.freq, 1000, + _radio.group:getName()) + end - return true + return true end function ctld.listRadioBeacons(_args) + local _heli = ctld.getTransportUnit(_args[1]) + local _message = "" - local _heli = ctld.getTransportUnit(_args[1]) - local _message = "" - - if _heli ~= nil then - - for _x, _details in pairs(ctld.deployedRadioBeacons) do - - if _details.coalition == _heli:getCoalition() then - _message = _message .. _details.text .. "\n" - end - end - - if _message ~= "" then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Radio Beacons:\n%1", _message), 20) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No Active Radio Beacons"), 20) - end + if _heli ~= nil then + for _x, _details in pairs(ctld.deployedRadioBeacons) do + if _details.coalition == _heli:getCoalition() then + _message = _message .. _details.text .. "\n" + end end + + if _message ~= "" then + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Radio Beacons:\n%1", _message), 20) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No Active Radio Beacons"), 20) + end + end end function ctld.dropRadioBeacon(_args) + local _heli = ctld.getTransportUnit(_args[1]) + local _message = "" - local _heli = ctld.getTransportUnit(_args[1]) - local _message = "" + if _heli ~= nil and ctld.inAir(_heli) == false then + --deploy 50 m infront + --try to spawn at 12 oclock to us + local _point = ctld.getPointAt12Oclock(_heli, 50) - if _heli ~= nil and ctld.inAir(_heli) == false then + ctld.beaconCount = ctld.beaconCount + 1 + local _name = "Beacon #" .. ctld.beaconCount - --deploy 50 m infront - --try to spawn at 12 oclock to us - local _point = ctld.getPointAt12Oclock(_heli, 50) + local _radioBeaconDetails = ctld.createRadioBeacon(_point, _heli:getCoalition(), _heli:getCountry(), _name, nil, + false) - ctld.beaconCount = ctld.beaconCount + 1 - local _name = "Beacon #" .. ctld.beaconCount + -- mark with flare? - local _radioBeaconDetails = ctld.createRadioBeacon(_point, _heli:getCoalition(), _heli:getCountry(), _name, nil, false) - - -- mark with flare? - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 deployed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), _radioBeaconDetails.text, 20)) - - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before you can deploy a Radio Beacon!"), 20) - end + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 deployed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), + _radioBeaconDetails.text, 20)) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before you can deploy a Radio Beacon!"), + 20) + end end --remove closet radio beacon function ctld.removeRadioBeacon(_args) + local _heli = ctld.getTransportUnit(_args[1]) + local _message = "" - local _heli = ctld.getTransportUnit(_args[1]) - local _message = "" + if _heli ~= nil and ctld.inAir(_heli) == false then + -- mark with flare? - if _heli ~= nil and ctld.inAir(_heli) == false then + local _closestBeacon = nil + local _shortestDistance = -1 + local _distance = 0 - -- mark with flare? + for _x, _details in pairs(ctld.deployedRadioBeacons) do + if _details.coalition == _heli:getCoalition() then + local _group = Group.getByName(_details.vhfGroup) - local _closestBeacon = nil - local _shortestDistance = -1 - local _distance = 0 - - for _x, _details in pairs(ctld.deployedRadioBeacons) do - - if _details.coalition == _heli:getCoalition() then - - local _group = Group.getByName(_details.vhfGroup) - - if _group ~= nil and #_group:getUnits() == 1 then - - _distance = ctld.getDistance(_heli:getPoint(), _group:getUnit(1):getPoint()) - if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then - _shortestDistance = _distance - _closestBeacon = _details - end - end - end + if _group ~= nil and #_group:getUnits() == 1 then + _distance = ctld.getDistance(_heli:getPoint(), _group:getUnit(1):getPoint()) + if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then + _shortestDistance = _distance + _closestBeacon = _details + end end - - if _closestBeacon ~= nil and _shortestDistance then - local _vhfGroup = Group.getByName(_closestBeacon.vhfGroup) - - local _uhfGroup = Group.getByName(_closestBeacon.uhfGroup) - - local _fmGroup = Group.getByName(_closestBeacon.fmGroup) - - if _vhfGroup ~= nil then - trigger.action.stopRadioTransmission(_vhfGroup:getName()) - _vhfGroup:destroy() - end - if _uhfGroup ~= nil then - trigger.action.stopRadioTransmission(_uhfGroup:getName()) - _uhfGroup:destroy() - end - if _fmGroup ~= nil then - trigger.action.stopRadioTransmission(_fmGroup:getName()) - _fmGroup:destroy() - end - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 removed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), _closestBeacon.text, 20)) - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No Radio Beacons within 500m."), 20) - end - - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before remove a Radio Beacon"), 20) + end end + + if _closestBeacon ~= nil and _shortestDistance then + local _vhfGroup = Group.getByName(_closestBeacon.vhfGroup) + + local _uhfGroup = Group.getByName(_closestBeacon.uhfGroup) + + local _fmGroup = Group.getByName(_closestBeacon.fmGroup) + + if _vhfGroup ~= nil then + trigger.action.stopRadioTransmission(_vhfGroup:getName()) + _vhfGroup:destroy() + end + if _uhfGroup ~= nil then + trigger.action.stopRadioTransmission(_uhfGroup:getName()) + _uhfGroup:destroy() + end + if _fmGroup ~= nil then + trigger.action.stopRadioTransmission(_fmGroup:getName()) + _fmGroup:destroy() + end + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 removed a Radio Beacon.\n\n%2", ctld.getPlayerNameOrType(_heli), + _closestBeacon.text, 20)) + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("No Radio Beacons within 500m."), 20) + end + else + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You need to land before remove a Radio Beacon"), 20) + end end -- gets the center of a bunch of points! -- return proper DCS point with height function ctld.getCentroid(_points) - local _tx, _ty = 0, 0 - for _index, _point in ipairs(_points) do - _tx = _tx + _point.x - _ty = _ty + _point.z - end + local _tx, _ty = 0, 0 + for _index, _point in ipairs(_points) do + _tx = _tx + _point.x + _ty = _ty + _point.z + end - local _npoints = #_points + local _npoints = #_points - local _point = { x = _tx / _npoints, z = _ty / _npoints } + local _point = { x = _tx / _npoints, z = _ty / _npoints } - _point.y = land.getHeight({ _point.x, _point.z }) + _point.y = land.getHeight({ _point.x, _point.z }) - return _point + return _point end function ctld.getAATemplate(_unitName) - - for _,_system in pairs(ctld.AASystemTemplate) do - - if _system.repair == _unitName then - return _system - end - - for _,_part in pairs(_system.parts) do - - if _unitName == _part.name then - return _system - end - end + for _, _system in pairs(ctld.AASystemTemplate) do + if _system.repair == _unitName then + return _system end - return nil + for _, _part in pairs(_system.parts) do + if _unitName == _part.name then + return _system + end + end + end + return nil end function ctld.getLauncherUnitFromAATemplate(_aaTemplate) - for _,_part in pairs(_aaTemplate.parts) do - - if _part.launcher then - return _part.name - end + for _, _part in pairs(_aaTemplate.parts) do + if _part.launcher then + return _part.name end + end - return nil + return nil end function ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTemplate) + -- are we adding to existing aa system? + -- check to see if the crate is a launcher + if ctld.getLauncherUnitFromAATemplate(_aaSystemTemplate) == _nearestCrate.details.unit then + -- find nearest COMPLETE AA system + local _nearestSystem = ctld.findNearestAASystem(_heli, _aaSystemTemplate) - -- are we adding to existing aa system? - -- check to see if the crate is a launcher - if ctld.getLauncherUnitFromAATemplate(_aaSystemTemplate) == _nearestCrate.details.unit then + if _nearestSystem ~= nil and _nearestSystem.dist < 300 then + local _uniqueTypes = {} -- stores each unique part of system + local _types = {} + local _points = {} + local _hdgs = {} - -- find nearest COMPLETE AA system - local _nearestSystem = ctld.findNearestAASystem(_heli, _aaSystemTemplate) + local _units = _nearestSystem.group:getUnits() - if _nearestSystem ~= nil and _nearestSystem.dist < 300 then + if _units ~= nil and #_units > 0 then + for x = 1, #_units do + if _units[x]:getLife() > 0 then + --this allows us to count each type once + _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() - local _uniqueTypes = {} -- stores each unique part of system - local _types = {} - local _points = {} - local _hdgs = {} - - local _units = _nearestSystem.group:getUnits() - - if _units ~= nil and #_units > 0 then - - for x = 1, #_units do - if _units[x]:getLife() > 0 then - - --this allows us to count each type once - _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() - - table.insert(_points, _units[x]:getPoint()) - table.insert(_types, _units[x]:getTypeName()) - table.insert(_hdgs, mist.getHeading(_units[x], true)) - end - end - end - - -- do we have the correct number of unique pieces and do we have enough points for all the pieces - if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then - - -- rearm aa system - -- destroy old group - ctld.completeAASystems[_nearestSystem.group:getName()] = nil - - _nearestSystem.group:destroy() - - local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) - - ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, _aaSystemTemplate) - - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "rearm"}) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully rearmed a full %2 in the field", ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, 20)) - - if _heli:getCoalition() == 1 then - ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil - else - ctld.spawnedCratesBLUE[_nearestCrate.crateUnit:getName()] = nil - end - - -- remove crate - -- if ctld.slingLoad == false then - _nearestCrate.crateUnit:destroy() - -- end - - return true -- all done so quit - end + table.insert(_points, _units[x]:getPoint()) + table.insert(_types, _units[x]:getTypeName()) + table.insert(_hdgs, mist.getHeading(_units[x], true)) + end end - end + end - return false + -- do we have the correct number of unique pieces and do we have enough points for all the pieces + if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then + -- rearm aa system + -- destroy old group + ctld.completeAASystems[_nearestSystem.group:getName()] = nil + + _nearestSystem.group:destroy() + + local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) + + ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, + _aaSystemTemplate) + + ctld.processCallback({ unit = _heli, crate = _nearestCrate, spawnedGroup = _spawnedGroup, action = + "rearm" }) + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 successfully rearmed a full %2 in the field", ctld.getPlayerNameOrType(_heli), + _aaSystemTemplate.name, 20)) + + if _heli:getCoalition() == 1 then + ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil + else + ctld.spawnedCratesBLUE[_nearestCrate.crateUnit:getName()] = nil + end + + -- remove crate + -- if ctld.slingLoad == false then + _nearestCrate.crateUnit:destroy() + -- end + + return true -- all done so quit + end + end + end + + return false end -function ctld.getAASystemDetails(_hawkGroup,_aaSystemTemplate) +function ctld.getAASystemDetails(_hawkGroup, _aaSystemTemplate) + local _units = _hawkGroup:getUnits() - local _units = _hawkGroup:getUnits() + local _hawkDetails = {} - local _hawkDetails = {} + for _, _unit in pairs(_units) do + table.insert(_hawkDetails, + { point = _unit:getPoint(), unit = _unit:getTypeName(), name = _unit:getName(), system = _aaSystemTemplate, hdg = + mist.getHeading(_unit, true) }) + end - for _, _unit in pairs(_units) do - table.insert(_hawkDetails, { point = _unit:getPoint(), unit = _unit:getTypeName(), name = _unit:getName(), system =_aaSystemTemplate, hdg = mist.getHeading(_unit, true) }) - end - - return _hawkDetails + return _hawkDetails end function ctld.countTableEntries(_table) - - if _table == nil then - return 0 - end + if _table == nil then + return 0 + end - local _count = 0 + local _count = 0 - for _key, _value in pairs(_table) do + for _key, _value in pairs(_table) do + _count = _count + 1 + end - _count = _count + 1 - end - - return _count + return _count end -function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) - ctld.logTrace("_nearestCrate = %s", ctld.p(_nearestCrate)) - ctld.logTrace("_nearbyCrates = %s", ctld.p(_nearbyCrates)) - ctld.logTrace("_aaSystemTemplate = %s", ctld.p(_aaSystemTemplate)) +function ctld.unpackAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTemplate) + ctld.logTrace("_nearestCrate = %s", ctld.p(_nearestCrate)) + ctld.logTrace("_nearbyCrates = %s", ctld.p(_nearbyCrates)) + ctld.logTrace("_aaSystemTemplate = %s", ctld.p(_aaSystemTemplate)) - if ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates,_aaSystemTemplate) then - -- rearmed system - return + if ctld.rearmAASystem(_heli, _nearestCrate, _nearbyCrates, _aaSystemTemplate) then + -- rearmed system + return + end + + local _systemParts = {} + + --initialise list of parts + for _, _part in pairs(_aaSystemTemplate.parts) do + 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 - local _systemParts = {} + local _cratePositions = {} + local _crateHdg = {} - --initialise list of parts - for _,_part in pairs(_aaSystemTemplate.parts) do - 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 + 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 + ctld.logTrace("_nearbyCrate = %s", ctld.p(_nearbyCrate)) + if _nearbyCrate.dist < crateDistance then + local _name = _nearbyCrate.details.unit + ctld.logTrace("_name = %s", ctld.p(_name)) + + if _systemParts[_name] ~= nil then + local foundCount = _systemParts[_name].found + ctld.logTrace("foundCount = %s", ctld.p(foundCount)) + + if not _cratePositions[_name] then + _cratePositions[_name] = {} end - _systemParts[_part.name] = _systemPart - end - - 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 - ctld.logTrace("_nearbyCrate = %s", ctld.p(_nearbyCrate)) - if _nearbyCrate.dist < crateDistance then - - local _name = _nearbyCrate.details.unit - ctld.logTrace("_name = %s", ctld.p(_name)) - - if _systemParts[_name] ~= nil then - - local foundCount = _systemParts[_name].found - ctld.logTrace("foundCount = %s", ctld.p(foundCount)) - - if not _cratePositions[_name] then - _cratePositions[_name] = {} - end - if not _crateHdg[_name] then - _crateHdg[_name] = {} - end - - -- if this is our first time encountering this part of the system - if foundCount == 0 then - local _foundPart = _systemParts[_name] - - _foundPart.found = 1 - - -- 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 - ctld.logTrace("cratesRequired = %s", ctld.p(cratesRequired)) - if cratesRequired ~= nil then - _foundPart.required = cratesRequired - end - - _systemParts[_name] = _foundPart - 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 - if not _systemParts[_name].crates then - _systemParts[_name].crates = {} - end - table.insert(_systemParts[_name].crates, _nearbyCrate) - table.insert(_cratePositions[_name], crateUnit:getPoint()) - table.insert(_crateHdg[_name], mist.getHeading(crateUnit, true)) - end + if not _crateHdg[_name] then + _crateHdg[_name] = {} end - end - -- 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) + -- if this is our first time encountering this part of the system + if foundCount == 0 then + local _foundPart = _systemParts[_name] - -- 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 + _foundPart.found = 1 - local spawnDistance = 50 -- circle radius to spawn units in a circle and randomize position relative to the crate location - local arcRad = math.pi * 2 + -- 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 + ctld.logTrace("cratesRequired = %s", ctld.p(cratesRequired)) + if cratesRequired ~= nil then + _foundPart.required = cratesRequired + end - local _txt = "" - - local _posArray = {} - local _hdgArray = {} - local _typeArray = {} - -- for each part of the system parts - for _name, _systemPart in pairs(_systemParts) do - - -- check if enough crates were found to build the part - if _systemPart.found < _systemPart.required then - _txt = _txt..ctld.i18n_translate("Missing %1\n",_systemPart.desc) + _systemParts[_name] = _foundPart 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 - - -- 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 - - -- 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 - - --handle multiple units per part by spawning them in a circle around the crate - if partAmount > 1 then - - local angular_step = arcRad / partAmount - - 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 - - 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) - end - else - table.insert(_posArray, _point) - table.insert(_hdgArray, _hdg) - table.insert(_typeArray, _name) - end + -- 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 + if not _systemParts[_name].crates then + _systemParts[_name].crates = {} + end + table.insert(_systemParts[_name].crates, _nearbyCrate) + table.insert(_cratePositions[_name], crateUnit:getPoint()) + table.insert(_crateHdg[_name], mist.getHeading(crateUnit, true)) + end end + end - local _activeLaunchers = ctld.countCompleteAASystems(_heli) + -- 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) - local _allowed = ctld.getAllowedAASystems(_heli) - - env.info("Active: ".._activeLaunchers.." Allowed: ".._allowed) - - if _activeLaunchers + 1 > _allowed then - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("Out of parts for AA Systems. Current limit is %1\n", _allowed, 10)) - return + -- 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 - if _txt ~= "" then - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Cannot build %1\n%2\n\nOr the crates are not close enough together", _aaSystemTemplate.name, _txt), 20) - return + local spawnDistance = 50 -- circle radius to spawn units in a circle and randomize position relative to the crate location + local arcRad = math.pi * 2 + + local _txt = "" + + local _posArray = {} + local _hdgArray = {} + local _typeArray = {} + -- for each part of the system parts + for _name, _systemPart in pairs(_systemParts) do + -- check if enough crates were found to build the part + if _systemPart.found < _systemPart.required then + _txt = _txt .. ctld.i18n_translate("Missing %1\n", _systemPart.desc) 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 - -- 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 + -- 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 - -- 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 + -- 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 - -- HAWK / BUK READY! - local _spawnedGroup = ctld.spawnCrateGroup(_heli, _posArray, _typeArray, _hdgArray) + --handle multiple units per part by spawning them in a circle around the crate + if partAmount > 1 then + local angular_step = arcRad / partAmount - ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup,_aaSystemTemplate) + 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 - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "unpack"}) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4", ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, _allowed, (_activeLaunchers+1)), 10) + 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) + end + else + table.insert(_posArray, _point) + table.insert(_hdgArray, _hdg) + table.insert(_typeArray, _name) + end end + end + + local _activeLaunchers = ctld.countCompleteAASystems(_heli) + + local _allowed = ctld.getAllowedAASystems(_heli) + + env.info("Active: " .. _activeLaunchers .. " Allowed: " .. _allowed) + + if _activeLaunchers + 1 > _allowed then + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("Out of parts for AA Systems. Current limit is %1\n", _allowed, 10)) + return + end + + if _txt ~= "" then + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Cannot build %1\n%2\n\nOr the crates are not close enough together", + _aaSystemTemplate.name, _txt), 20) + return + else + -- 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 + + -- 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 + end + + -- HAWK / BUK READY! + local _spawnedGroup = ctld.spawnCrateGroup(_heli, _posArray, _typeArray, _hdgArray) + + ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, _aaSystemTemplate) + + ctld.processCallback({ unit = _heli, crate = _nearestCrate, spawnedGroup = _spawnedGroup, action = "unpack" }) + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate( + "%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4", + ctld.getPlayerNameOrType(_heli), _aaSystemTemplate.name, _allowed, (_activeLaunchers + 1)), 10) + end end --count the number of captured cities, sets the amount of allowed AA Systems function ctld.getAllowedAASystems(_heli) - - if _heli:getCoalition() == 1 then - return ctld.AASystemLimitBLUE - else - return ctld.AASystemLimitRED - end - - + if _heli:getCoalition() == 1 then + return ctld.AASystemLimitBLUE + else + return ctld.AASystemLimitRED + end end - function ctld.countCompleteAASystems(_heli) + local _count = 0 - local _count = 0 + for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do + local _hawkGroup = Group.getByName(_groupName) - for _groupName, _hawkDetails in pairs(ctld.completeAASystems) do + -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) + if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() then + local _units = _hawkGroup:getUnits() - local _hawkGroup = Group.getByName(_groupName) - - -- env.info(_groupName..": "..mist.utils.tableShow(_hawkDetails)) - if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() then - - local _units = _hawkGroup:getUnits() - - if _units ~=nil and #_units > 0 then - --get the system template - local _aaSystemTemplate = _hawkDetails[1].system - - local _uniqueTypes = {} -- stores each unique part of system - local _types = {} - local _points = {} - - if _units ~= nil and #_units > 0 then - - for x = 1, #_units do - if _units[x]:getLife() > 0 then - - --this allows us to count each type once - _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() - - table.insert(_points, _units[x]:getPoint()) - table.insert(_types, _units[x]:getTypeName()) - end - end - end - - -- do we have the correct number of unique pieces and do we have enough points for all the pieces - if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then - _count = _count +1 - end - end - end - end - - return _count -end - - -function ctld.repairAASystem(_heli, _nearestCrate,_aaSystem) - - -- find nearest COMPLETE AA system - local _nearestHawk = ctld.findNearestAASystem(_heli,_aaSystem) - - - - if _nearestHawk ~= nil and _nearestHawk.dist < 300 then - - local _oldHawk = ctld.completeAASystems[_nearestHawk.group:getName()] - - --spawn new one + if _units ~= nil and #_units > 0 then + --get the system template + local _aaSystemTemplate = _hawkDetails[1].system + local _uniqueTypes = {} -- stores each unique part of system local _types = {} - local _hdgs = {} local _points = {} - for _, _part in pairs(_oldHawk) do - table.insert(_points, _part.point) - table.insert(_hdgs, _part.hdg) - table.insert(_types, _part.unit) + if _units ~= nil and #_units > 0 then + for x = 1, #_units do + if _units[x]:getLife() > 0 then + --this allows us to count each type once + _uniqueTypes[_units[x]:getTypeName()] = _units[x]:getTypeName() + + table.insert(_points, _units[x]:getPoint()) + table.insert(_types, _units[x]:getTypeName()) + end + end end - --remove old system - ctld.completeAASystems[_nearestHawk.group:getName()] = nil - _nearestHawk.group:destroy() - - local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) - - ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup,_aaSystem) - - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "repair"}) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully repaired a full %2 in the field.", ctld.getPlayerNameOrType(_heli), _aaSystem.name), 10) - - if _heli:getCoalition() == 1 then - ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil - else - ctld.spawnedCratesBLUE[_nearestCrate.crateUnit:getName()] = nil + -- do we have the correct number of unique pieces and do we have enough points for all the pieces + if ctld.countTableEntries(_uniqueTypes) == _aaSystemTemplate.count and #_points >= _aaSystemTemplate.count then + _count = _count + 1 end - - -- remove crate - -- if ctld.slingLoad == false then - _nearestCrate.crateUnit:destroy() - -- end - - else - ctld.displayMessageToGroup(_heli, ctld.i18n_translate("Cannot repair %1. No damaged %2 within 300m", _aaSystem.name, _aaSystem.name), 10) + end end + end + + return _count +end + +function ctld.repairAASystem(_heli, _nearestCrate, _aaSystem) + -- find nearest COMPLETE AA system + local _nearestHawk = ctld.findNearestAASystem(_heli, _aaSystem) + + + + if _nearestHawk ~= nil and _nearestHawk.dist < 300 then + local _oldHawk = ctld.completeAASystems[_nearestHawk.group:getName()] + + --spawn new one + + local _types = {} + local _hdgs = {} + local _points = {} + + for _, _part in pairs(_oldHawk) do + table.insert(_points, _part.point) + table.insert(_hdgs, _part.hdg) + table.insert(_types, _part.unit) + end + + --remove old system + ctld.completeAASystems[_nearestHawk.group:getName()] = nil + _nearestHawk.group:destroy() + + local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types, _hdgs) + + ctld.completeAASystems[_spawnedGroup:getName()] = ctld.getAASystemDetails(_spawnedGroup, _aaSystem) + + ctld.processCallback({ unit = _heli, crate = _nearestCrate, spawnedGroup = _spawnedGroup, action = "repair" }) + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 successfully repaired a full %2 in the field.", ctld.getPlayerNameOrType(_heli), + _aaSystem.name), 10) + + if _heli:getCoalition() == 1 then + ctld.spawnedCratesRED[_nearestCrate.crateUnit:getName()] = nil + else + ctld.spawnedCratesBLUE[_nearestCrate.crateUnit:getName()] = nil + end + + -- remove crate + -- if ctld.slingLoad == false then + _nearestCrate.crateUnit:destroy() + -- end + else + ctld.displayMessageToGroup(_heli, + ctld.i18n_translate("Cannot repair %1. No damaged %2 within 300m", _aaSystem.name, _aaSystem.name), 10) + end end function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) + -- unpack multi crate + local _nearbyMultiCrates = {} - -- unpack multi crate - local _nearbyMultiCrates = {} + for _, _nearbyCrate in pairs(_nearbyCrates) do + if _nearbyCrate.dist < 300 then + if _nearbyCrate.details.unit == _nearestCrate.details.unit then + table.insert(_nearbyMultiCrates, _nearbyCrate) - for _, _nearbyCrate in pairs(_nearbyCrates) do - - if _nearbyCrate.dist < 300 then - - if _nearbyCrate.details.unit == _nearestCrate.details.unit then - - table.insert(_nearbyMultiCrates, _nearbyCrate) - - if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then - break - end - end + if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then + break end + end + end + end + + --- check crate count + if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then + local _point = _nearestCrate.crateUnit:getPoint() + local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true) + + -- destroy crates + for _, _crate in pairs(_nearbyMultiCrates) do + if _point == nil then + _point = _crate.crateUnit:getPoint() + end + + 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() + -- end end - --- check crate count - if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then - local _point = _nearestCrate.crateUnit:getPoint() - local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true) - - -- destroy crates - for _, _crate in pairs(_nearbyMultiCrates) do - - if _point == nil then - _point = _crate.crateUnit:getPoint() - end - - 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() - -- end - end - - - local _spawnedGroup = ctld.spawnCrateGroup(_heli, { _point }, { _nearestCrate.details.unit }, { _crateHdg }) - if _spawnedGroup == nil then - ctld.logError("ctld.unpackMultiCrate group was not spawned - skipping setGrpROE") - else - ctld.setGrpROE(_spawnedGroup) - ctld.processCallback({unit = _heli, crate = _nearestCrate , spawnedGroup = _spawnedGroup, action = "unpack"}) - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed %2 to the field using %3 crates.", ctld.getPlayerNameOrType(_heli), _nearestCrate.details.desc, #_nearbyMultiCrates), 10) - end + local _spawnedGroup = ctld.spawnCrateGroup(_heli, { _point }, { _nearestCrate.details.unit }, { _crateHdg }) + if _spawnedGroup == nil then + ctld.logError("ctld.unpackMultiCrate group was not spawned - skipping setGrpROE") else - - local _txt = ctld.i18n_translate("Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other", _nearestCrate.details.desc, _nearestCrate.details.cratesRequired, #_nearbyMultiCrates) - - ctld.displayMessageToGroup(_heli, _txt, 20) + ctld.setGrpROE(_spawnedGroup) + ctld.processCallback({ unit = _heli, crate = _nearestCrate, spawnedGroup = _spawnedGroup, action = "unpack" }) + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 successfully deployed %2 to the field using %3 crates.", + ctld.getPlayerNameOrType(_heli), _nearestCrate.details.desc, #_nearbyMultiCrates), 10) end -end + else + local _txt = ctld.i18n_translate( + "Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other", + _nearestCrate.details.desc, _nearestCrate.details.cratesRequired, #_nearbyMultiCrates) + ctld.displayMessageToGroup(_heli, _txt, 20) + end +end function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) - local _id = ctld.getNextGroupId() - local _groupName = _types[1] .. " #" .. _id - local _side = _heli:getCoalition() - local _group = { - ["visible"] = false, - -- ["groupId"] = _id, - ["hidden"] = false, - ["units"] = {}, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = _groupName, - ["tasks"] = {}, - ["radioSet"] = false, - ["task"] = "Reconnaissance", - ["route"] = {}, + local _id = ctld.getNextGroupId() + local _groupName = _types[1] .. " #" .. _id + local _side = _heli:getCoalition() + local _group = { + ["visible"] = false, + -- ["groupId"] = _id, + ["hidden"] = false, + ["units"] = {}, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = _groupName, + ["tasks"] = {}, + ["radioSet"] = false, + ["task"] = "Reconnaissance", + ["route"] = {}, + } + + local _hdg = 120 * math.pi / 180 -- radians = 120 degrees + if _types[1] ~= "MQ-9 Reaper" and _types[1] ~= "RQ-1A Predator" then -- non-drones - JTAC + local _spreadMin = 5 + local _spreadMax = 5 + local _spreadMult = 1 + 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 + _hdg = _hdgs[_i] + end + + _group.units[_i] = ctld.createUnit(_pos.x + math.random(_spreadMin, _spreadMax) * _spreadMult, + _pos.z + math.random(_spreadMin, _spreadMax) * _spreadMult, + _hdg, + _details) + end + _group.category = Group.Category.GROUND + else -- drones - JTAC + local _unitId = ctld.getNextUnitId() + local _details = { + type = _types[1], + unitId = _unitId, + name = string.format("Unpacked %s #%i", _types[1], _unitId), + livery_id = "'camo' scheme", + skill = "High", + speed = 80, + payload = { pylons = {}, fuel = 1300, flare = 0, chaff = 0, gun = 100 } } - local _hdg = 120 * math.pi / 180 -- radians = 120 degrees - if _types[1] ~= "MQ-9 Reaper" and _types[1] ~= "RQ-1A Predator" then -- non-drones - JTAC - local _spreadMin = 5 - local _spreadMax = 5 - local _spreadMult = 1 - 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 - _hdg = _hdgs[_i] - end - - _group.units[_i] = ctld.createUnit( _pos.x +math.random(_spreadMin,_spreadMax)*_spreadMult, - _pos.z +math.random(_spreadMin,_spreadMax)*_spreadMult, - _hdg, - _details) - end - _group.category = Group.Category.GROUND - else -- drones - JTAC - local _unitId = ctld.getNextUnitId() - local _details = { type = _types[1], - unitId = _unitId, - name = string.format("Unpacked %s #%i", _types[1], _unitId), - livery_id = "'camo' scheme", - skill = "High", - speed = 80, - payload = { pylons = {}, fuel = 1300, flare = 0, chaff = 0, gun = 100 } } - - _group.units[1] = ctld.createUnit( _positions[1].x, - _positions[1].z + ctld.jtacDroneRadius, - _hdg, - _details) - - _group.category = Group.Category.AIRPLANE -- for drones + _group.units[1] = ctld.createUnit(_positions[1].x, + _positions[1].z + ctld.jtacDroneRadius, + _hdg, + _details) - -- create drone orbiting route - local DroneRoute = { - ["points"] = - { - [1] = - { - ["alt"] = 2000, - ["action"] = "Turning Point", - ["alt_type"] = "BARO", - ["properties"] = - { - ["addopt"] = {}, - }, -- end of ["properties"] - ["speed"] = 80, - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["enabled"] = true, - ["auto"] = false, - ["id"] = "WrappedAction", - ["number"] = 1, - ["params"] = - { - ["action"] = - { - ["id"] = "EPLRS", - ["params"] = - { - ["value"] = true, - ["groupId"] = 0, - }, -- end of ["params"] - }, -- end of ["action"] - }, -- end of ["params"] - }, -- end of [1] - [2] = - { - ["number"] = 2, - ["auto"] = false, - ["id"] = "Orbit", - ["enabled"] = true, - ["params"] = - { - ["altitude"] = ctld.jtacDroneAltitude, - ["pattern"] = "Circle", - ["speed"] = 80, - }, -- end of ["params"] - }, -- end of [2] - [3] = - { - ["enabled"] = true, - ["auto"] = false, - ["id"] = "WrappedAction", - ["number"] = 3, - ["params"] = - { - ["action"] = - { - ["id"] = "Option", - ["params"] = - { - ["value"] = true, - ["name"] = 6, - }, -- end of ["params"] - }, -- end of ["action"] - }, -- end of ["params"] - }, -- end of [3] - }, -- end of ["tasks"] - }, -- end of ["params"] - }, -- end of ["task"] - ["type"] = "Turning Point", - ["ETA"] = 0, - ["ETA_locked"] = true, - ["y"] = _positions[1].z, - ["x"] = _positions[1].x, - ["speed_locked"] = true, - ["formation_template"] = "", - }, -- end of [1] - }, -- end of ["points"] - } -- end of ["route"] - --------------------------------------------------------------------------------- - _group.route = DroneRoute - end - - _group.country = _heli:getCountry() - local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) - return _spawnedGroup + _group.category = Group.Category.AIRPLANE -- for drones + + -- create drone orbiting route + local DroneRoute = { + ["points"] = + { + [1] = + { + ["alt"] = 2000, + ["action"] = "Turning Point", + ["alt_type"] = "BARO", + ["properties"] = + { + ["addopt"] = {}, + }, -- end of ["properties"] + ["speed"] = 80, + ["task"] = + { + ["id"] = "ComboTask", + ["params"] = + { + ["tasks"] = + { + [1] = + { + ["enabled"] = true, + ["auto"] = false, + ["id"] = "WrappedAction", + ["number"] = 1, + ["params"] = + { + ["action"] = + { + ["id"] = "EPLRS", + ["params"] = + { + ["value"] = true, + ["groupId"] = 0, + }, -- end of ["params"] + }, -- end of ["action"] + }, -- end of ["params"] + }, -- end of [1] + [2] = + { + ["number"] = 2, + ["auto"] = false, + ["id"] = "Orbit", + ["enabled"] = true, + ["params"] = + { + ["altitude"] = ctld.jtacDroneAltitude, + ["pattern"] = "Circle", + ["speed"] = 80, + }, -- end of ["params"] + }, -- end of [2] + [3] = + { + ["enabled"] = true, + ["auto"] = false, + ["id"] = "WrappedAction", + ["number"] = 3, + ["params"] = + { + ["action"] = + { + ["id"] = "Option", + ["params"] = + { + ["value"] = true, + ["name"] = 6, + }, -- end of ["params"] + }, -- end of ["action"] + }, -- end of ["params"] + }, -- end of [3] + }, -- end of ["tasks"] + }, -- end of ["params"] + }, -- end of ["task"] + ["type"] = "Turning Point", + ["ETA"] = 0, + ["ETA_locked"] = true, + ["y"] = _positions[1].z, + ["x"] = _positions[1].x, + ["speed_locked"] = true, + ["formation_template"] = "", + }, -- end of [1] + }, -- end of ["points"] + } -- end of ["route"] + --------------------------------------------------------------------------------- + _group.route = DroneRoute + end + + _group.country = _heli:getCountry() + local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) + return _spawnedGroup end - - -- spawn normal group function ctld.spawnDroppedGroup(_point, _details, _spawnBehind, _maxSearch) + local _groupName = _details.groupName - local _groupName = _details.groupName - - local _group = { - ["visible"] = false, - -- ["groupId"] = _details.groupId, - ["hidden"] = false, - ["units"] = {}, - -- ["y"] = _positions[1].z, - -- ["x"] = _positions[1].x, - ["name"] = _groupName, - ["task"] = {}, - } + local _group = { + ["visible"] = false, + -- ["groupId"] = _details.groupId, + ["hidden"] = false, + ["units"] = {}, + -- ["y"] = _positions[1].z, + -- ["x"] = _positions[1].x, + ["name"] = _groupName, + ["task"] = {}, + } - if _spawnBehind == false then + if _spawnBehind == false then + -- spawn in circle around heli - -- spawn in circle around heli + local _pos = _point - local _pos = _point + for _i, _detail in ipairs(_details.units) do + local _angle = math.pi * 2 * (_i - 1) / #_details.units + local _xOffset = math.cos(_angle) * 30 + local _yOffset = math.sin(_angle) * 30 - for _i, _detail in ipairs(_details.units) do - - local _angle = math.pi * 2 * (_i - 1) / #_details.units - local _xOffset = math.cos(_angle) * 30 - local _yOffset = math.sin(_angle) * 30 - - _group.units[_i] = ctld.createUnit(_pos.x + _xOffset, _pos.z + _yOffset, _angle, _detail) - end - - else - - local _pos = _point - - --try to spawn at 6 oclock to us - local _angle = math.atan2(_pos.z, _pos.x) - local _xOffset = math.cos(_angle) * -30 - local _yOffset = math.sin(_angle) * -30 - - - for _i, _detail in ipairs(_details.units) do - _group.units[_i] = ctld.createUnit(_pos.x + (_xOffset + 10 * _i), _pos.z + (_yOffset + 10 * _i), _angle, _detail) - end + _group.units[_i] = ctld.createUnit(_pos.x + _xOffset, _pos.z + _yOffset, _angle, _detail) end + else + local _pos = _point - --switch to MIST - _group.category = Group.Category.GROUND; - _group.country = _details.country; - - local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) - - --local _spawnedGroup = coalition.addGroup(_details.country, Group.Category.GROUND, _group) + --try to spawn at 6 oclock to us + local _angle = math.atan2(_pos.z, _pos.x) + local _xOffset = math.cos(_angle) * -30 + local _yOffset = math.sin(_angle) * -30 - -- find nearest enemy and head there - if _maxSearch == nil then - _maxSearch = ctld.maximumSearchDistance + for _i, _detail in ipairs(_details.units) do + _group.units[_i] = ctld.createUnit(_pos.x + (_xOffset + 10 * _i), _pos.z + (_yOffset + 10 * _i), _angle, + _detail) end + end - local _wpZone = ctld.inWaypointZone(_point,_spawnedGroup:getCoalition()) + --switch to MIST + _group.category = Group.Category.GROUND; + _group.country = _details.country; - if _wpZone.inZone then - ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _wpZone.point) - env.info("Heading to waypoint - In Zone ".._wpZone.name) - else - local _enemyPos = ctld.findNearestEnemy(_details.side, _point, _maxSearch) + local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) - ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _enemyPos) - end + --local _spawnedGroup = coalition.addGroup(_details.country, Group.Category.GROUND, _group) - return _spawnedGroup + + -- find nearest enemy and head there + if _maxSearch == nil then + _maxSearch = ctld.maximumSearchDistance + end + + local _wpZone = ctld.inWaypointZone(_point, _spawnedGroup:getCoalition()) + + if _wpZone.inZone then + ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _wpZone.point) + env.info("Heading to waypoint - In Zone " .. _wpZone.name) + else + local _enemyPos = ctld.findNearestEnemy(_details.side, _point, _maxSearch) + + ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _enemyPos) + end + + return _spawnedGroup end function ctld.findNearestEnemy(_side, _point, _searchDistance) + local _closestEnemy = nil - local _closestEnemy = nil + local _groups - local _groups + local _closestEnemyDist = _searchDistance - local _closestEnemyDist = _searchDistance + local _heliPoint = _point - local _heliPoint = _point + if _side == 2 then + _groups = coalition.getGroups(1, Group.Category.GROUND) + else + _groups = coalition.getGroups(2, Group.Category.GROUND) + end - if _side == 2 then - _groups = coalition.getGroups(1, Group.Category.GROUND) - else - _groups = coalition.getGroups(2, Group.Category.GROUND) - end + for _, _group in pairs(_groups) do + if _group ~= nil then + local _units = _group:getUnits() - for _, _group in pairs(_groups) do + if _units ~= nil and #_units > 0 then + local _leader = nil - if _group ~= nil then - local _units = _group:getUnits() - - if _units ~= nil and #_units > 0 then - - local _leader = nil - - -- find alive leader - for x = 1, #_units do - if _units[x]:getLife() > 0 then - _leader = _units[x] - break - end - end - - if _leader ~= nil then - local _leaderPos = _leader:getPoint() - local _dist = ctld.getDistance(_heliPoint, _leaderPos) - if _dist < _closestEnemyDist then - _closestEnemyDist = _dist - _closestEnemy = _leaderPos - end - end - end + -- find alive leader + for x = 1, #_units do + if _units[x]:getLife() > 0 then + _leader = _units[x] + break + end end + + if _leader ~= nil then + local _leaderPos = _leader:getPoint() + local _dist = ctld.getDistance(_heliPoint, _leaderPos) + if _dist < _closestEnemyDist then + _closestEnemyDist = _dist + _closestEnemy = _leaderPos + end + end + end end + end - -- no enemy - move to random point - if _closestEnemy ~= nil then + -- no enemy - move to random point + if _closestEnemy ~= nil then + -- env.info("found enemy") + return _closestEnemy + else + local _x = _heliPoint.x + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) + local _z = _heliPoint.z + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) + local _y = _heliPoint.y + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) - -- env.info("found enemy") - return _closestEnemy - else - - local _x = _heliPoint.x + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) - local _z = _heliPoint.z + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) - local _y = _heliPoint.y + math.random(0, ctld.maximumMoveDistance) - math.random(0, ctld.maximumMoveDistance) - - return { x = _x, z = _z,y=_y } - end + return { x = _x, z = _z, y = _y } + end end function ctld.findNearestGroup(_heli, _groups) + local _closestGroupDetails = {} + local _closestGroup = nil - local _closestGroupDetails = {} - local _closestGroup = nil + local _closestGroupDist = ctld.maxExtractDistance - local _closestGroupDist = ctld.maxExtractDistance + local _heliPoint = _heli:getPoint() - local _heliPoint = _heli:getPoint() + for _, _groupName in pairs(_groups) do + local _group = Group.getByName(_groupName) - for _, _groupName in pairs(_groups) do + if _group ~= nil then + local _units = _group:getUnits() - local _group = Group.getByName(_groupName) + if _units ~= nil and #_units > 0 then + local _leader = nil - if _group ~= nil then - local _units = _group:getUnits() + local _groupDetails = { groupId = _group:getID(), groupName = _group:getName(), side = _group + :getCoalition(), units = {} } - if _units ~= nil and #_units > 0 then - - local _leader = nil - - local _groupDetails = { groupId = _group:getID(), groupName = _group:getName(), side = _group:getCoalition(), units = {} } - - -- find alive leader - for x = 1, #_units do - if _units[x]:getLife() > 0 then - - if _leader == nil then - _leader = _units[x] - -- set country based on leader - _groupDetails.country = _leader:getCountry() - end - - local _unitDetails = { type = _units[x]:getTypeName(), unitId = _units[x]:getID(), name = _units[x]:getName() } - - table.insert(_groupDetails.units, _unitDetails) - end - end - - if _leader ~= nil then - local _leaderPos = _leader:getPoint() - local _dist = ctld.getDistance(_heliPoint, _leaderPos) - if _dist < _closestGroupDist then - _closestGroupDist = _dist - _closestGroupDetails = _groupDetails - _closestGroup = _group - end - end + -- find alive leader + for x = 1, #_units do + if _units[x]:getLife() > 0 then + if _leader == nil then + _leader = _units[x] + -- set country based on leader + _groupDetails.country = _leader:getCountry() end + + local _unitDetails = { type = _units[x]:getTypeName(), unitId = _units[x]:getID(), name = _units + [x]:getName() } + + table.insert(_groupDetails.units, _unitDetails) + end end + + if _leader ~= nil then + local _leaderPos = _leader:getPoint() + local _dist = ctld.getDistance(_heliPoint, _leaderPos) + if _dist < _closestGroupDist then + _closestGroupDist = _dist + _closestGroupDetails = _groupDetails + _closestGroup = _group + end + end + end end + end - if _closestGroup ~= nil then - - return { group = _closestGroup, details = _closestGroupDetails } - else - - return nil - end + if _closestGroup ~= nil then + return { group = _closestGroup, details = _closestGroupDetails } + else + return nil + end end - function ctld.createUnit(_x, _y, _angle, _details) + local _newUnit = { + ["y"] = _y, + ["type"] = _details.type, + ["name"] = _details.name, + -- ["unitId"] = _details.unitId, + ["heading"] = _angle, + ["playerCanDrive"] = true, + ["skill"] = "Excellent", + ["x"] = _x, + } - local _newUnit = { - ["y"] = _y, - ["type"] = _details.type, - ["name"] = _details.name, - -- ["unitId"] = _details.unitId, - ["heading"] = _angle, - ["playerCanDrive"] = true, - ["skill"] = "Excellent", - ["x"] = _x, - } - - return _newUnit + return _newUnit end function ctld.addEWRTask(_group) + -- delayed 2 second to work around bug + timer.scheduleFunction(function(_ewrGroup) + local _grp = ctld.getAliveGroup(_ewrGroup) - -- delayed 2 second to work around bug - timer.scheduleFunction(function(_ewrGroup) - local _grp = ctld.getAliveGroup(_ewrGroup) - - if _grp ~= nil then - local _controller = _grp:getController(); - local _EWR = { - id = 'EWR', - auto = true, - params = { - } - } - _controller:setTask(_EWR) - end + if _grp ~= nil then + local _controller = _grp:getController(); + local _EWR = { + id = 'EWR', + auto = true, + params = { + } + } + _controller:setTask(_EWR) end - , _group:getName(), timer.getTime() + 2) - + end + , _group:getName(), timer.getTime() + 2) end function ctld.orderGroupToMoveToPoint(_leader, _destination) + local _group = _leader:getGroup() - local _group = _leader:getGroup() + local _path = {} + table.insert(_path, mist.ground.buildWP(_leader:getPoint(), 'Off Road', 50)) + table.insert(_path, mist.ground.buildWP(_destination, 'Off Road', 50)) - local _path = {} - table.insert(_path, mist.ground.buildWP(_leader:getPoint(), 'Off Road', 50)) - table.insert(_path, mist.ground.buildWP(_destination, 'Off Road', 50)) - - local _mission = { - id = 'Mission', - params = { - route = { - points =_path - }, - }, - } + local _mission = { + id = 'Mission', + params = { + route = { + points = _path + }, + }, + } - -- delayed 2 second to work around bug - timer.scheduleFunction(function(_arg) - local _grp = ctld.getAliveGroup(_arg[1]) + -- delayed 2 second to work around bug + timer.scheduleFunction(function(_arg) + local _grp = ctld.getAliveGroup(_arg[1]) - if _grp ~= nil then - local _controller = _grp:getController(); - Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) - Controller.setOption(_controller, AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE) - _controller:setTask(_arg[2]) - end + if _grp ~= nil then + local _controller = _grp:getController(); + Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) + Controller.setOption(_controller, AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE) + _controller:setTask(_arg[2]) end - , {_group:getName(), _mission}, timer.getTime() + 2) - + end + , { _group:getName(), _mission }, timer.getTime() + 2) end -- are we in pickup zone function ctld.inPickupZone(_heli) + if ctld.inAir(_heli) then + return { inZone = false, limit = -1, index = -1 } + end - if ctld.inAir(_heli) then - return { inZone = false, limit = -1, index = -1 } + local _heliPoint = _heli:getPoint() + + for _i, _zoneDetails in pairs(ctld.pickupZones) do + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) + + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_zoneDetails[1]) + + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point + _triggerZone.radius = 200 -- should be big enough for ship + end end - local _heliPoint = _heli:getPoint() - - for _i, _zoneDetails in pairs(ctld.pickupZones) do - - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_zoneDetails[1]) - - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point - _triggerZone.radius = 200 -- should be big enough for ship - end + if _triggerZone ~= nil then + --get distance to center + local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) + if _dist <= _triggerZone.radius then + local _heliCoalition = _heli:getCoalition() + if _zoneDetails[4] == 1 and (_zoneDetails[5] == _heliCoalition or _zoneDetails[5] == 0) then + return { inZone = true, limit = _zoneDetails[3], index = _i } end - - if _triggerZone ~= nil then - - --get distance to center - - local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) - if _dist <= _triggerZone.radius then - local _heliCoalition = _heli:getCoalition() - if _zoneDetails[4] == 1 and (_zoneDetails[5] == _heliCoalition or _zoneDetails[5] == 0) then - return { inZone = true, limit = _zoneDetails[3], index = _i } - end - end - end + end end + end - local _fobs = ctld.getSpawnedFobs(_heli) + local _fobs = ctld.getSpawnedFobs(_heli) - -- now check spawned fobs - for _, _fob in ipairs(_fobs) do + -- now check spawned fobs + for _, _fob in ipairs(_fobs) do + --get distance to center - --get distance to center + local _dist = ctld.getDistance(_heliPoint, _fob:getPoint()) - local _dist = ctld.getDistance(_heliPoint, _fob:getPoint()) - - if _dist <= 150 then - return { inZone = true, limit = 10000, index = -1 }; - end + if _dist <= 150 then + return { inZone = true, limit = 10000, index = -1 }; end + end - return { inZone = false, limit = -1, index = -1 }; + return { inZone = false, limit = -1, index = -1 }; end function ctld.getSpawnedFobs(_heli) + local _fobs = {} - local _fobs = {} + for _, _fobName in ipairs(ctld.builtFOBS) do + local _fob = StaticObject.getByName(_fobName) - for _, _fobName in ipairs(ctld.builtFOBS) do - - local _fob = StaticObject.getByName(_fobName) - - if _fob ~= nil and _fob:isExist() and _fob:getCoalition() == _heli:getCoalition() and _fob:getLife() > 0 then - - table.insert(_fobs, _fob) - end + if _fob ~= nil and _fob:isExist() and _fob:getCoalition() == _heli:getCoalition() and _fob:getLife() > 0 then + table.insert(_fobs, _fob) end + end - return _fobs + return _fobs end -- are we in a dropoff zone function ctld.inDropoffZone(_heli) - - if ctld.inAir(_heli) then - return false - end - - local _heliPoint = _heli:getPoint() - - for _, _zoneDetails in pairs(ctld.dropOffZones) do - - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - - if _triggerZone ~= nil and (_zoneDetails[3] == _heli:getCoalition() or _zoneDetails[3]== 0) then - - --get distance to center - - local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) - - if _dist <= _triggerZone.radius then - return true - end - end - end - + if ctld.inAir(_heli) then return false + end + + local _heliPoint = _heli:getPoint() + + for _, _zoneDetails in pairs(ctld.dropOffZones) do + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) + + if _triggerZone ~= nil and (_zoneDetails[3] == _heli:getCoalition() or _zoneDetails[3] == 0) then + --get distance to center + + local _dist = ctld.getDistance(_heliPoint, _triggerZone.point) + + if _dist <= _triggerZone.radius then + return true + end + end + end + + return false end -- are we in a waypoint zone -function ctld.inWaypointZone(_point,_coalition) +function ctld.inWaypointZone(_point, _coalition) + for _, _zoneDetails in pairs(ctld.wpZones) do + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - for _, _zoneDetails in pairs(ctld.wpZones) do + --right coalition and active? + if _triggerZone ~= nil and (_zoneDetails[4] == _coalition or _zoneDetails[4] == 0) and _zoneDetails[3] == 1 then + --get distance to center - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) + local _dist = ctld.getDistance(_point, _triggerZone.point) - --right coalition and active? - if _triggerZone ~= nil and (_zoneDetails[4] == _coalition or _zoneDetails[4]== 0) and _zoneDetails[3] == 1 then - - --get distance to center - - local _dist = ctld.getDistance(_point, _triggerZone.point) - - if _dist <= _triggerZone.radius then - return {inZone = true, point = _triggerZone.point, name = _zoneDetails[1]} - end - end + if _dist <= _triggerZone.radius then + return { inZone = true, point = _triggerZone.point, name = _zoneDetails[1] } + end end + end - return {inZone = false} + return { inZone = false } end -- are we near friendly logistics zone function ctld.inLogisticsZone(_heli) - ctld.logDebug("ctld.inLogisticsZone(), _heli = %s", ctld.p(_heli)) - - if ctld.inAir(_heli) then - return false - end - local _heliPoint = _heli:getPoint() - ctld.logDebug("_heliPoint = %s", ctld.p(_heliPoint)) - for _, _name in pairs(ctld.logisticUnits) do - ctld.logDebug("_name = %s", ctld.p(_name)) - local _logistic = StaticObject.getByName(_name) - if not _logistic then - _logistic = Unit.getByName(_name) - end - ctld.logDebug("_logistic = %s", ctld.p(_logistic)) - if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() and _logistic:getLife() > 0 then - --get distance - local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) - ctld.logDebug("_dist = %s", ctld.p(_dist)) - if _dist <= ctld.maximumDistanceLogistic then - return true - end - end - end + ctld.logDebug("ctld.inLogisticsZone(), _heli = %s", ctld.p(_heli)) + if ctld.inAir(_heli) then return false -end + end + local _heliPoint = _heli:getPoint() + ctld.logDebug("_heliPoint = %s", ctld.p(_heliPoint)) + for _, _name in pairs(ctld.logisticUnits) do + ctld.logDebug("_name = %s", ctld.p(_name)) + local _logistic = StaticObject.getByName(_name) + if not _logistic then + _logistic = Unit.getByName(_name) + end + ctld.logDebug("_logistic = %s", ctld.p(_logistic)) + if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() and _logistic:getLife() > 0 then + --get distance + local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) + ctld.logDebug("_dist = %s", ctld.p(_dist)) + if _dist <= ctld.maximumDistanceLogistic then + return true + end + end + end + return false +end -- are far enough from a friendly logistics zone function ctld.farEnoughFromLogisticZone(_heli) + if ctld.inAir(_heli) then + return false + end - if ctld.inAir(_heli) then - return false + local _heliPoint = _heli:getPoint() + + local _farEnough = true + + for _, _name in pairs(ctld.logisticUnits) do + local _logistic = StaticObject.getByName(_name) + + if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() then + --get distance + local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) + -- env.info("DIST ".._dist) + if _dist <= ctld.minimumDeployDistance then + -- env.info("TOO CLOSE ".._dist) + _farEnough = false + end end + end - local _heliPoint = _heli:getPoint() - - local _farEnough = true - - for _, _name in pairs(ctld.logisticUnits) do - - local _logistic = StaticObject.getByName(_name) - - if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() then - - --get distance - local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) - -- env.info("DIST ".._dist) - if _dist <= ctld.minimumDeployDistance then - -- env.info("TOO CLOSE ".._dist) - _farEnough = false - end - end - end - - return _farEnough + return _farEnough end function ctld.refreshSmoke() + if ctld.disableAllSmoke == true then + return + end - if ctld.disableAllSmoke == true then - return - end + for _, _zoneGroup in pairs({ ctld.pickupZones, ctld.dropOffZones }) do + for _, _zoneDetails in pairs(_zoneGroup) do + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - for _, _zoneGroup in pairs({ ctld.pickupZones, ctld.dropOffZones }) do + if _triggerZone == nil then + local _ship = ctld.getTransportUnit(_triggerZone) - for _, _zoneDetails in pairs(_zoneGroup) do - - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - - if _triggerZone == nil then - local _ship = ctld.getTransportUnit(_triggerZone) - - if _ship then - local _point = _ship:getPoint() - _triggerZone = {} - _triggerZone.point = _point - end - - end - - - --only trigger if smoke is on AND zone is active - if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[4] == 1 then - - -- Trigger smoke markers - - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - - trigger.action.smoke(_pos3, _zoneDetails[2]) - end + if _ship then + local _point = _ship:getPoint() + _triggerZone = {} + _triggerZone.point = _point end + end + + + --only trigger if smoke is on AND zone is active + if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[4] == 1 then + -- Trigger smoke markers + + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } + + trigger.action.smoke(_pos3, _zoneDetails[2]) + end end + end - --waypoint zones - for _, _zoneDetails in pairs(ctld.wpZones) do + --waypoint zones + for _, _zoneDetails in pairs(ctld.wpZones) do + local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) - local _triggerZone = trigger.misc.getZone(_zoneDetails[1]) + --only trigger if smoke is on AND zone is active + if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[3] == 1 then + -- Trigger smoke markers - --only trigger if smoke is on AND zone is active - if _triggerZone ~= nil and _zoneDetails[2] >= 0 and _zoneDetails[3] == 1 then + local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - -- Trigger smoke markers - - local _pos2 = { x = _triggerZone.point.x, y = _triggerZone.point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _pos2.x, y = _alt, z = _pos2.y } - - trigger.action.smoke(_pos3, _zoneDetails[2]) - end + trigger.action.smoke(_pos3, _zoneDetails[2]) end + end - --refresh in 5 minutes - timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 300) + --refresh in 5 minutes + timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 300) end function ctld.dropSmoke(_args) + local _heli = ctld.getTransportUnit(_args[1]) - local _heli = ctld.getTransportUnit(_args[1]) + if _heli ~= nil then + local _colour = "" - if _heli ~= nil then - - local _colour = "" - - if _args[2] == trigger.smokeColor.Red then - - _colour = "RED" - elseif _args[2] == trigger.smokeColor.Blue then - - _colour = "BLUE" - elseif _args[2] == trigger.smokeColor.Green then - - _colour = "GREEN" - elseif _args[2] == trigger.smokeColor.Orange then - - _colour = "ORANGE" - end - - local _point = _heli:getPoint() - - local _pos2 = { x = _point.x, y = _point.z } - local _alt = land.getHeight(_pos2) - local _pos3 = { x = _point.x, y = _alt, z = _point.z } - - trigger.action.smoke(_pos3, _args[2]) - - trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 dropped %2 smoke.", ctld.getPlayerNameOrType(_heli), _colour), 10) + if _args[2] == trigger.smokeColor.Red then + _colour = "RED" + elseif _args[2] == trigger.smokeColor.Blue then + _colour = "BLUE" + elseif _args[2] == trigger.smokeColor.Green then + _colour = "GREEN" + elseif _args[2] == trigger.smokeColor.Orange then + _colour = "ORANGE" end + + local _point = _heli:getPoint() + + local _pos2 = { x = _point.x, y = _point.z } + local _alt = land.getHeight(_pos2) + local _pos3 = { x = _point.x, y = _alt, z = _point.z } + + trigger.action.smoke(_pos3, _args[2]) + + trigger.action.outTextForCoalition(_heli:getCoalition(), + ctld.i18n_translate("%1 dropped %2 smoke.", ctld.getPlayerNameOrType(_heli), _colour), 10) + end end function ctld.unitCanCarryVehicles(_unit) + local _type = string.lower(_unit:getTypeName()) - local _type = string.lower(_unit:getTypeName()) - - for _, _name in ipairs(ctld.vehicleTransportEnabled) do - local _nameLower = string.lower(_name) - if string.find(_type, _nameLower, 1, true) then - return true - end + for _, _name in ipairs(ctld.vehicleTransportEnabled) do + local _nameLower = string.lower(_name) + if string.find(_type, _nameLower, 1, true) then + return true end + end - return false + return false end function ctld.unitDynamicCargoCapable(_unit) - local cache = {} - local _type = string.lower(_unit:getTypeName()) - local result = cache[_type] - if result == nil then - result = false - ctld.logDebug("ctld.unitDynamicCargoCapable(_type=[%s])", ctld.p(_type)) - for _, _name in ipairs(ctld.dynamicCargoUnits) do - local _nameLower = string.lower(_name) - if string.find(_type, _nameLower, 1, true) then --string.match does not work with patterns containing '-' as it is a magic character - result = true - break - end - end - cache[_type] = result - ctld.logDebug("result=[%s]", ctld.p(result)) + local cache = {} + local _type = string.lower(_unit:getTypeName()) + local result = cache[_type] + if result == nil then + result = false + ctld.logDebug("ctld.unitDynamicCargoCapable(_type=[%s])", ctld.p(_type)) + for _, _name in ipairs(ctld.dynamicCargoUnits) do + local _nameLower = string.lower(_name) + if string.find(_type, _nameLower, 1, true) then --string.match does not work with patterns containing '-' as it is a magic character + result = true + break + end end - return result + cache[_type] = result + ctld.logDebug("result=[%s]", ctld.p(result)) + end + return result end function ctld.isJTACUnitType(_type) - if _type then - _type = string.lower(_type) - for _, _name in ipairs(ctld.jtacUnitTypes) do - local _nameLower = string.lower(_name) - if string.match(_type, _nameLower) then - return true - end - end + if _type then + _type = string.lower(_type) + for _, _name in ipairs(ctld.jtacUnitTypes) do + local _nameLower = string.lower(_name) + if string.match(_type, _nameLower) then + return true + end end - return false + end + return false end function ctld.updateZoneCounter(_index, _diff) + if ctld.pickupZones[_index] ~= nil then + ctld.pickupZones[_index][3] = ctld.pickupZones[_index][3] + _diff - if ctld.pickupZones[_index] ~= nil then - - ctld.pickupZones[_index][3] = ctld.pickupZones[_index][3] + _diff - - if ctld.pickupZones[_index][3] < 0 then - ctld.pickupZones[_index][3] = 0 - end - - if ctld.pickupZones[_index][6] ~= nil then - trigger.action.setUserFlag(ctld.pickupZones[_index][6], ctld.pickupZones[_index][3]) - end - -- env.info(ctld.pickupZones[_index][1].." = " ..ctld.pickupZones[_index][3]) + if ctld.pickupZones[_index][3] < 0 then + ctld.pickupZones[_index][3] = 0 end + + if ctld.pickupZones[_index][6] ~= nil then + trigger.action.setUserFlag(ctld.pickupZones[_index][6], ctld.pickupZones[_index][3]) + end + -- env.info(ctld.pickupZones[_index][1].." = " ..ctld.pickupZones[_index][3]) + end end function ctld.processCallback(_callbackArgs) + for _, _callback in pairs(ctld.callbacks) do + local _status, _result = pcall(function() + _callback(_callbackArgs) + end) - for _, _callback in pairs(ctld.callbacks) do - - local _status, _result = pcall(function() - - _callback(_callbackArgs) - - end) - - if (not _status) then - env.error(string.format("CTLD Callback Error: %s", _result)) - end + if (not _status) then + env.error(string.format("CTLD Callback Error: %s", _result)) end + end end - -- checks the status of all AI troop carriers and auto loads and unloads troops -- as long as the troops are on the ground function ctld.checkAIStatus() - - timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 2) + timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 2) - for _, _unitName in pairs(ctld.transportPilotNames) do - local status, error = pcall(function() + for _, _unitName in pairs(ctld.transportPilotNames) do + local status, error = pcall(function() + local _unit = ctld.getTransportUnit(_unitName) - local _unit = ctld.getTransportUnit(_unitName) + -- no player name means AI! + if _unit ~= nil and _unit:getPlayerName() == nil then + local _zone = ctld.inPickupZone(_unit) + -- env.error("Checking.. ".._unit:getName()) + if _zone.inZone == true and not ctld.troopsOnboard(_unit, true) then + -- env.error("in zone, loading.. ".._unit:getName()) - -- no player name means AI! - if _unit ~= nil and _unit:getPlayerName() == nil then - local _zone = ctld.inPickupZone(_unit) - -- env.error("Checking.. ".._unit:getName()) - if _zone.inZone == true and not ctld.troopsOnboard(_unit, true) then - -- env.error("in zone, loading.. ".._unit:getName()) - - if ctld.allowRandomAiTeamPickups == true then - -- Random troop pickup implementation - local _team = nil - if _unit:getCoalition() == 1 then - _team = math.floor((math.random(#ctld.redTeams * 100) / 100) + 1) - ctld.loadTroopsFromZone({ _unitName, true,ctld.loadableGroups[ctld.redTeams[_team]],true }) - else - _team = math.floor((math.random(#ctld.blueTeams * 100) / 100) + 1) - ctld.loadTroopsFromZone({ _unitName, true,ctld.loadableGroups[ctld.blueTeams[_team]],true }) - end - else - ctld.loadTroopsFromZone({ _unitName, true,"",true }) - end - - elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, true) then - -- env.error("in dropoff zone, unloading.. ".._unit:getName()) - ctld.unloadTroops( { _unitName, true }) - end - - if ctld.unitCanCarryVehicles(_unit) then - - if _zone.inZone == true and not ctld.troopsOnboard(_unit, false) then - - ctld.loadTroopsFromZone({ _unitName, false,"",true }) - - elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, false) then - - ctld.unloadTroops( { _unitName, false }) - end - end + if ctld.allowRandomAiTeamPickups == true then + -- Random troop pickup implementation + local _team = nil + if _unit:getCoalition() == 1 then + _team = math.floor((math.random(#ctld.redTeams * 100) / 100) + 1) + ctld.loadTroopsFromZone({ _unitName, true, ctld.loadableGroups[ctld.redTeams[_team]], true }) + else + _team = math.floor((math.random(#ctld.blueTeams * 100) / 100) + 1) + ctld.loadTroopsFromZone({ _unitName, true, ctld.loadableGroups[ctld.blueTeams[_team]], true }) end - end) - - if (not status) then - env.error(string.format("Error with ai status: %s", error), false) + else + ctld.loadTroopsFromZone({ _unitName, true, "", true }) + end + elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, true) then + -- env.error("in dropoff zone, unloading.. ".._unit:getName()) + ctld.unloadTroops({ _unitName, true }) end + + if ctld.unitCanCarryVehicles(_unit) then + if _zone.inZone == true and not ctld.troopsOnboard(_unit, false) then + ctld.loadTroopsFromZone({ _unitName, false, "", true }) + elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit, false) then + ctld.unloadTroops({ _unitName, false }) + end + end + end + end) + + if (not status) then + env.error(string.format("Error with ai status: %s", error), false) end - - + end end function ctld.getTransportLimit(_unitType) + if ctld.unitLoadLimits[_unitType] then + return ctld.unitLoadLimits[_unitType] + end - if ctld.unitLoadLimits[_unitType] then - - return ctld.unitLoadLimits[_unitType] - end - - return ctld.numberOfTroops - + return ctld.numberOfTroops end function ctld.getUnitActions(_unitType) + if ctld.unitActions[_unitType] then + return ctld.unitActions[_unitType] + end - if ctld.unitActions[_unitType] then - return ctld.unitActions[_unitType] - end - - return {crates=true,troops=true} - + return { crates = true, troops = true } end -- Adds menuitem to a human unit function ctld.addTransportF10MenuOptions(_unitName) - ctld.logDebug("ctld.addTransportF10MenuOptions(_unitName=[%s])", ctld.p(_unitName)) - local status, error = pcall(function() + ctld.logDebug("ctld.addTransportF10MenuOptions(_unitName=[%s])", ctld.p(_unitName)) + local status, error = pcall(function() + local _unit = ctld.getTransportUnit(_unitName) + ctld.logTrace("_unit = %s", ctld.p(_unit)) - local _unit = ctld.getTransportUnit(_unitName) - ctld.logTrace("_unit = %s", ctld.p(_unit)) + if _unit then + local _unitTypename = _unit:getTypeName() + local _groupId = ctld.getGroupId(_unit) + if _groupId then + ctld.logTrace("_groupId = %s", ctld.p(_groupId)) + ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) + if ctld.addedTo[tostring(_groupId)] == nil then + ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) + local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) + local _unitActions = ctld.getUnitActions(_unitTypename) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _rootPath, + ctld.checkTroopStatus, { _unitName }) + if _unitActions.troops then + local _troopCommandsPath = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Troop Transport"), _rootPath) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload / Extract Troops"), + _troopCommandsPath, ctld.unloadExtractTroops, { _unitName }) - if _unit then - local _unitTypename = _unit:getTypeName() - local _groupId = ctld.getGroupId(_unit) - if _groupId then - ctld.logTrace("_groupId = %s", ctld.p(_groupId)) - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) - if ctld.addedTo[tostring(_groupId)] == nil then - ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) - local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) - local _unitActions = ctld.getUnitActions(_unitTypename) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Check Cargo"), _rootPath, ctld.checkTroopStatus, { _unitName }) - if _unitActions.troops then - local _troopCommandsPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Troop Transport"), _rootPath) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Unload / Extract Troops"), _troopCommandsPath, ctld.unloadExtractTroops, { _unitName }) - - -- local _loadPath = missionCommands.addSubMenuForGroup(_groupId, "Load From Zone", _troopCommandsPath) - local _transportLimit = ctld.getTransportLimit(_unitTypename) - local itemNb = 0 - local menuEntries = {} - local menuPath = _troopCommandsPath - for _,_loadGroup in pairs(ctld.loadableGroups) do - if not _loadGroup.side or _loadGroup.side == _unit:getCoalition() then - -- check size & unit - if _transportLimit >= _loadGroup.total then - table.insert(menuEntries, { text = ctld.i18n_translate("Load ").._loadGroup.name, group = _loadGroup }) - 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.vehicleCommandsPath == nil then - ctld.vehicleCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) - end - - if ctld.enableRepackingVehicles then - ctld.updateRepackMenu(_unitName) - end - 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 - - 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("Vehicle / FOB Crates / 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 - 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 - end - end - for _i, _menu in ipairs(menuEntries) do - ctld.logTrace("_menu = [%s]", ctld.p(_menu)) - -- add the submenu item - itemNbSubmenu = itemNbSubmenu + 1 - if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached - _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"), _subMenuPath) - itemNbSubmenu = 1 - end - missionCommands.addCommandForGroup(_groupId, _menu.text, _subMenuPath, ctld.spawnCrate, { _unitName, _menu.crate.weight }) - 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.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 - 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 + -- local _loadPath = missionCommands.addSubMenuForGroup(_groupId, "Load From Zone", _troopCommandsPath) + local _transportLimit = ctld.getTransportLimit(_unitTypename) + local itemNb = 0 + local menuEntries = {} + local menuPath = _troopCommandsPath + for _, _loadGroup in pairs(ctld.loadableGroups) do + if not _loadGroup.side or _loadGroup.side == _unit:getCoalition() then + -- check size & unit + if _transportLimit >= _loadGroup.total then + table.insert(menuEntries, + { text = ctld.i18n_translate("Load ") .. _loadGroup.name, group = _loadGroup }) end + end end - end) - if (not status) then - ctld.logError(string.format("Error adding f10 to transport: %s", error)) + 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.vehicleCommandsPath == nil then + ctld.vehicleCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) + end + + if ctld.enableRepackingVehicles then + ctld.updateRepackMenu(_unitName) + end + 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 + + 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("Vehicle / FOB Crates / 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 + 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 + end + end + for _i, _menu in ipairs(menuEntries) do + ctld.logTrace("_menu = [%s]", ctld.p(_menu)) + -- add the submenu item + itemNbSubmenu = itemNbSubmenu + 1 + if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached + _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Next page"), _subMenuPath) + itemNbSubmenu = 1 + end + missionCommands.addCommandForGroup(_groupId, _menu.text, _subMenuPath, + ctld.spawnCrate, { _unitName, _menu.crate.weight }) + 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.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 + 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) + if (not status) then + ctld.logError(string.format("Error adding f10 to transport: %s", error)) + end end + --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) - local nextSubMenuPath = "" - local itemNbSubmenu = 0 - for i, menu in ipairs(_menuEntries) do - --ctld.logTrace("FG ctld.buildPaginatedMenu. [%s] mist.utils.tableShow(menu) = [%s]", i, mist.utils.tableShow(menu)) - if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then - menu.subMenuPath = nextSubMenuPath - end - -- add the submenu item - itemNbSubmenu = itemNbSubmenu + 1 - if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached - menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) - nextSubMenuPath = menu.subMenuPath - itemNbSubmenu = 1 - end - menu.menuArgsTable.subMenuPath = menu.subMenuPath - menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu - missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) + ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) + local nextSubMenuPath = "" + local itemNbSubmenu = 0 + for i, menu in ipairs(_menuEntries) do + --ctld.logTrace("FG ctld.buildPaginatedMenu. [%s] mist.utils.tableShow(menu) = [%s]", i, mist.utils.tableShow(menu)) + if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then + menu.subMenuPath = nextSubMenuPath end + -- add the submenu item + itemNbSubmenu = itemNbSubmenu + 1 + if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached + menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), + menu.subMenuPath) + nextSubMenuPath = menu.subMenuPath + itemNbSubmenu = 1 + end + menu.menuArgsTable.subMenuPath = menu.subMenuPath + menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu + missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, + menu.menuArgsTable) + end end + --****************************************************************************************************** function ctld.updateRepackMenu(_playerUnitName) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then - local _unitTypename = playerUnit:getTypeName() - local _groupId = ctld.getGroupId(playerUnit) + local _unitTypename = playerUnit:getTypeName() + local _groupId = ctld.getGroupId(playerUnit) if ctld.enableRepackingVehicles then - local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) + local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, + ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then missionCommands.removeItemForGroup(1, ctld.vehicleCommandsPath) -- remove the old menu - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) - + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) local menuEntries = {} local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) - RepackCommandsPath[#RepackCommandsPath+1] = ctld.i18n_translate("Repack Vehicles") + RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") for _, _vehicle in pairs(repackableVehicles) do - table.insert(menuEntries, { text = ctld.i18n_translate("repack ").._vehicle.unit, - groupId = _groupId, - subMenuPath = RepackCommandsPath, - menuFunction = ctld.repackVehicleRequest, - menuArgsTable = {_vehicle, _playerUnitName} }) + table.insert(menuEntries, { + text = ctld.i18n_translate("repack ") .. _vehicle.unit, + groupId = _groupId, + subMenuPath = RepackCommandsPath, + menuFunction = ctld.repackVehicleRequest, + menuArgsTable = { _vehicle, _playerUnitName } + }) end ctld.buildPaginatedMenu(menuEntries) end end end end + --****************************************************************************************************** -function ctld.autoUpdateRepackMenu() - timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) +function ctld.autoUpdateRepackMenu() -- auto update repack menus for each transport unit + --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) if ctld.enableRepackingVehicles then for _, _unitName in pairs(ctld.transportPilotNames) do - local status, error = pcall(function() - local _unit = ctld.getTransportUnit(_unitName) - if _unit then - local _unitTypename = _unit:getTypeName() - local _groupId = ctld.getGroupId(_unit) - if _groupId then - --if ctld.addedTo[tostring(_groupId)] == nil then - ctld.updateRepackMenu(_unitName) - --end + local status, error = pcall( + function() + local _unit = ctld.getTransportUnit(_unitName) + if _unit then + -- if transport unit landed => update repack menus + if (ctld.inAir(_unit) == false or (ctld.heightDiff(_unit) <= 0.1 + 3.0 and mist.vec.mag(_unit:getVelocity()) < 0.1)) then + local _unitTypename = _unit:getTypeName() + local _groupId = ctld.getGroupId(_unit) + if _groupId then + if ctld.addedTo[tostring(_groupId)] ~= nil then + ctld.updateRepackMenu(_unitName) + end + end + end end - end - end) + end) if (not status) then - env.error(string.format("Error with repack menu: %s", error), false) + env.error(string.format("Error in ctld.autoUpdateRepackMenu : %s", error), false) end end end end + --****************************************************************************************************** function ctld.addOtherF10MenuOptions() - ctld.logDebug("ctld.addOtherF10MenuOptions") - -- reschedule every 10 seconds - timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 10) - local status, error = pcall(function() + ctld.logDebug("ctld.addOtherF10MenuOptions") + -- reschedule every 10 seconds + timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 10) + local status, error = pcall(function() + -- now do any player controlled aircraft that ARENT transport units + if ctld.enabledRadioBeaconDrop then + -- get all BLUE players + ctld.addRadioListCommand(2) - -- now do any player controlled aircraft that ARENT transport units - if ctld.enabledRadioBeaconDrop then - -- get all BLUE players - ctld.addRadioListCommand(2) - - -- get all RED players - ctld.addRadioListCommand(1) - end - - if ctld.JTAC_jtacStatusF10 then - ctld.addJTACRadioCommand(2) -- get all BLUE players - ctld.addJTACRadioCommand(1) -- get all RED players - end - - if ctld.reconF10Menu then - ctld.addReconRadioCommand(2) -- get all BLUE players - ctld.addReconRadioCommand(1) -- get all RED players - end - end) - - if (not status) then - env.error(string.format("Error adding f10 to other players: %s", error), false) + -- get all RED players + ctld.addRadioListCommand(1) end + + if ctld.JTAC_jtacStatusF10 then + ctld.addJTACRadioCommand(2) -- get all BLUE players + ctld.addJTACRadioCommand(1) -- get all RED players + end + + if ctld.reconF10Menu then + ctld.addReconRadioCommand(2) -- get all BLUE players + ctld.addReconRadioCommand(1) -- get all RED players + end + end) + + if (not status) then + env.error(string.format("Error adding f10 to other players: %s", error), false) + end end --add to all players that arent transport function ctld.addRadioListCommand(_side) + local _players = coalition.getPlayers(_side) - local _players = coalition.getPlayers(_side) + if _players ~= nil then + for _, _playerUnit in pairs(_players) do + local _groupId = ctld.getGroupId(_playerUnit) - if _players ~= nil then - - for _, _playerUnit in pairs(_players) do - - local _groupId = ctld.getGroupId(_playerUnit) - - if _groupId then - - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) - if ctld.addedTo[tostring(_groupId)] == nil then - ctld.logTrace("adding List Radio Beacons for _groupId = %s", ctld.p(_groupId)) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Radio Beacons"), nil, ctld.listRadioBeacons, { _playerUnit:getName() }) - ctld.addedTo[tostring(_groupId)] = true - end - end + if _groupId then + ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) + if ctld.addedTo[tostring(_groupId)] == nil then + ctld.logTrace("adding List Radio Beacons for _groupId = %s", ctld.p(_groupId)) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Radio Beacons"), nil, + ctld.listRadioBeacons, { _playerUnit:getName() }) + ctld.addedTo[tostring(_groupId)] = true end + end end + end end function ctld.addJTACRadioCommand(_side) + local _players = coalition.getPlayers(_side) - local _players = coalition.getPlayers(_side) + if _players ~= nil then + for _, _playerUnit in pairs(_players) do + local _groupId = ctld.getGroupId(_playerUnit) - if _players ~= nil then + if _groupId then + local newGroup = false + if ctld.jtacRadioAdded[tostring(_groupId)] == nil then + ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", + ctld.p(_playerUnit:getName())) + newGroup = true + local JTACpath = missionCommands.addSubMenuForGroup(_groupId, ctld.jtacMenuName) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("JTAC Status"), JTACpath, + ctld.getJTACStatus, { _playerUnit:getName() }) + ctld.jtacRadioAdded[tostring(_groupId)] = true + end - for _, _playerUnit in pairs(_players) do + --fetch the time to check for a regular refresh + local time = timer.getTime() - local _groupId = ctld.getGroupId(_playerUnit) + --depending on the delay, this part of the radio menu will be refreshed less often or as often as the static JTAC status command, this is for better reliability for the user when navigating through the menus. New groups will get the lists regardless and if a new JTAC is added all lists will be refreshed regardless of the delay. + if ctld.jtacLastRadioRefresh + ctld.jtacRadioRefreshDelay <= time or ctld.refreshJTACmenu[_side] or newGroup then + ctld.jtacLastRadioRefresh = time - if _groupId then + --build the path to the CTLD JTAC menu + local jtacCurrentPagePath = { [1] = ctld.jtacMenuName } + --build the path for the NextPage submenu on the first page of the CTLD JTAC menu + local NextPageText = "Next Page" + local MainNextPagePath = { [1] = ctld.jtacMenuName, [2] = NextPageText } + --remove it along with everything that's in it + missionCommands.removeItemForGroup(_groupId, MainNextPagePath) - local newGroup = false - if ctld.jtacRadioAdded[tostring(_groupId)] == nil then - ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", ctld.p(_playerUnit:getName())) - newGroup = true - local JTACpath = missionCommands.addSubMenuForGroup(_groupId, ctld.jtacMenuName) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("JTAC Status"), JTACpath, ctld.getJTACStatus, { _playerUnit:getName() }) - ctld.jtacRadioAdded[tostring(_groupId)] = true + --counter to know when to add the next page submenu to fit all of the JTAC group submenus + local jtacCounter = 0 + + for _jtacGroupName, jtacUnit in pairs(ctld.jtacUnits) do + --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 + if jtacCoalition and jtacCoalition == _side then + --only bother removing the submenus on the first page of the CTLD JTAC menu as the other pages were deleted entirely above + 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]))) + + local jtacActionMenu = false + for _, _specialOptionTable in pairs(ctld.jtacSpecialOptions) do + if _specialOptionTable.globalToggle then + jtacActionMenu = true + break end + end - --fetch the time to check for a regular refresh - local time = timer.getTime() + --if JTAC has at least one other target in sight or (if special options are available (NOTE : accessed through the JTAC's own menu also) and the JTAC has at least one target) + if (ctld.jtacTargetsList[_jtacGroupName] and #ctld.jtacTargetsList[_jtacGroupName] >= 1) or (ctld.jtacCurrentTargets[_jtacGroupName] and jtacActionMenu) then + local jtacGroupSubMenuName = string.format(_jtacGroupName .. " Selection") - --depending on the delay, this part of the radio menu will be refreshed less often or as often as the static JTAC status command, this is for better reliability for the user when navigating through the menus. New groups will get the lists regardless and if a new JTAC is added all lists will be refreshed regardless of the delay. - if ctld.jtacLastRadioRefresh + ctld.jtacRadioRefreshDelay <= time or ctld.refreshJTACmenu[_side] or newGroup then + jtacCounter = jtacCounter + 1 + --F2 through F10 makes 9 entries possible per page, with one being the NextMenu submenu. F1 is taken by JTAC status entry. + if jtacCounter % 9 == 0 then + --recover the path to the current page with space available for JTAC group submenus + jtacCurrentPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, + jtacCurrentPagePath) + 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.jtacLastRadioRefresh = time + --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]) - --build the path to the CTLD JTAC menu - local jtacCurrentPagePath = {[1]=ctld.jtacMenuName} - --build the path for the NextPage submenu on the first page of the CTLD JTAC menu - local NextPageText = "Next Page" - local MainNextPagePath = {[1]=ctld.jtacMenuName, [2]=NextPageText} - --remove it along with everything that's in it - missionCommands.removeItemForGroup(_groupId, MainNextPagePath) + --counter to know when to add the next page submenu to fit all of the targets in the JTAC's group submenu. SMay not actually start at 0 due to static items being present on the first page + local itemCounter = 0 + local jtacSpecialOptPagePath = nil - --counter to know when to add the next page submenu to fit all of the JTAC group submenus - local jtacCounter = 0 + if jtacActionMenu then + --special options + local SpecialOptionsCounter = 0 - for _jtacGroupName,jtacUnit in pairs(ctld.jtacUnits) do - --ctld.logTrace(string.format("JTAC - MENU - [%s] - processing menu", ctld.p(_jtacGroupName))) + for _, _specialOption in pairs(ctld.jtacSpecialOptions) do + if _specialOption.globalToggle then + if not jtacSpecialOptPagePath then + itemCounter = itemCounter + + 1 --one item is added to the first JTAC target page + jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, + ctld.i18n_translate("Actions"), jtacTargetPagePath) + end - --if the JTAC is on the same team as the group being considered - local jtacCoalition = ctld.jtacUnits[_jtacGroupName].side - if jtacCoalition and jtacCoalition == _side then - --only bother removing the submenus on the first page of the CTLD JTAC menu as the other pages were deleted entirely above - 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]))) + SpecialOptionsCounter = SpecialOptionsCounter + 1 - local jtacActionMenu = false - for _,_specialOptionTable in pairs(ctld.jtacSpecialOptions) do - if _specialOptionTable.globalToggle then - jtacActionMenu = true - break - end - end + if SpecialOptionsCounter % 10 == 0 then + jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, + NextPageText, jtacSpecialOptPagePath) + SpecialOptionsCounter = SpecialOptionsCounter + 1 --Added Next Page item + end - --if JTAC has at least one other target in sight or (if special options are available (NOTE : accessed through the JTAC's own menu also) and the JTAC has at least one target) - if (ctld.jtacTargetsList[_jtacGroupName] and #ctld.jtacTargetsList[_jtacGroupName] >= 1) or (ctld.jtacCurrentTargets[_jtacGroupName] and jtacActionMenu) then - - local jtacGroupSubMenuName = string.format(_jtacGroupName .. " Selection") - - jtacCounter = jtacCounter + 1 - --F2 through F10 makes 9 entries possible per page, with one being the NextMenu submenu. F1 is taken by JTAC status entry. - if jtacCounter % 9 == 0 then - --recover the path to the current page with space available for JTAC group submenus - jtacCurrentPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacCurrentPagePath) - 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]))) - - --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]) - - --counter to know when to add the next page submenu to fit all of the targets in the JTAC's group submenu. SMay not actually start at 0 due to static items being present on the first page - local itemCounter = 0 - local jtacSpecialOptPagePath = nil - - if jtacActionMenu then - --special options - local SpecialOptionsCounter = 0 - - for _,_specialOption in pairs(ctld.jtacSpecialOptions) do - if _specialOption.globalToggle then - - if not jtacSpecialOptPagePath then - itemCounter = itemCounter + 1 --one item is added to the first JTAC target page - jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Actions"), jtacTargetPagePath) - end - - SpecialOptionsCounter = SpecialOptionsCounter+1 - - if SpecialOptionsCounter%10 == 0 then - jtacSpecialOptPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacSpecialOptPagePath) - SpecialOptionsCounter = SpecialOptionsCounter+1 --Added Next Page item - end - - if _specialOption.jtacs then - if _specialOption.jtacs[_jtacGroupName] then - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("DISABLE ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = false}) - else - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("ENABLE ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = true}) - end - else - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("REQUEST ") .. _specialOption.message, jtacSpecialOptPagePath, _specialOption.setter, {jtacGroupName = _jtacGroupName, value = false}) --value is not used here - end - end - end - end - - if #ctld.jtacTargetsList[_jtacGroupName] >= 1 then - --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, ctld.i18n_translate("Reset TGT Selection"), jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = nil}) - - itemCounter = itemCounter + 1 --one item is added to the first JTAC target page - - --indicator table to know which unitType was already added to the radio submenu - local typeNameList = {} - for _,target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do - local targetName = target.unit:getName() - --check if the jtac has a current target before filtering it out if possible - if (ctld.jtacCurrentTargets[_jtacGroupName] and targetName ~= ctld.jtacCurrentTargets[_jtacGroupName].name) then - local targetType_name = target.unit:getTypeName() - - if targetType_name then - if typeNameList[targetType_name] then - typeNameList[targetType_name].amount = typeNameList[targetType_name].amount + 1 - else - typeNameList[targetType_name] = {} - typeNameList[targetType_name].targetName = targetName --store the first targetName - typeNameList[targetType_name].amount = 1 - end - end - end - end - - for typeName,info in pairs(typeNameList) do - local amount = info.amount - local targetName = info.targetName - itemCounter = itemCounter + 1 - - --F1 through F10 makes 10 entries possible per page, with one being the NextMenu submenu. - if itemCounter%10 == 0 then - jtacTargetPagePath = missionCommands.addSubMenuForGroup(_groupId, NextPageText, jtacTargetPagePath) - itemCounter = itemCounter + 1 --added the next page item - end - - missionCommands.addCommandForGroup(_groupId, string.format(typeName .. "(" .. amount .. ")"), jtacTargetPagePath, ctld.setJTACTarget, {jtacGroupName = _jtacGroupName, targetName = targetName}) - end - end - end + if _specialOption.jtacs then + if _specialOption.jtacs[_jtacGroupName] then + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("DISABLE ") .. _specialOption.message, + jtacSpecialOptPagePath, _specialOption.setter, + { jtacGroupName = _jtacGroupName, value = false }) + else + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("ENABLE ") .. _specialOption.message, + jtacSpecialOptPagePath, _specialOption.setter, + { jtacGroupName = _jtacGroupName, value = true }) end + else + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("REQUEST ") .. _specialOption.message, + jtacSpecialOptPagePath, _specialOption.setter, + { jtacGroupName = _jtacGroupName, value = false }) --value is not used here + end end + end end - end - end - if ctld.refreshJTACmenu[_side] then - ctld.refreshJTACmenu[_side] = false + if #ctld.jtacTargetsList[_jtacGroupName] >= 1 then + --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, + ctld.i18n_translate("Reset TGT Selection"), jtacTargetPagePath, + ctld.setJTACTarget, { jtacGroupName = _jtacGroupName, targetName = nil }) + + itemCounter = itemCounter + 1 --one item is added to the first JTAC target page + + --indicator table to know which unitType was already added to the radio submenu + local typeNameList = {} + for _, target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do + local targetName = target.unit:getName() + --check if the jtac has a current target before filtering it out if possible + if (ctld.jtacCurrentTargets[_jtacGroupName] and targetName ~= ctld.jtacCurrentTargets[_jtacGroupName].name) then + local targetType_name = target.unit:getTypeName() + + if targetType_name then + if typeNameList[targetType_name] then + typeNameList[targetType_name].amount = typeNameList[targetType_name] + .amount + 1 + else + typeNameList[targetType_name] = {} + typeNameList[targetType_name].targetName = + targetName --store the first targetName + typeNameList[targetType_name].amount = 1 + end + end + end + end + + for typeName, info in pairs(typeNameList) do + local amount = info.amount + local targetName = info.targetName + itemCounter = itemCounter + 1 + + --F1 through F10 makes 10 entries possible per page, with one being the NextMenu submenu. + if itemCounter % 10 == 0 then + jtacTargetPagePath = missionCommands.addSubMenuForGroup(_groupId, + NextPageText, jtacTargetPagePath) + itemCounter = itemCounter + 1 --added the next page item + end + + missionCommands.addCommandForGroup(_groupId, + string.format(typeName .. "(" .. amount .. ")"), jtacTargetPagePath, + ctld.setJTACTarget, { jtacGroupName = _jtacGroupName, targetName = targetName }) + end + end + end + end + end end + end end + + if ctld.refreshJTACmenu[_side] then + ctld.refreshJTACmenu[_side] = false + end + end end function ctld.getGroupId(_unit) + local _unitDB = mist.DBs.unitsById[tonumber(_unit:getID())] + if _unitDB ~= nil and _unitDB.groupId then + return _unitDB.groupId + end - local _unitDB = mist.DBs.unitsById[tonumber(_unit:getID())] - if _unitDB ~= nil and _unitDB.groupId then - return _unitDB.groupId - end - - return nil + return nil end --get distance in meters assuming a Flat world function ctld.getDistance(_point1, _point2) + local xUnit = _point1.x + local yUnit = _point1.z + local xZone = _point2.x + local yZone = _point2.z - local xUnit = _point1.x - local yUnit = _point1.z - local xZone = _point2.x - local yZone = _point2.z + local xDiff = xUnit - xZone + local yDiff = yUnit - yZone - local xDiff = xUnit - xZone - local yDiff = yUnit - yZone - - return math.sqrt(xDiff * xDiff + yDiff * yDiff) + return math.sqrt(xDiff * xDiff + yDiff * yDiff) end - ------------ JTAC ----------- ctld.jtacMenuName = "JTAC" --name of the CTLD JTAC radio menu ctld.jtacLaserPoints = {} ctld.jtacIRPoints = {} ctld.jtacSmokeMarks = {} -ctld.jtacUnits = {} -- list of JTAC units for f10 command -ctld.jtacStop = {} -- jtacs to tell to stop lasing +ctld.jtacUnits = {} -- list of JTAC units for f10 command +ctld.jtacStop = {} -- jtacs to tell to stop lasing ctld.jtacCurrentTargets = {} -ctld.jtacTargetsList = {} --current available targets to each JTAC for lasing (targets from other JTACs are filtered out). Contains DCS unit objects with their methods and the distance to the JTAC {unit, dist} +ctld.jtacTargetsList = {} --current available targets to each JTAC for lasing (targets from other JTACs are filtered out). Contains DCS unit objects with their methods and the distance to the JTAC {unit, dist} ctld.jtacSelectedTarget = {} --currently user selected target if it contains a unit's name, otherwise contains 1 or nil (if not initialized) -ctld.jtacSpecialOptions = { --list which contains the status of special options for each jtac, ordered for them to show up in the correct order in the corresponding radio menu - standbyMode = { --#1 - globalToggle = ctld.JTAC_allowStandbyMode; - message = "Standby Mode"; - setter = nil; --ctld.setStdbMode, will be set after declaration of said function - jtacs = { - --enable flag for each JTAC - }; - }; --disable designation by the JTAC - smokeMarker = { --#4 - globalToggle = ctld.JTAC_allowSmokeRequest; - message = "Smoke on TGT"; - setter = nil; --ctld.setSmokeOnTarget - }; --smoke marker on target - laseSpotCorrections = { --#2 - globalToggle = ctld.JTAC_laseSpotCorrections; - message = "Speed Corrections"; - setter = nil; --ctld.setLaseCompensation - jtacs = { - --enable flag for each JTAC - }; - }; --target speed and wind compensation for laser spot - _9Line = { --#3 - globalToggle = ctld.JTAC_allow9Line; - message = "9 Line"; - setter = nil; --ctld.setJTAC9Line - }; --9Line message for JTAC +ctld.jtacSpecialOptions = { --list which contains the status of special options for each jtac, ordered for them to show up in the correct order in the corresponding radio menu + standbyMode = { --#1 + globalToggle = ctld.JTAC_allowStandbyMode, + message = "Standby Mode", + setter = nil, --ctld.setStdbMode, will be set after declaration of said function + jtacs = { + --enable flag for each JTAC + }, + }, --disable designation by the JTAC + smokeMarker = { --#4 + globalToggle = ctld.JTAC_allowSmokeRequest, + message = "Smoke on TGT", + setter = nil, --ctld.setSmokeOnTarget + }, --smoke marker on target + laseSpotCorrections = { --#2 + globalToggle = ctld.JTAC_laseSpotCorrections, + message = "Speed Corrections", + setter = nil, --ctld.setLaseCompensation + jtacs = { + --enable flag for each JTAC + }, + }, --target speed and wind compensation for laser spot + _9Line = { --#3 + globalToggle = ctld.JTAC_allow9Line, + message = "9 Line", + setter = nil, --ctld.setJTAC9Line + }, --9Line message for JTAC } -ctld.jtacRadioAdded = {} --keeps track of who's had the radio command added -ctld.jtacGroupSubMenuPath = {} --keeps track of which submenu contains each JTAC's target selection menu -ctld.jtacRadioRefreshDelay = 120 --determines how often in seconds the dynamic parts of the jtac radio menu (target lists) will be refreshed -ctld.jtacLastRadioRefresh = 0 -- time at which the target lists were refreshed for everyone at least -ctld.refreshJTACmenu = {} --indicator to know when a new JTAC is added to a coalition in order to rebuild the corresponding target lists +ctld.jtacRadioAdded = {} --keeps track of who's had the radio command added +ctld.jtacGroupSubMenuPath = {} --keeps track of which submenu contains each JTAC's target selection menu +ctld.jtacRadioRefreshDelay = 120 --determines how often in seconds the dynamic parts of the jtac radio menu (target lists) will be refreshed +ctld.jtacLastRadioRefresh = 0 -- time at which the target lists were refreshed for everyone at least +ctld.refreshJTACmenu = {} --indicator to know when a new JTAC is added to a coalition in order to rebuild the corresponding target lists ctld.jtacGeneratedLaserCodes = {} -- keeps track of generated codes, cycles when they run out ctld.jtacLaserPointCodes = {} ctld.jtacRadioData = {} @@ -6520,1059 +6367,1050 @@ ctld.jtacRadioData = {} By waiting a bit, the group gets populated before JTACAutoLase is called, hence avoiding a trip to cleanupJTAC. ]] function ctld.JTACStart(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio) - mist.scheduleFunction(ctld.JTACAutoLase, {_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio}, timer.getTime()+1) + mist.scheduleFunction(ctld.JTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, + timer.getTime() + 1) end function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio) - ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), ctld.p(_laserCode))) + ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), + ctld.p(_laserCode))) - local _radio = _radio - if not _radio then - _radio = {} - if _laserCode then - local _laserCode = tonumber(_laserCode) - if _laserCode and _laserCode >= 1111 and _laserCode <= 1688 then - local _laserB = math.floor((_laserCode - 1000)/100) - local _laserCD = _laserCode - 1000 - _laserB*100 - local _frequency = tostring(30+_laserB+_laserCD*0.05) - ctld.logTrace(string.format("_laserB=%s", ctld.p(_laserB))) - ctld.logTrace(string.format("_laserCD=%s", ctld.p(_laserCD))) - ctld.logTrace(string.format("_frequency=%s", ctld.p(_frequency))) - _radio.freq = _frequency - _radio.mod = "fm" - end + local _radio = _radio + if not _radio then + _radio = {} + if _laserCode then + local _laserCode = tonumber(_laserCode) + if _laserCode and _laserCode >= 1111 and _laserCode <= 1688 then + local _laserB = math.floor((_laserCode - 1000) / 100) + local _laserCD = _laserCode - 1000 - _laserB * 100 + local _frequency = tostring(30 + _laserB + _laserCD * 0.05) + ctld.logTrace(string.format("_laserB=%s", ctld.p(_laserB))) + ctld.logTrace(string.format("_laserCD=%s", ctld.p(_laserCD))) + ctld.logTrace(string.format("_frequency=%s", ctld.p(_frequency))) + _radio.freq = _frequency + _radio.mod = "fm" + end + end + end + + if _radio and not _radio.name then + _radio.name = _jtacGroupName + end + + if ctld.jtacStop[_jtacGroupName] == true then + ctld.jtacStop[_jtacGroupName] = nil -- allow it to be started again + ctld.cleanupJTAC(_jtacGroupName) + return + end + + if _lock == nil then + _lock = ctld.JTAC_lock + end + + ctld.jtacLaserPointCodes[_jtacGroupName] = _laserCode + ctld.jtacRadioData[_jtacGroupName] = _radio + + local _jtacGroup = ctld.getGroup(_jtacGroupName) + local _jtacUnit + + if _jtacGroup == nil or #_jtacGroup == 0 then + --check not in a heli + if ctld.inTransitTroops then + for _, _onboard in pairs(ctld.inTransitTroops) do + if _onboard ~= nil then + if _onboard.troops ~= nil and _onboard.troops.groupName ~= nil and _onboard.troops.groupName == _jtacGroupName then + --jtac soldier being transported by heli + ctld.cleanupJTAC(_jtacGroupName) + + ctld.logTrace(string.format( + "JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", + ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) + timer.scheduleFunction(ctld.timerJTACAutoLase, + { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) + return + end + + if _onboard.vehicles ~= nil and _onboard.vehicles.groupName ~= nil and _onboard.vehicles.groupName == _jtacGroupName then + --jtac vehicle being transported by heli + ctld.cleanupJTAC(_jtacGroupName) + + ctld.logTrace(string.format( + "JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", + ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) + timer.scheduleFunction(ctld.timerJTACAutoLase, + { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) + return + end end + end end - if _radio and not _radio.name then - _radio.name = _jtacGroupName + if ctld.jtacUnits[_jtacGroupName] ~= nil then + ctld.notifyCoalition(ctld.i18n_translate("JTAC Group %1 KIA!", _jtacGroupName), 10, + ctld.jtacUnits[_jtacGroupName].side, _radio) end - if ctld.jtacStop[_jtacGroupName] == true then - ctld.jtacStop[_jtacGroupName] = nil -- allow it to be started again - ctld.cleanupJTAC(_jtacGroupName) - return - end + --remove from list + ctld.cleanupJTAC(_jtacGroupName) - if _lock == nil then - _lock = ctld.JTAC_lock - end + return + else + _jtacUnit = _jtacGroup[1] + local _jtacCoalition = _jtacUnit:getCoalition() + --add to list + ctld.jtacUnits[_jtacGroupName] = { name = _jtacUnit:getName(), side = _jtacCoalition, radio = _radio } - ctld.jtacLaserPointCodes[_jtacGroupName] = _laserCode - ctld.jtacRadioData[_jtacGroupName] = _radio + --Targets list, special options and Selected target initialization + if not ctld.jtacTargetsList[_jtacGroupName] then + --Target list + ctld.jtacTargetsList[_jtacGroupName] = {} + if _jtacCoalition then ctld.refreshJTACmenu[_jtacCoalition] = true end - local _jtacGroup = ctld.getGroup(_jtacGroupName) - local _jtacUnit - - if _jtacGroup == nil or #_jtacGroup == 0 then - - --check not in a heli - if ctld.inTransitTroops then - for _, _onboard in pairs(ctld.inTransitTroops) do - if _onboard ~= nil then - if _onboard.troops ~= nil and _onboard.troops.groupName ~= nil and _onboard.troops.groupName == _jtacGroupName then - - --jtac soldier being transported by heli - ctld.cleanupJTAC(_jtacGroupName) - - ctld.logTrace(string.format("JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) - return - end - - if _onboard.vehicles ~= nil and _onboard.vehicles.groupName ~= nil and _onboard.vehicles.groupName == _jtacGroupName then - --jtac vehicle being transported by heli - ctld.cleanupJTAC(_jtacGroupName) - - ctld.logTrace(string.format("JTAC - LASE - [%s] - in transport, waiting - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(10), ctld.p(timer.getTime() + 10))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 10) - return - end - end - end + --Special Options + for _, _specialOption in pairs(ctld.jtacSpecialOptions) do + if _specialOption.jtacs then + _specialOption.jtacs[_jtacGroupName] = false end + end + end - if ctld.jtacUnits[_jtacGroupName] ~= nil then - ctld.notifyCoalition(ctld.i18n_translate("JTAC Group %1 KIA!", _jtacGroupName), 10, ctld.jtacUnits[_jtacGroupName].side, _radio) + if not ctld.jtacSelectedTarget[_jtacGroupName] then + ctld.jtacSelectedTarget[_jtacGroupName] = 1 + end + + -- work out smoke colour + if _colour == nil then + if _jtacUnit:getCoalition() == 1 then + _colour = ctld.JTAC_smokeColour_RED + else + _colour = ctld.JTAC_smokeColour_BLUE + end + end + + + if _smoke == nil then + if _jtacUnit:getCoalition() == 1 then + _smoke = ctld.JTAC_smokeOn_RED + else + _smoke = ctld.JTAC_smokeOn_BLUE + end + end + end + + + -- search for current unit + + if _jtacUnit:isActive() == false then + ctld.cleanupJTAC(_jtacGroupName) + + ctld.logTrace(string.format("JTAC - LASE - [%s] - not active, scheduling JTACAutoLase in 30s at %s", + ctld.p(_jtacGroupName), ctld.p(timer.getTime() + 30))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, + timer.getTime() + 30) + + return + end + + local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) + --update targets list and store the next potential target if the selected one was lost + local _defaultEnemyUnit = ctld.findNearestVisibleEnemy(_jtacUnit, _lock) + + -- if the JTAC sees a unit and a target was selected by users but is not the current unit, check if the selected target is in the targets list, if it is, then it's been reacquired + if _enemyUnit and ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 and ctld.jtacSelectedTarget[_jtacGroupName] ~= _enemyUnit:getName() then + for _, target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do + if target then + local targetUnit = target.unit + local targetName = targetUnit:getName() + + if ctld.jtacSelectedTarget[_jtacGroupName] == targetName then + ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = targetUnit:getTypeName(), unitId = + targetUnit:getID() } + _enemyUnit = targetUnit + + local message = ctld.i18n_translate("%1, selected target reacquired, %2", _jtacGroupName, + _enemyUnit:getTypeName()) + local fullMessage = message .. + ctld.i18n_translate(". CODE: %1. POSITION: %2", _laserCode, ctld.getPositionString(_enemyUnit)) + ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) end + end + end + end - --remove from list - ctld.cleanupJTAC(_jtacGroupName) + local targetDestroyed = false + local targetLost = false + local wasSelected = false - return + if _enemyUnit == nil and ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then + local _tempUnitInfo = ctld.jtacCurrentTargets[_jtacGroupName] + + -- env.info("TEMP UNIT INFO: " .. tempUnitInfo.name .. " " .. tempUnitInfo.unitType) + + local _tempUnit = Unit.getByName(_tempUnitInfo.name) + + wasSelected = (ctld.jtacCurrentTargets[_jtacGroupName].name == ctld.jtacSelectedTarget[_jtacGroupName]) + + if _tempUnit ~= nil and _tempUnit:getLife() > 0 and _tempUnit:isActive() == true then + targetLost = true else - - _jtacUnit = _jtacGroup[1] - local _jtacCoalition = _jtacUnit:getCoalition() - --add to list - ctld.jtacUnits[_jtacGroupName] = { name = _jtacUnit:getName(), side = _jtacCoalition, radio = _radio } - - --Targets list, special options and Selected target initialization - if not ctld.jtacTargetsList[_jtacGroupName] then - --Target list - ctld.jtacTargetsList[_jtacGroupName] = {} - if _jtacCoalition then ctld.refreshJTACmenu[_jtacCoalition] = true end - - --Special Options - for _,_specialOption in pairs(ctld.jtacSpecialOptions) do - if _specialOption.jtacs then - _specialOption.jtacs[_jtacGroupName] = false - end - end - end - - if not ctld.jtacSelectedTarget[_jtacGroupName] then - ctld.jtacSelectedTarget[_jtacGroupName] = 1 - end - - -- work out smoke colour - if _colour == nil then - - if _jtacUnit:getCoalition() == 1 then - _colour = ctld.JTAC_smokeColour_RED - else - _colour = ctld.JTAC_smokeColour_BLUE - end - end - - - if _smoke == nil then - - if _jtacUnit:getCoalition() == 1 then - _smoke = ctld.JTAC_smokeOn_RED - else - _smoke = ctld.JTAC_smokeOn_BLUE - end - end + targetDestroyed = true + ctld.jtacSelectedTarget[_jtacGroupName] = 1 end + --remove from smoke list + ctld.jtacSmokeMarks[_tempUnitInfo.name] = nil - -- search for current unit + -- JTAC Unit: resume his route ------------ + trigger.action.groupContinueMoving(Group.getByName(_jtacGroupName)) - if _jtacUnit:isActive() == false then + -- remove from target list + ctld.jtacCurrentTargets[_jtacGroupName] = nil - ctld.cleanupJTAC(_jtacGroupName) + --stop lasing + ctld.cancelLase(_jtacGroupName) + end - ctld.logTrace(string.format("JTAC - LASE - [%s] - not active, scheduling JTACAutoLase in 30s at %s", ctld.p(_jtacGroupName), ctld.p(timer.getTime() + 30))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 30) - return + if _enemyUnit == nil then + if _defaultEnemyUnit ~= nil then + -- store current target for easy lookup + ctld.jtacCurrentTargets[_jtacGroupName] = { name = _defaultEnemyUnit:getName(), unitType = _defaultEnemyUnit + :getTypeName(), unitId = _defaultEnemyUnit:getID() } + + --add check for lasing or not + local action = ctld.i18n_translate("new target, ") + + if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then + action = ctld.i18n_translate("standing by on %1", action) + else + action = ctld.i18n_translate("lasing %1", action) + end + + if wasSelected and targetLost then + action = ctld.i18n_translate(", temporarily %1", action) + else + action = ", " .. action + end + + if targetLost then + action = ctld.i18n_translate("target lost") .. action + elseif targetDestroyed then + action = ctld.i18n_translate("target destroyed") .. action + end + + if wasSelected then + action = ctld.i18n_translate(", selected %1", action) + elseif targetLost or targetDestroyed then + action = ", " .. action + end + wasSelected = false + targetDestroyed = false + targetLost = false + + local message = _jtacGroupName .. action .. _defaultEnemyUnit:getTypeName() + local fullMessage = message .. + '. CODE: ' .. _laserCode .. ". POSITION: " .. ctld.getPositionString(_defaultEnemyUnit) + ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) + + -- JTAC Unit stop his route ----------------- + trigger.action.groupStopMoving(Group.getByName(_jtacGroupName)) -- stop JTAC + + -- create smoke + if _smoke == true then + --create first smoke + ctld.createSmokeMarker(_defaultEnemyUnit, _colour) + end + end + end + + if _enemyUnit ~= nil and not ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then + local refreshDelay = 15 --delay in between JTACAutoLase scheduled calls when a target is tracked + local targetSpeedVec = _enemyUnit:getVelocity() + local targetSpeed = math.sqrt(targetSpeedVec.x ^ 2 + targetSpeedVec.y ^ 2 + targetSpeedVec.z ^ 2) + local maxUpdateDist = 5 --maximum distance the unit will be allowed to travel before the lase spot is updated again + ctld.logTrace(string.format("targetSpeed=%s", ctld.p(targetSpeed))) + + ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) + + --if the target is going sufficiently fast for it to wander off futher than the maxUpdateDist, schedule laseUnit calls to update the lase spot only (we consider that the unit lives and drives on between JTACAutoLase calls) + if targetSpeed >= maxUpdateDist / refreshDelay then + local updateTimeStep = maxUpdateDist / + targetSpeed --calculate the time step so that the target is never more than maxUpdateDist from it's last lased position + ctld.logTrace(string.format( + "JTAC - LASE - [%s] - target is moving at %s m/s, schedulting lasing steps every %ss", ctld.p(_jtacGroupName), + ctld.p(targetSpeed), ctld.p(updateTimeStep))) + + local i = 1 + while i * updateTimeStep <= refreshDelay - updateTimeStep do --while the scheduled time for the laseUnit call isn't greater than the time between two JTACAutoLase() calls minus one time step (because at the next time step JTACAutoLase() should have been called and this in term also calls laseUnit()) + timer.scheduleFunction(ctld.timerLaseUnit, { _enemyUnit, _jtacUnit, _jtacGroupName, _laserCode }, + timer.getTime() + i * updateTimeStep) + i = i + 1 + end + ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduled %s moving target lasing steps", + ctld.p(_jtacGroupName), ctld.p(i))) end - local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) - --update targets list and store the next potential target if the selected one was lost - local _defaultEnemyUnit = ctld.findNearestVisibleEnemy(_jtacUnit, _lock) + ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), + ctld.p(refreshDelay), ctld.p(timer.getTime() + refreshDelay))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, + timer.getTime() + refreshDelay) - -- if the JTAC sees a unit and a target was selected by users but is not the current unit, check if the selected target is in the targets list, if it is, then it's been reacquired - if _enemyUnit and ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 and ctld.jtacSelectedTarget[_jtacGroupName] ~= _enemyUnit:getName() then - for _,target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do - if target then - local targetUnit = target.unit - local targetName = targetUnit:getName() + if _smoke == true then + local _nextSmokeTime = ctld.jtacSmokeMarks[_enemyUnit:getName()] - if ctld.jtacSelectedTarget[_jtacGroupName] == targetName then - - ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = targetUnit:getTypeName(), unitId = targetUnit:getID() } - _enemyUnit = targetUnit - - local message = ctld.i18n_translate("%1, selected target reacquired, %2", _jtacGroupName, _enemyUnit:getTypeName()) - local fullMessage = message .. ctld.i18n_translate(". CODE: %1. POSITION: %2", _laserCode, ctld.getPositionString(_enemyUnit)) - ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) - end - end - end + --recreate smoke marker after 5 mins + if _nextSmokeTime ~= nil and _nextSmokeTime < timer.getTime() then + ctld.createSmokeMarker(_enemyUnit, _colour) + end end + else + ctld.logDebug(string.format("JTAC - MODE - [%s] - No Enemies Nearby / Standby mode", ctld.p(_jtacGroupName))) - local targetDestroyed = false - local targetLost = false - local wasSelected = false + -- stop lazing the old spot + ctld.logDebug(string.format("JTAC - LASE - [%s] - canceling lasing of the old spot", ctld.p(_jtacGroupName))) + ctld.cancelLase(_jtacGroupName) - if _enemyUnit == nil and ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then + ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), + ctld.p(5), ctld.p(timer.getTime() + 5))) + timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, + timer.getTime() + 5) + end - local _tempUnitInfo = ctld.jtacCurrentTargets[_jtacGroupName] + local action = ", " + if wasSelected then + action = action .. "selected " + end - -- env.info("TEMP UNIT INFO: " .. tempUnitInfo.name .. " " .. tempUnitInfo.unitType) - - local _tempUnit = Unit.getByName(_tempUnitInfo.name) - - wasSelected = (ctld.jtacCurrentTargets[_jtacGroupName].name == ctld.jtacSelectedTarget[_jtacGroupName]) - - if _tempUnit ~= nil and _tempUnit:getLife() > 0 and _tempUnit:isActive() == true then - targetLost = true - else - targetDestroyed = true - ctld.jtacSelectedTarget[_jtacGroupName] = 1 - end - - --remove from smoke list - ctld.jtacSmokeMarks[_tempUnitInfo.name] = nil - - -- JTAC Unit: resume his route ------------ - trigger.action.groupContinueMoving(Group.getByName(_jtacGroupName)) - - -- remove from target list - ctld.jtacCurrentTargets[_jtacGroupName] = nil - - --stop lasing - ctld.cancelLase(_jtacGroupName) - end - - - if _enemyUnit == nil then - if _defaultEnemyUnit ~= nil then - - -- store current target for easy lookup - ctld.jtacCurrentTargets[_jtacGroupName] = { name = _defaultEnemyUnit:getName(), unitType = _defaultEnemyUnit:getTypeName(), unitId = _defaultEnemyUnit:getID() } - - --add check for lasing or not - local action = ctld.i18n_translate("new target, ") - - if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - action = ctld.i18n_translate("standing by on %1", action) - else - action = ctld.i18n_translate("lasing %1", action) - end - - if wasSelected and targetLost then - action = ctld.i18n_translate(", temporarily %1", action) - else - action = ", " .. action - end - - if targetLost then - action = ctld.i18n_translate("target lost") .. action - elseif targetDestroyed then - action = ctld.i18n_translate("target destroyed") .. action - end - - if wasSelected then - action = ctld.i18n_translate(", selected %1", action) - elseif targetLost or targetDestroyed then - action = ", " .. action - end - wasSelected = false - targetDestroyed = false - targetLost = false - - local message = _jtacGroupName .. action .. _defaultEnemyUnit:getTypeName() - local fullMessage = message .. '. CODE: ' .. _laserCode .. ". POSITION: " .. ctld.getPositionString(_defaultEnemyUnit) - ctld.notifyCoalition(fullMessage, 10, _jtacUnit:getCoalition(), _radio, message) - - -- JTAC Unit stop his route ----------------- - trigger.action.groupStopMoving(Group.getByName(_jtacGroupName)) -- stop JTAC - - -- create smoke - if _smoke == true then - - --create first smoke - ctld.createSmokeMarker(_defaultEnemyUnit, _colour) - end - end - end - - if _enemyUnit ~= nil and not ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - - local refreshDelay = 15 --delay in between JTACAutoLase scheduled calls when a target is tracked - local targetSpeedVec = _enemyUnit:getVelocity() - local targetSpeed = math.sqrt(targetSpeedVec.x^2+targetSpeedVec.y^2+targetSpeedVec.z^2) - local maxUpdateDist = 5 --maximum distance the unit will be allowed to travel before the lase spot is updated again - ctld.logTrace(string.format("targetSpeed=%s", ctld.p(targetSpeed))) - - ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) - - --if the target is going sufficiently fast for it to wander off futher than the maxUpdateDist, schedule laseUnit calls to update the lase spot only (we consider that the unit lives and drives on between JTACAutoLase calls) - if targetSpeed >= maxUpdateDist/refreshDelay then - local updateTimeStep = maxUpdateDist/targetSpeed --calculate the time step so that the target is never more than maxUpdateDist from it's last lased position - ctld.logTrace(string.format("JTAC - LASE - [%s] - target is moving at %s m/s, schedulting lasing steps every %ss", ctld.p(_jtacGroupName), ctld.p(targetSpeed), ctld.p(updateTimeStep))) - - local i = 1 - while i*updateTimeStep <= refreshDelay - updateTimeStep do --while the scheduled time for the laseUnit call isn't greater than the time between two JTACAutoLase() calls minus one time step (because at the next time step JTACAutoLase() should have been called and this in term also calls laseUnit()) - timer.scheduleFunction(ctld.timerLaseUnit,{_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode}, timer.getTime()+i*updateTimeStep) - i = i + 1 - end - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduled %s moving target lasing steps", ctld.p(_jtacGroupName), ctld.p(i))) - end - - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(refreshDelay), ctld.p(timer.getTime() + refreshDelay))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + refreshDelay) - - if _smoke == true then - local _nextSmokeTime = ctld.jtacSmokeMarks[_enemyUnit:getName()] - - --recreate smoke marker after 5 mins - if _nextSmokeTime ~= nil and _nextSmokeTime < timer.getTime() then - - ctld.createSmokeMarker(_enemyUnit, _colour) - end - end - - else - ctld.logDebug(string.format("JTAC - MODE - [%s] - No Enemies Nearby / Standby mode", ctld.p(_jtacGroupName))) - - -- stop lazing the old spot - ctld.logDebug(string.format("JTAC - LASE - [%s] - canceling lasing of the old spot", ctld.p(_jtacGroupName))) - ctld.cancelLase(_jtacGroupName) - - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(5), ctld.p(timer.getTime() + 5))) - timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 5) - end - - local action = ", " - if wasSelected then - action = action .. "selected " - end - - if targetLost then - ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target lost.", _jtacGroupName , action), 10, _jtacUnit:getCoalition(), _radio) - elseif targetDestroyed then - ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target destroyed.", _jtacGroupName , action), 10, _jtacUnit:getCoalition(), _radio) - end + if targetLost then + ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target lost.", _jtacGroupName, action), 10, + _jtacUnit:getCoalition(), _radio) + elseif targetDestroyed then + ctld.notifyCoalition(ctld.i18n_translate("%1 %2 target destroyed.", _jtacGroupName, action), 10, + _jtacUnit:getCoalition(), _radio) + end end function ctld.JTACAutoLaseStop(_jtacGroupName) - ctld.jtacStop[_jtacGroupName] = true + ctld.jtacStop[_jtacGroupName] = true end -- used by the timer function function ctld.timerJTACAutoLase(_args) - - ctld.JTACAutoLase(_args[1], _args[2], _args[3], _args[4], _args[5], _args[6]) + ctld.JTACAutoLase(_args[1], _args[2], _args[3], _args[4], _args[5], _args[6]) end function ctld.cleanupJTAC(_jtacGroupName) - -- clear laser - just in case - ctld.cancelLase(_jtacGroupName) + -- clear laser - just in case + ctld.cancelLase(_jtacGroupName) - -- Cleanup - ctld.jtacCurrentTargets[_jtacGroupName] = nil + -- Cleanup + ctld.jtacCurrentTargets[_jtacGroupName] = nil - ctld.jtacTargetsList[_jtacGroupName] = nil + ctld.jtacTargetsList[_jtacGroupName] = nil - ctld.jtacSelectedTarget[_jtacGroupName] = nil + ctld.jtacSelectedTarget[_jtacGroupName] = nil - for _,_specialOption in pairs(ctld.jtacSpecialOptions) do --delete jtac specific settings for all special options - if _specialOption.jtacs then - _specialOption.jtacs[_jtacGroupName] = nil - end + for _, _specialOption in pairs(ctld.jtacSpecialOptions) do --delete jtac specific settings for all special options + if _specialOption.jtacs then + _specialOption.jtacs[_jtacGroupName] = nil end + end - ctld.jtacRadioData[_jtacGroupName] = nil + ctld.jtacRadioData[_jtacGroupName] = nil - --remove the JTAC's group submenu and all of the target pages it potentially contained if the JTAC has or had a menu - if ctld.jtacUnits[_jtacGroupName] and ctld.jtacUnits[_jtacGroupName].side and ctld.jtacGroupSubMenuPath[_jtacGroupName] then - local _players = coalition.getPlayers(ctld.jtacUnits[_jtacGroupName].side) + --remove the JTAC's group submenu and all of the target pages it potentially contained if the JTAC has or had a menu + if ctld.jtacUnits[_jtacGroupName] and ctld.jtacUnits[_jtacGroupName].side and ctld.jtacGroupSubMenuPath[_jtacGroupName] then + local _players = coalition.getPlayers(ctld.jtacUnits[_jtacGroupName].side) - if _players ~= nil then + if _players ~= nil then + for _, _playerUnit in pairs(_players) do + local _groupId = ctld.getGroupId(_playerUnit) - for _, _playerUnit in pairs(_players) do - - local _groupId = ctld.getGroupId(_playerUnit) - - if _groupId then - missionCommands.removeItemForGroup(_groupId, ctld.jtacGroupSubMenuPath[_jtacGroupName]) - end - end + if _groupId then + missionCommands.removeItemForGroup(_groupId, ctld.jtacGroupSubMenuPath[_jtacGroupName]) end + end end + end - ctld.jtacUnits[_jtacGroupName] = nil + ctld.jtacUnits[_jtacGroupName] = nil - ctld.jtacGroupSubMenuPath[_jtacGroupName] = nil + ctld.jtacGroupSubMenuPath[_jtacGroupName] = nil end - --- send a message to the coalition --- if _radio is set, the message will be read out loud via SRS function ctld.notifyCoalition(_message, _displayFor, _side, _radio, _shortMessage) + trigger.action.outTextForCoalition(_side, _message, _displayFor) - trigger.action.outTextForCoalition(_side, _message, _displayFor) + local _shortMessage = _shortMessage + if _shortMessage == nil then + _shortMessage = _message + end - local _shortMessage = _shortMessage - if _shortMessage == nil then - _shortMessage = _message - end - - if STTS and STTS.TextToSpeech and _radio and _radio.freq then - local _freq = _radio.freq - local _modulation = _radio.mod or "FM" - local _volume = _radio.volume or "1.0" - local _name = _radio.name or "JTAC" - local _gender = _radio.gender or "male" - local _culture = _radio.culture or "en-US" - local _voice = _radio.voice - local _googleTTS = _radio.googleTTS or false - STTS.TextToSpeech(_shortMessage, _freq, _modulation, _volume, _name, _side, nil, 1, _gender, _culture, _voice, _googleTTS) - else - trigger.action.outSoundForCoalition(_side, "radiobeep.ogg") - end + if STTS and STTS.TextToSpeech and _radio and _radio.freq then + local _freq = _radio.freq + local _modulation = _radio.mod or "FM" + local _volume = _radio.volume or "1.0" + local _name = _radio.name or "JTAC" + local _gender = _radio.gender or "male" + local _culture = _radio.culture or "en-US" + local _voice = _radio.voice + local _googleTTS = _radio.googleTTS or false + STTS.TextToSpeech(_shortMessage, _freq, _modulation, _volume, _name, _side, nil, 1, _gender, _culture, _voice, + _googleTTS) + else + trigger.action.outSoundForCoalition(_side, "radiobeep.ogg") + end end function ctld.createSmokeMarker(_enemyUnit, _colour) + --recreate in 5 mins + ctld.jtacSmokeMarks[_enemyUnit:getName()] = timer.getTime() + 300.0 - --recreate in 5 mins - ctld.jtacSmokeMarks[_enemyUnit:getName()] = timer.getTime() + 300.0 - - local _enemyPoint = _enemyUnit:getPoint() - trigger.action.smoke({ x = _enemyPoint.x + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_x, y = _enemyPoint.y + ctld.JTAC_smokeOffset_y, z = _enemyPoint.z + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_z }, _colour) + local _enemyPoint = _enemyUnit:getPoint() + trigger.action.smoke( + { x = _enemyPoint.x + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + + ctld.JTAC_smokeOffset_x, y = _enemyPoint.y + ctld.JTAC_smokeOffset_y, z = _enemyPoint.z + + math.random(-ctld.JTAC_smokeMarginOfError, ctld.JTAC_smokeMarginOfError) + ctld.JTAC_smokeOffset_z }, _colour) end function ctld.cancelLase(_jtacGroupName) + --local index = "JTAC_"..jtacUnit:getID() - --local index = "JTAC_"..jtacUnit:getID() + local _tempLase = ctld.jtacLaserPoints[_jtacGroupName] - local _tempLase = ctld.jtacLaserPoints[_jtacGroupName] + if _tempLase ~= nil then + Spot.destroy(_tempLase) + ctld.jtacLaserPoints[_jtacGroupName] = nil - if _tempLase ~= nil then - Spot.destroy(_tempLase) - ctld.jtacLaserPoints[_jtacGroupName] = nil + -- env.info('Destroy laze '..index) - -- env.info('Destroy laze '..index) + _tempLase = nil + end - _tempLase = nil - end + local _tempIR = ctld.jtacIRPoints[_jtacGroupName] - local _tempIR = ctld.jtacIRPoints[_jtacGroupName] + if _tempIR ~= nil then + Spot.destroy(_tempIR) + ctld.jtacIRPoints[_jtacGroupName] = nil - if _tempIR ~= nil then - Spot.destroy(_tempIR) - ctld.jtacIRPoints[_jtacGroupName] = nil + -- env.info('Destroy laze '..index) - -- env.info('Destroy laze '..index) - - _tempIR = nil - end + _tempIR = nil + end end -- used by the timer function function ctld.timerLaseUnit(_args) - - ctld.laseUnit(_args[1], _args[2], _args[3], _args[4]) + ctld.laseUnit(_args[1], _args[2], _args[3], _args[4]) end function ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) + --cancelLase(jtacGroupName) + ctld.logTrace("ctld.laseUnit()") - --cancelLase(jtacGroupName) - ctld.logTrace("ctld.laseUnit()") + local _spots = {} - local _spots = {} + if _enemyUnit:isExist() then + local _enemyVector = _enemyUnit:getPoint() + local _enemyVectorUpdated = { x = _enemyVector.x, y = _enemyVector.y + 2.0, z = _enemyVector.z } - if _enemyUnit:isExist() then - local _enemyVector = _enemyUnit:getPoint() - local _enemyVectorUpdated = { x = _enemyVector.x, y = _enemyVector.y + 2.0, z = _enemyVector.z } + if ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] then + local _enemySpeedVector = _enemyUnit:getVelocity() + ctld.logTrace(string.format("_enemySpeedVector=%s", ctld.p(_enemySpeedVector))) - if ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] then - local _enemySpeedVector = _enemyUnit:getVelocity() - ctld.logTrace(string.format("_enemySpeedVector=%s", ctld.p(_enemySpeedVector))) + local _WindSpeedVector = atmosphere.getWind(_enemyVectorUpdated) + ctld.logTrace(string.format("_WindSpeedVector=%s", ctld.p(_WindSpeedVector))) - local _WindSpeedVector = atmosphere.getWind(_enemyVectorUpdated) - ctld.logTrace(string.format("_WindSpeedVector=%s", ctld.p(_WindSpeedVector))) + --if target speed is greater than 0, calculated using absolute value norm + if math.abs(_enemySpeedVector.x) + math.abs(_enemySpeedVector.y) + math.abs(_enemySpeedVector.z) > 0 then + local CorrectionFactor = 1 --correction factor in seconds applied to the target speed components to determine the lasing spot for a direct hit on a moving vehicle - --if target speed is greater than 0, calculated using absolute value norm - if math.abs(_enemySpeedVector.x) + math.abs(_enemySpeedVector.y) + math.abs(_enemySpeedVector.z) > 0 then - local CorrectionFactor = 1 --correction factor in seconds applied to the target speed components to determine the lasing spot for a direct hit on a moving vehicle + --correct in the direction of the movement + _enemyVectorUpdated.x = _enemyVectorUpdated.x + _enemySpeedVector.x * CorrectionFactor + _enemyVectorUpdated.y = _enemyVectorUpdated.y + _enemySpeedVector.y * CorrectionFactor + _enemyVectorUpdated.z = _enemyVectorUpdated.z + _enemySpeedVector.z * CorrectionFactor + end - --correct in the direction of the movement - _enemyVectorUpdated.x = _enemyVectorUpdated.x + _enemySpeedVector.x * CorrectionFactor - _enemyVectorUpdated.y = _enemyVectorUpdated.y + _enemySpeedVector.y * CorrectionFactor - _enemyVectorUpdated.z = _enemyVectorUpdated.z + _enemySpeedVector.z * CorrectionFactor - end + --if wind speed is greater than 0, calculated using absolute value norm + if math.abs(_WindSpeedVector.x) + math.abs(_WindSpeedVector.y) + math.abs(_WindSpeedVector.z) > 0 then + local CorrectionFactor = 1.05 --correction factor in seconds applied to the wind speed components to determine the lasing spot for a direct hit in adverse conditions - --if wind speed is greater than 0, calculated using absolute value norm - if math.abs(_WindSpeedVector.x) + math.abs(_WindSpeedVector.y) + math.abs(_WindSpeedVector.z) > 0 then - local CorrectionFactor = 1.05 --correction factor in seconds applied to the wind speed components to determine the lasing spot for a direct hit in adverse conditions - - --correct to the opposite of the wind direction - _enemyVectorUpdated.x = _enemyVectorUpdated.x - _WindSpeedVector.x * CorrectionFactor - _enemyVectorUpdated.y = _enemyVectorUpdated.y - _WindSpeedVector.y * CorrectionFactor --not sure about correcting altitude but that component is always 0 in testing - _enemyVectorUpdated.z = _enemyVectorUpdated.z - _WindSpeedVector.z * CorrectionFactor - end - --combination of both should result in near perfect accuracy if the bomb doesn't stall itself following fast vehicles or correcting for heavy winds, correction factors can be adjusted but should work up to 40kn of wind for vehicles moving at 90kph (beware to drop the bomb in a way to not stall it, facing which ever is larger, target speed or wind) - end - - local _oldLase = ctld.jtacLaserPoints[_jtacGroupName] - local _oldIR = ctld.jtacIRPoints[_jtacGroupName] - - if _oldLase == nil or _oldIR == nil then - - -- create lase - - local _status, _result = pcall(function() - _spots['irPoint'] = Spot.createInfraRed(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated) - _spots['laserPoint'] = Spot.createLaser(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated, _laserCode) - return _spots - end) - - if not _status then - env.error('ERROR: ' .. _result, false) - else - if _result.irPoint then - - -- env.info(jtacUnit:getName() .. ' placed IR Pointer on '..enemyUnit:getName()) - - ctld.jtacIRPoints[_jtacGroupName] = _result.irPoint --store so we can remove after - end - if _result.laserPoint then - - -- env.info(jtacUnit:getName() .. ' is Lasing '..enemyUnit:getName()..'. CODE:'..laserCode) - - ctld.jtacLaserPoints[_jtacGroupName] = _result.laserPoint - end - end - - else - - -- update lase - - if _oldLase ~= nil then - _oldLase:setPoint(_enemyVectorUpdated) - end - - if _oldIR ~= nil then - _oldIR:setPoint(_enemyVectorUpdated) - end - end + --correct to the opposite of the wind direction + _enemyVectorUpdated.x = _enemyVectorUpdated.x - _WindSpeedVector.x * CorrectionFactor + _enemyVectorUpdated.y = _enemyVectorUpdated.y - + _WindSpeedVector.y * + CorrectionFactor --not sure about correcting altitude but that component is always 0 in testing + _enemyVectorUpdated.z = _enemyVectorUpdated.z - _WindSpeedVector.z * CorrectionFactor + end + --combination of both should result in near perfect accuracy if the bomb doesn't stall itself following fast vehicles or correcting for heavy winds, correction factors can be adjusted but should work up to 40kn of wind for vehicles moving at 90kph (beware to drop the bomb in a way to not stall it, facing which ever is larger, target speed or wind) end + + local _oldLase = ctld.jtacLaserPoints[_jtacGroupName] + local _oldIR = ctld.jtacIRPoints[_jtacGroupName] + + if _oldLase == nil or _oldIR == nil then + -- create lase + + local _status, _result = pcall(function() + _spots['irPoint'] = Spot.createInfraRed(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated) + _spots['laserPoint'] = Spot.createLaser(_jtacUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated, + _laserCode) + return _spots + end) + + if not _status then + env.error('ERROR: ' .. _result, false) + else + if _result.irPoint then + -- env.info(jtacUnit:getName() .. ' placed IR Pointer on '..enemyUnit:getName()) + + ctld.jtacIRPoints[_jtacGroupName] = _result.irPoint --store so we can remove after + end + if _result.laserPoint then + -- env.info(jtacUnit:getName() .. ' is Lasing '..enemyUnit:getName()..'. CODE:'..laserCode) + + ctld.jtacLaserPoints[_jtacGroupName] = _result.laserPoint + end + end + else + -- update lase + + if _oldLase ~= nil then + _oldLase:setPoint(_enemyVectorUpdated) + end + + if _oldIR ~= nil then + _oldIR:setPoint(_enemyVectorUpdated) + end + end + end end -- get currently selected unit and check they're still in range function ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) + local _unit = nil - local _unit = nil + if ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then + _unit = Unit.getByName(ctld.jtacCurrentTargets[_jtacGroupName].name) + end - if ctld.jtacCurrentTargets[_jtacGroupName] ~= nil then - _unit = Unit.getByName(ctld.jtacCurrentTargets[_jtacGroupName].name) + local _tempPoint = nil + local _tempDist = nil + local _tempPosition = nil + + local _jtacPosition = _jtacUnit:getPosition() + local _jtacPoint = _jtacUnit:getPoint() + + if _unit ~= nil and _unit:getLife() > 0 and _unit:isActive() == true then + -- calc distance + _tempPoint = _unit:getPoint() + -- tempPosition = unit:getPosition() + + _tempDist = ctld.getDistance(_unit:getPoint(), _jtacUnit:getPoint()) + if _tempDist < ctld.JTAC_maxDistance then + -- calc visible + + -- check slightly above the target as rounding errors can cause issues, plus the unit has some height anyways + local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } + local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } + + if land.isVisible(_offsetEnemyPos, _offsetJTACPos) then + return _unit + end end - - local _tempPoint = nil - local _tempDist = nil - local _tempPosition = nil - - local _jtacPosition = _jtacUnit:getPosition() - local _jtacPoint = _jtacUnit:getPoint() - - if _unit ~= nil and _unit:getLife() > 0 and _unit:isActive() == true then - - -- calc distance - _tempPoint = _unit:getPoint() - -- tempPosition = unit:getPosition() - - _tempDist = ctld.getDistance(_unit:getPoint(), _jtacUnit:getPoint()) - if _tempDist < ctld.JTAC_maxDistance then - -- calc visible - - -- check slightly above the target as rounding errors can cause issues, plus the unit has some height anyways - local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } - local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } - - if land.isVisible(_offsetEnemyPos, _offsetJTACPos) then - return _unit - end - end - end - return nil + end + return nil end - -- Find nearest enemy to JTAC that isn't blocked by terrain -function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType,_distance) +function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType, _distance) + --local startTime = os.clock() - --local startTime = os.clock() + local _maxDistance = _distance or ctld.JTAC_maxDistance - local _maxDistance = _distance or ctld.JTAC_maxDistance + local _nearestDistance = _maxDistance - local _nearestDistance = _maxDistance + local _jtacGroupName = _jtacUnit:getGroup():getName() + local _jtacPoint = _jtacUnit:getPoint() + local _coa = _jtacUnit:getCoalition() - local _jtacGroupName = _jtacUnit:getGroup():getName() - local _jtacPoint = _jtacUnit:getPoint() - local _coa = _jtacUnit:getCoalition() + local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } - local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } - - local _volume = { - id = world.VolumeType.SPHERE, - params = { - point = _offsetJTACPos, - radius = _maxDistance - } + local _volume = { + id = world.VolumeType.SPHERE, + params = { + point = _offsetJTACPos, + radius = _maxDistance } + } - local _unitList = {} + local _unitList = {} - local _search = function(_unit, _coa) - pcall(function() + local _search = function(_unit, _coa) + pcall(function() + if _unit ~= nil + and _unit:getLife() > 0 + and _unit:isActive() + and _unit:getCoalition() ~= _coa + and not _unit:inAir() + and not ctld.alreadyTarget(_jtacUnit, _unit) then + local _tempPoint = _unit:getPoint() + local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } - if _unit ~= nil - and _unit:getLife() > 0 - and _unit:isActive() - and _unit:getCoalition() ~= _coa - and not _unit:inAir() - and not ctld.alreadyTarget(_jtacUnit,_unit) then + if land.isVisible(_offsetJTACPos, _offsetEnemyPos) then + local _dist = ctld.getDistance(_offsetJTACPos, _offsetEnemyPos) - local _tempPoint = _unit:getPoint() - local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } - - if land.isVisible(_offsetJTACPos,_offsetEnemyPos ) then - - local _dist = ctld.getDistance(_offsetJTACPos, _offsetEnemyPos) - - if _dist < _maxDistance then - table.insert(_unitList,{unit=_unit, dist=_dist}) - - end - end - end - end) - - return true - end - - world.searchObjects(Object.Category.UNIT, _volume, _search, _coa) - - --log.info(string.format("JTAC Search elapsed time: %.4f\n", os.clock() - startTime)) - - -- generate list order by distance & visible - - -- first check - -- hpriority - -- priority - -- vehicle - -- unit - - - ctld.jtacTargetsList[_jtacGroupName] = _unitList - --from the units in range, build the targets list, unsorted as to keep consistency between radio menu refreshes - - local _sort = function( a,b ) return a.dist < b.dist end - table.sort(_unitList,_sort) - -- sort list - - -- check for hpriority - for _, _enemyUnit in ipairs(_unitList) do - local _enemyName = _enemyUnit.unit:getName() - - if string.match(_enemyName, "hpriority") then - return _enemyUnit.unit + if _dist < _maxDistance then + table.insert(_unitList, { unit = _unit, dist = _dist }) + end end + end + end) + + return true + end + + world.searchObjects(Object.Category.UNIT, _volume, _search, _coa) + + --log.info(string.format("JTAC Search elapsed time: %.4f\n", os.clock() - startTime)) + + -- generate list order by distance & visible + + -- first check + -- hpriority + -- priority + -- vehicle + -- unit + + + ctld.jtacTargetsList[_jtacGroupName] = _unitList + --from the units in range, build the targets list, unsorted as to keep consistency between radio menu refreshes + + local _sort = function(a, b) return a.dist < b.dist end + table.sort(_unitList, _sort) + -- sort list + + -- check for hpriority + for _, _enemyUnit in ipairs(_unitList) do + local _enemyName = _enemyUnit.unit:getName() + + if string.match(_enemyName, "hpriority") then + return _enemyUnit.unit end + end - for _, _enemyUnit in ipairs(_unitList) do - local _enemyName = _enemyUnit.unit:getName() + for _, _enemyUnit in ipairs(_unitList) do + local _enemyName = _enemyUnit.unit:getName() - if string.match(_enemyName, "priority") then - return _enemyUnit.unit - end + if string.match(_enemyName, "priority") then + return _enemyUnit.unit end + end - local result = nil - for _, _enemyUnit in ipairs(_unitList) do - local _enemyName = _enemyUnit.unit:getName() - --log.info(string.format("CTLD - checking _enemyName=%s", _enemyName)) + local result = nil + for _, _enemyUnit in ipairs(_unitList) do + local _enemyName = _enemyUnit.unit:getName() + --log.info(string.format("CTLD - checking _enemyName=%s", _enemyName)) - -- check for air defenses - --log.info(string.format("CTLD - _enemyUnit.unit:getDesc()[attributes]=%s", ctld.p(_enemyUnit.unit:getDesc()["attributes"]))) - local airdefense = (_enemyUnit.unit:getDesc()["attributes"]["Air Defence"] ~= nil) - --log.info(string.format("CTLD - airdefense=%s", tostring(airdefense))) + -- check for air defenses + --log.info(string.format("CTLD - _enemyUnit.unit:getDesc()[attributes]=%s", ctld.p(_enemyUnit.unit:getDesc()["attributes"]))) + local airdefense = (_enemyUnit.unit:getDesc()["attributes"]["Air Defence"] ~= nil) + --log.info(string.format("CTLD - airdefense=%s", tostring(airdefense))) - if (_targetType == "vehicle" and ctld.isVehicle(_enemyUnit.unit)) or _targetType == "all" then - if airdefense then - return _enemyUnit.unit - else - result = _enemyUnit.unit - end - - elseif (_targetType == "troop" and ctld.isInfantry(_enemyUnit.unit)) or _targetType == "all" then - if airdefense then - return _enemyUnit.unit - else - result = _enemyUnit.unit - end - end + if (_targetType == "vehicle" and ctld.isVehicle(_enemyUnit.unit)) or _targetType == "all" then + if airdefense then + return _enemyUnit.unit + else + result = _enemyUnit.unit + end + elseif (_targetType == "troop" and ctld.isInfantry(_enemyUnit.unit)) or _targetType == "all" then + if airdefense then + return _enemyUnit.unit + else + result = _enemyUnit.unit + end end + end - return result - + return result end - function ctld.listNearbyEnemies(_jtacUnit) + local _maxDistance = ctld.JTAC_maxDistance - local _maxDistance = ctld.JTAC_maxDistance + local _jtacPoint = _jtacUnit:getPoint() + local _coa = _jtacUnit:getCoalition() - local _jtacPoint = _jtacUnit:getPoint() - local _coa = _jtacUnit:getCoalition() + local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } - local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } - - local _volume = { - id = world.VolumeType.SPHERE, - params = { - point = _offsetJTACPos, - radius = _maxDistance - } + local _volume = { + id = world.VolumeType.SPHERE, + params = { + point = _offsetJTACPos, + radius = _maxDistance } - local _enemies = nil + } + local _enemies = nil - local _search = function(_unit, _coa) - pcall(function() + local _search = function(_unit, _coa) + pcall(function() + if _unit ~= nil + and _unit:getLife() > 0 + and _unit:isActive() + and _unit:getCoalition() ~= _coa + and not _unit:inAir() then + local _tempPoint = _unit:getPoint() + local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } - if _unit ~= nil - and _unit:getLife() > 0 - and _unit:isActive() - and _unit:getCoalition() ~= _coa - and not _unit:inAir() then + if land.isVisible(_offsetJTACPos, _offsetEnemyPos) then + if not _enemies then + _enemies = {} + end - local _tempPoint = _unit:getPoint() - local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z } + _enemies[_unit:getTypeName()] = _unit:getTypeName() + end + end + end) - if land.isVisible(_offsetJTACPos,_offsetEnemyPos ) then + return true + end - if not _enemies then - _enemies = {} - end + world.searchObjects(Object.Category.UNIT, _volume, _search, _coa) - _enemies[_unit:getTypeName()] = _unit:getTypeName() - - end - end - end) - - return true - end - - world.searchObjects(Object.Category.UNIT, _volume, _search, _coa) - - return _enemies + return _enemies end -- tests whether the unit is targeted by another JTAC function ctld.alreadyTarget(_jtacUnit, _enemyUnit) - - for _, _jtacTarget in pairs(ctld.jtacCurrentTargets) do - - if _jtacTarget.unitId == _enemyUnit:getID() then - -- env.info("ALREADY TARGET") - return true - end + for _, _jtacTarget in pairs(ctld.jtacCurrentTargets) do + if _jtacTarget.unitId == _enemyUnit:getID() then + -- env.info("ALREADY TARGET") + return true end + end - return false + return false end - -- Returns only alive units from group but the group / unit may not be active function ctld.getGroup(groupName) + local _group = Group.getByName(groupName) - local _group = Group.getByName(groupName) + local _filteredUnits = {} --contains alive units + local _x = 1 - local _filteredUnits = {} --contains alive units - local _x = 1 + if _group ~= nil then + ctld.logTrace(string.format("ctld.getGroup - %s - group ~= nil", ctld.p(groupName))) + if _group:isExist() then + ctld.logTrace(string.format("ctld.getGroup - %s - group:isExist()", ctld.p(groupName))) + local _groupUnits = _group:getUnits() - if _group ~= nil then - ctld.logTrace(string.format("ctld.getGroup - %s - group ~= nil", ctld.p(groupName))) - if _group:isExist() then - ctld.logTrace(string.format("ctld.getGroup - %s - group:isExist()", ctld.p(groupName))) - local _groupUnits = _group:getUnits() - - if _groupUnits ~= nil and #_groupUnits > 0 then - ctld.logTrace(string.format("ctld.getGroup - %s - group has %s units", ctld.p(groupName), ctld.p(#_groupUnits))) - for _x = 1, #_groupUnits do - if _groupUnits[_x]:getLife() > 0 then -- removed and _groupUnits[_x]:isExist() as isExist doesnt work on single units! - table.insert(_filteredUnits, _groupUnits[_x]) - else - ctld.logTrace(string.format("ctld.getGroup - %s - dead unit %s", ctld.p(groupName), ctld.p(_groupUnits[_x]:getName()))) - end - end - end + if _groupUnits ~= nil and #_groupUnits > 0 then + ctld.logTrace(string.format("ctld.getGroup - %s - group has %s units", ctld.p(groupName), + ctld.p(#_groupUnits))) + for _x = 1, #_groupUnits do + if _groupUnits[_x]:getLife() > 0 then -- removed and _groupUnits[_x]:isExist() as isExist doesnt work on single units! + table.insert(_filteredUnits, _groupUnits[_x]) + else + ctld.logTrace(string.format("ctld.getGroup - %s - dead unit %s", ctld.p(groupName), + ctld.p(_groupUnits[_x]:getName()))) + end end + end end + end - return _filteredUnits + return _filteredUnits end function ctld.getAliveGroup(_groupName) + local _group = Group.getByName(_groupName) - local _group = Group.getByName(_groupName) + if _group and _group:isExist() == true and #_group:getUnits() > 0 then + return _group + end - if _group and _group:isExist() == true and #_group:getUnits() > 0 then - return _group - end - - return nil + return nil end -- gets the JTAC status and displays to coalition units function ctld.getJTACStatus(_args) + --returns the status of all JTAC units unless the status of a single JTAC is asked for (by inserting it's groupName in _args[2]) - --returns the status of all JTAC units unless the status of a single JTAC is asked for (by inserting it's groupName in _args[2]) + local _playerUnit = ctld.getTransportUnit(_args[1]) + local _singleJtacGroupName = _args[2] - local _playerUnit = ctld.getTransportUnit(_args[1]) - local _singleJtacGroupName = _args[2] + if _playerUnit == nil and _singleJtacGroupName == nil then + return + end - if _playerUnit == nil and _singleJtacGroupName == nil then - return - end + local _side = nil - local _side = nil + if _playerUnit == nil then + _side = ctld.jtacUnits[_singleJtacGroupName].side + else + _side = _playerUnit:getCoalition() + end - if _playerUnit == nil then - _side = ctld.jtacUnits[_singleJtacGroupName].side - else - _side = _playerUnit:getCoalition() - end + local _jtacUnit = nil + local hasJTAC = false + local _message = ctld.i18n_translate("JTAC STATUS: \n\n") - local _jtacUnit = nil - local hasJTAC = false - local _message = ctld.i18n_translate("JTAC STATUS: \n\n") + for _jtacGroupName, _jtacDetails in pairs(ctld.jtacUnits) do + --look up units + if _singleJtacGroupName == nil or (_singleJtacGroupName and _singleJtacGroupName == _jtacGroupName) then --if the status of a single JTAC or if the status of a single JTAC was asked and this is the correct JTAC we're going over in the loop + _jtacUnit = Unit.getByName(_jtacDetails.name) - for _jtacGroupName, _jtacDetails in pairs(ctld.jtacUnits) do + if _jtacUnit ~= nil and _jtacUnit:getLife() > 0 and _jtacUnit:isActive() == true and _jtacUnit:getCoalition() == _side then + hasJTAC = true - --look up units - if _singleJtacGroupName == nil or (_singleJtacGroupName and _singleJtacGroupName == _jtacGroupName) then --if the status of a single JTAC or if the status of a single JTAC was asked and this is the correct JTAC we're going over in the loop - _jtacUnit = Unit.getByName(_jtacDetails.name) + local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) - if _jtacUnit ~= nil and _jtacUnit:getLife() > 0 and _jtacUnit:isActive() == true and _jtacUnit:getCoalition() == _side then + local _laserCode = ctld.jtacLaserPointCodes[_jtacGroupName] - hasJTAC = true - - local _enemyUnit = ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) - - local _laserCode = ctld.jtacLaserPointCodes[_jtacGroupName] - - local _start = "->" .. _jtacGroupName - if (_jtacDetails.radio) then - _start = _start .. ctld.i18n_translate(", available on %1 %2,", _jtacDetails.radio.freq, _jtacDetails.radio.mod) - end - - if _laserCode == nil then - _laserCode = ctld.i18n_translate("UNKNOWN") - end - - if _enemyUnit ~= nil and _enemyUnit:getLife() > 0 and _enemyUnit:isActive() == true then - - local action = ctld.i18n_translate(" targeting ") - - if ctld.jtacSelectedTarget[_jtacGroupName] == _enemyUnit:getName() then - action = ctld.i18n_translate(" targeting selected unit ") - else - if ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then - action = ctld.i18n_translate(" attempting to find selected unit, temporarily targeting ") - end - end - - if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - action = action .. ctld.i18n_translate("(Laser OFF) ") - end - - _message = _message .. "" .. _start .. action .. _enemyUnit:getTypeName() .. " CODE: " .. _laserCode .. ctld.getPositionString(_enemyUnit) .. "\n" - - local _list = ctld.listNearbyEnemies(_jtacUnit) - - if _list then - _message = _message .. ctld.i18n_translate("Visual On: ") - - for _,_type in pairs(_list) do - _message = _message.._type..", " - end - _message = _message.."\n" - end - - else - _message = _message .. "" .. _start .. ctld.i18n_translate(" searching for targets %1\n", ctld.getPositionString(_jtacUnit)) - end - end + local _start = "->" .. _jtacGroupName + if (_jtacDetails.radio) then + _start = _start .. + ctld.i18n_translate(", available on %1 %2,", _jtacDetails.radio.freq, _jtacDetails.radio.mod) end - end - if not hasJTAC then - ctld.notifyCoalition(ctld.i18n_translate("No Active JTACs"), 10, _side) - else - ctld.notifyCoalition(_message, 10, _side) + if _laserCode == nil then + _laserCode = ctld.i18n_translate("UNKNOWN") + end + + if _enemyUnit ~= nil and _enemyUnit:getLife() > 0 and _enemyUnit:isActive() == true then + local action = ctld.i18n_translate(" targeting ") + + if ctld.jtacSelectedTarget[_jtacGroupName] == _enemyUnit:getName() then + action = ctld.i18n_translate(" targeting selected unit ") + else + if ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then + action = ctld.i18n_translate(" attempting to find selected unit, temporarily targeting ") + end + end + + if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then + action = action .. ctld.i18n_translate("(Laser OFF) ") + end + + _message = _message .. + "" .. + _start .. + action .. + _enemyUnit:getTypeName() .. " CODE: " .. _laserCode .. ctld.getPositionString(_enemyUnit) .. "\n" + + local _list = ctld.listNearbyEnemies(_jtacUnit) + + if _list then + _message = _message .. ctld.i18n_translate("Visual On: ") + + for _, _type in pairs(_list) do + _message = _message .. _type .. ", " + end + _message = _message .. "\n" + end + else + _message = _message .. + "" .. _start .. ctld.i18n_translate(" searching for targets %1\n", ctld.getPositionString(_jtacUnit)) + end + end end + end + + if not hasJTAC then + ctld.notifyCoalition(ctld.i18n_translate("No Active JTACs"), 10, _side) + else + ctld.notifyCoalition(_message, 10, _side) + end end function ctld.setJTACTarget(_args) - if _args then - local _jtacGroupName = _args.jtacGroupName - local targetName = _args.targetName + if _args then + local _jtacGroupName = _args.jtacGroupName + local targetName = _args.targetName - if _jtacGroupName and targetName and ctld.jtacSelectedTarget[_jtacGroupName] and ctld.jtacTargetsList[_jtacGroupName] then + if _jtacGroupName and targetName and ctld.jtacSelectedTarget[_jtacGroupName] and ctld.jtacTargetsList[_jtacGroupName] then + --look for the unit's (target) name in the Targets List, create the required data structure for jtacCurrentTargets and then assign it to the JTAC called _jtacGroupName + for _, target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do + if target then + local listedTargetUnit = target.unit + local ListedTargetName = listedTargetUnit:getName() - --look for the unit's (target) name in the Targets List, create the required data structure for jtacCurrentTargets and then assign it to the JTAC called _jtacGroupName - for _, target in pairs(ctld.jtacTargetsList[_jtacGroupName]) do + if ListedTargetName == targetName then + ctld.jtacSelectedTarget[_jtacGroupName] = targetName + ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = listedTargetUnit + :getTypeName(), unitId = listedTargetUnit:getID() } - if target then - - local listedTargetUnit = target.unit - local ListedTargetName = listedTargetUnit:getName() - - if ListedTargetName == targetName then - - ctld.jtacSelectedTarget[_jtacGroupName] = targetName - ctld.jtacCurrentTargets[_jtacGroupName] = { name = targetName, unitType = listedTargetUnit:getTypeName(), unitId = listedTargetUnit:getID() } - - local message = _jtacGroupName .. ctld.i18n_translate(", targeting selected unit, %1",listedTargetUnit:getTypeName()) - local fullMessage = message .. ctld.i18n_translate(". CODE: %1. POSITION: %2", ctld.jtacLaserPointCodes[_jtacGroupName], ctld.getPositionString(listedTargetUnit)) - ctld.notifyCoalition(fullMessage, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName], message) - end - end - end - elseif not targetName and ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then - ctld.jtacSelectedTarget[_jtacGroupName] = 1 - ctld.jtacCurrentTargets[_jtacGroupName] = nil - - local message = _jtacGroupName .. ctld.i18n_translate(", target selection reset.") - ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) - - if ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] then - ctld.setLaseCompensation({jtacGroupName = _jtacGroupName, value = false}) --disable laser spot corrections - end - - if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then - ctld.setStdbMode({jtacGroupName = _jtacGroupName, value = false}) --make the JTAC exit standby mode after either target selection or targeting selection reset - end + local message = _jtacGroupName .. + ctld.i18n_translate(", targeting selected unit, %1", listedTargetUnit:getTypeName()) + local fullMessage = message .. + ctld.i18n_translate(". CODE: %1. POSITION: %2", ctld.jtacLaserPointCodes[_jtacGroupName], + ctld.getPositionString(listedTargetUnit)) + ctld.notifyCoalition(fullMessage, 10, ctld.jtacUnits[_jtacGroupName].side, + ctld.jtacRadioData[_jtacGroupName], message) + end end + end + elseif not targetName and ctld.jtacSelectedTarget[_jtacGroupName] ~= 1 then + ctld.jtacSelectedTarget[_jtacGroupName] = 1 + ctld.jtacCurrentTargets[_jtacGroupName] = nil - ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true + local message = _jtacGroupName .. ctld.i18n_translate(", target selection reset.") + ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) + + if ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] then + ctld.setLaseCompensation({ jtacGroupName = _jtacGroupName, value = false }) --disable laser spot corrections + end + + if ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] then + ctld.setStdbMode({ jtacGroupName = _jtacGroupName, value = false }) --make the JTAC exit standby mode after either target selection or targeting selection reset + end end + + ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true + end end --special option setters (make sure to affect the function pointer to the corresponding .setter in the special options table after declaration of said function) function ctld.setSpecialOptionArgsCheck(_args) - if _args then - local _jtacGroupName = _args.jtacGroupName - local _value = _args.value --expected boolean - local _notOutput = _args.noOutput --expected boolean + if _args then + local _jtacGroupName = _args.jtacGroupName + local _value = _args.value --expected boolean + local _notOutput = _args.noOutput --expected boolean - if _jtacGroupName then - return {jtacGroupName = _jtacGroupName, value = _value, noOutput = _notOutput} - end + if _jtacGroupName then + return { jtacGroupName = _jtacGroupName, value = _value, noOutput = _notOutput } end + end - return nil + return nil end function ctld.setStdbMode(_args) - local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) - if parsedArgs then + local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) + if parsedArgs then + local _jtacGroupName = parsedArgs.jtacGroupName + local _value = parsedArgs.value + local _noOutput = parsedArgs.noOutput - local _jtacGroupName = parsedArgs.jtacGroupName - local _value = parsedArgs.value - local _noOutput = parsedArgs.noOutput - - local message = ctld.i18n_translate("%1, laser and smokes enabled", _jtacGroupName) - if _value then - message = ctld.i18n_translate("%1, laser and smokes disabled", _jtacGroupName) - end - if not _noOutput then - ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) - end - - ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] = _value - ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true + local message = ctld.i18n_translate("%1, laser and smokes enabled", _jtacGroupName) + if _value then + message = ctld.i18n_translate("%1, laser and smokes disabled", _jtacGroupName) end + if not _noOutput then + ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) + end + + ctld.jtacSpecialOptions.standbyMode.jtacs[_jtacGroupName] = _value + ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true + end end + ctld.jtacSpecialOptions.standbyMode.setter = ctld.setStdbMode function ctld.setLaseCompensation(_args) - local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) - if parsedArgs then + local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) + if parsedArgs then + local _jtacGroupName = parsedArgs.jtacGroupName + local _value = parsedArgs.value + local _noOutput = parsedArgs.noOutput - local _jtacGroupName = parsedArgs.jtacGroupName - local _value = parsedArgs.value - local _noOutput = parsedArgs.noOutput - - local message = ctld.i18n_translate("%1, wind and target speed laser spot compensations enabled", _jtacGroupName) - if _value then - message = ctld.i18n_translate("%1, wind and target speed laser spot compensations disabled", _jtacGroupName) - end - if not _noOutput then - ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) - end - - ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] = _value - ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true + local message = ctld.i18n_translate("%1, wind and target speed laser spot compensations enabled", _jtacGroupName) + if _value then + message = ctld.i18n_translate("%1, wind and target speed laser spot compensations disabled", _jtacGroupName) end + if not _noOutput then + ctld.notifyCoalition(message, 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) + end + + ctld.jtacSpecialOptions.laseSpotCorrections.jtacs[_jtacGroupName] = _value + ctld.refreshJTACmenu[ctld.jtacUnits[_jtacGroupName].side] = true + end end + ctld.jtacSpecialOptions.laseSpotCorrections.setter = ctld.setLaseCompensation function ctld.setSmokeOnTarget(_args) - local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) - if parsedArgs then + local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) + if parsedArgs then + local _jtacGroupName = parsedArgs.jtacGroupName + local _noOutput = parsedArgs.noOutput + local _enemyUnit = Unit.getByName(ctld.jtacCurrentTargets[_jtacGroupName].name) - local _jtacGroupName = parsedArgs.jtacGroupName - local _noOutput = parsedArgs.noOutput - local _enemyUnit = Unit.getByName(ctld.jtacCurrentTargets[_jtacGroupName].name) + if _enemyUnit then + if not _noOutput then + ctld.notifyCoalition(ctld.i18n_translate("%1, WHITE smoke deployed near target", _jtacGroupName), 10, + ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) + end - if _enemyUnit then - if not _noOutput then - ctld.notifyCoalition(ctld.i18n_translate("%1, WHITE smoke deployed near target", _jtacGroupName), 10, ctld.jtacUnits[_jtacGroupName].side, ctld.jtacRadioData[_jtacGroupName]) - end - - local _enemyPoint = _enemyUnit:getPoint() - local randomCircleDiam = 30; - trigger.action.smoke({ x = _enemyPoint.x + math.random(randomCircleDiam,-randomCircleDiam), y = _enemyPoint.y + 2.0, z = _enemyPoint.z + math.random(randomCircleDiam,-randomCircleDiam)}, 2) - end + local _enemyPoint = _enemyUnit:getPoint() + local randomCircleDiam = 30; + trigger.action.smoke( + { x = _enemyPoint.x + math.random(randomCircleDiam, -randomCircleDiam), y = _enemyPoint.y + 2.0, z = + _enemyPoint.z + math.random(randomCircleDiam, -randomCircleDiam) }, 2) end - end + end +end + ctld.jtacSpecialOptions.smokeMarker.setter = ctld.setSmokeOnTarget function ctld.setJTAC9Line(_args) - local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) - if parsedArgs then + local parsedArgs = ctld.setSpecialOptionArgsCheck(_args) + if parsedArgs then + local _jtacGroupName = parsedArgs.jtacGroupName - local _jtacGroupName = parsedArgs.jtacGroupName - - ctld.getJTACStatus({nil, _jtacGroupName}) - end + ctld.getJTACStatus({ nil, _jtacGroupName }) + end end + ctld.jtacSpecialOptions._9Line.setter = ctld.setJTAC9Line function ctld.setGrpROE(_grp, _ROE) - if _grp == nil then - ctld.logError("ctld.setGrpROE called with a nil group") - return - end + if _grp == nil then + ctld.logError("ctld.setGrpROE called with a nil group") + return + end - if _ROE == nil then - _ROE = AI.Option.Ground.val.ROE.OPEN_FIRE - end + if _ROE == nil then + _ROE = AI.Option.Ground.val.ROE.OPEN_FIRE + end - if _grp and _grp:isExist() == true and #_grp:getUnits() > 0 then -- check if the group truly exists - local _controller = _grp:getController(); - Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) - Controller.setOption(_controller, AI.Option.Ground.id.ROE, _ROE) - _controller:setTask(_grp) - end + if _grp and _grp:isExist() == true and #_grp:getUnits() > 0 then -- check if the group truly exists + local _controller = _grp:getController(); + Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) + Controller.setOption(_controller, AI.Option.Ground.id.ROE, _ROE) + _controller:setTask(_grp) + end end function ctld.isInfantry(_unit) + local _typeName = _unit:getTypeName() - local _typeName = _unit:getTypeName() + --type coerce tostring + _typeName = string.lower(_typeName .. "") - --type coerce tostring - _typeName = string.lower(_typeName .. "") + local _soldierType = { "infantry", "paratrooper", "stinger", "manpad", "mortar" } - local _soldierType = { "infantry", "paratrooper", "stinger", "manpad", "mortar" } - - for _key, _value in pairs(_soldierType) do - if string.match(_typeName, _value) then - return true - end + for _key, _value in pairs(_soldierType) do + if string.match(_typeName, _value) then + return true end + end - return false + return false end -- assume anything that isnt soldier is vehicle function ctld.isVehicle(_unit) + if ctld.isInfantry(_unit) then + return false + end - if ctld.isInfantry(_unit) then - return false - end - - return true + return true end -- The entered value can range from 1111 - 1788, @@ -7581,183 +7419,171 @@ end -- The range used to be bugged so its not 1 - 8 but 0 - 7. -- function below will use the range 1-7 just incase function ctld.generateLaserCode() + ctld.jtacGeneratedLaserCodes = {} - ctld.jtacGeneratedLaserCodes = {} + -- generate list of laser codes + local _code = 1511 - -- generate list of laser codes - local _code = 1511 + local _count = 1 - local _count = 1 + while _code < 1777 and _count < 30 do + while true do + _code = _code + 1 - while _code < 1777 and _count < 30 do + if not ctld.containsDigit(_code, 8) + and not ctld.containsDigit(_code, 9) + and not ctld.containsDigit(_code, 0) then + table.insert(ctld.jtacGeneratedLaserCodes, _code) - while true do - - _code = _code + 1 - - if not ctld.containsDigit(_code, 8) - and not ctld.containsDigit(_code, 9) - and not ctld.containsDigit(_code, 0) then - - table.insert(ctld.jtacGeneratedLaserCodes, _code) - - --env.info(_code.." Code") - break - end - end - _count = _count + 1 + --env.info(_code.." Code") + break + end end + _count = _count + 1 + end end function ctld.containsDigit(_number, _numberToFind) + local _thisNumber = _number + local _thisDigit = 0 - local _thisNumber = _number - local _thisDigit = 0 + while _thisNumber ~= 0 do + _thisDigit = _thisNumber % 10 + _thisNumber = math.floor(_thisNumber / 10) - while _thisNumber ~= 0 do - - _thisDigit = _thisNumber % 10 - _thisNumber = math.floor(_thisNumber / 10) - - if _thisDigit == _numberToFind then - return true - end + if _thisDigit == _numberToFind then + return true end + end - return false + return false end -- 200 - 400 in 10KHz -- 400 - 850 in 10 KHz -- 850 - 1250 in 50 KHz function ctld.generateVHFrequencies() + --ignore list + --list of all frequencies in KHZ that could conflict with + -- 191 - 1290 KHz, beacon range + local _skipFrequencies = { + 745, --Astrahan + 381, + 384, + 300.50, + 312.5, + 1175, + 342, + 735, + 300.50, + 353.00, + 440, + 795, + 525, + 520, + 690, + 625, + 291.5, + 300.50, + 435, + 309.50, + 920, + 1065, + 274, + 312.50, + 580, + 602, + 297.50, + 750, + 485, + 950, + 214, + 1025, 730, 995, 455, 307, 670, 329, 395, 770, + 380, 705, 300.5, 507, 740, 1030, 515, + 330, 309.5, + 348, 462, 905, 352, 1210, 942, 435, + 324, + 320, 420, 311, 389, 396, 862, 680, 297.5, + 920, 662, + 866, 907, 309.5, 822, 515, 470, 342, 1182, 309.5, 720, 528, + 337, 312.5, 830, 740, 309.5, 641, 312, 722, 682, 1050, + 1116, 935, 1000, 430, 577, + 326 -- Nevada + } - --ignore list - --list of all frequencies in KHZ that could conflict with - -- 191 - 1290 KHz, beacon range - local _skipFrequencies = { - 745, --Astrahan - 381, - 384, - 300.50, - 312.5, - 1175, - 342, - 735, - 300.50, - 353.00, - 440, - 795, - 525, - 520, - 690, - 625, - 291.5, - 300.50, - 435, - 309.50, - 920, - 1065, - 274, - 312.50, - 580, - 602, - 297.50, - 750, - 485, - 950, - 214, - 1025, 730, 995, 455, 307, 670, 329, 395, 770, - 380, 705, 300.5, 507, 740, 1030, 515, - 330, 309.5, - 348, 462, 905, 352, 1210, 942, 435, - 324, - 320, 420, 311, 389, 396, 862, 680, 297.5, - 920, 662, - 866, 907, 309.5, 822, 515, 470, 342, 1182, 309.5, 720, 528, - 337, 312.5, 830, 740, 309.5, 641, 312, 722, 682, 1050, - 1116, 935, 1000, 430, 577, - 326 -- Nevada - } + ctld.freeVHFFrequencies = {} + local _start = 200000 - ctld.freeVHFFrequencies = {} - local _start = 200000 - - -- first range - while _start < 400000 do - - -- skip existing NDB frequencies - local _found = false - for _, value in pairs(_skipFrequencies) do - if value * 1000 == _start then - _found = true - break - end - end - - - if _found == false then - table.insert(ctld.freeVHFFrequencies, _start) - end - - _start = _start + 10000 + -- first range + while _start < 400000 do + -- skip existing NDB frequencies + local _found = false + for _, value in pairs(_skipFrequencies) do + if value * 1000 == _start then + _found = true + break + end end - _start = 400000 - -- second range - while _start < 850000 do - -- skip existing NDB frequencies - local _found = false - for _, value in pairs(_skipFrequencies) do - if value * 1000 == _start then - _found = true - break - end - end - - if _found == false then - table.insert(ctld.freeVHFFrequencies, _start) - end - - - _start = _start + 10000 + if _found == false then + table.insert(ctld.freeVHFFrequencies, _start) end - _start = 850000 - -- third range - while _start <= 1250000 do + _start = _start + 10000 + end - -- skip existing NDB frequencies - local _found = false - for _, value in pairs(_skipFrequencies) do - if value * 1000 == _start then - _found = true - break - end - end - - if _found == false then - table.insert(ctld.freeVHFFrequencies, _start) - end - - _start = _start + 50000 + _start = 400000 + -- second range + while _start < 850000 do + -- skip existing NDB frequencies + local _found = false + for _, value in pairs(_skipFrequencies) do + if value * 1000 == _start then + _found = true + break + end end + + if _found == false then + table.insert(ctld.freeVHFFrequencies, _start) + end + + + _start = _start + 10000 + end + + _start = 850000 + -- third range + while _start <= 1250000 do + -- skip existing NDB frequencies + local _found = false + for _, value in pairs(_skipFrequencies) do + if value * 1000 == _start then + _found = true + break + end + end + + if _found == false then + table.insert(ctld.freeVHFFrequencies, _start) + end + + _start = _start + 50000 + end end -- 220 - 399 MHZ, increments of 0.5MHZ function ctld.generateUHFrequencies() + ctld.freeUHFFrequencies = {} + local _start = 220000000 - ctld.freeUHFFrequencies = {} - local _start = 220000000 - - while _start < 399000000 do - table.insert(ctld.freeUHFFrequencies, _start) - _start = _start + 500000 - end + while _start < 399000000 do + table.insert(ctld.freeUHFFrequencies, _start) + _start = _start + 500000 + end end - -- 220 - 399 MHZ, increments of 0.5MHZ -- -- first digit 3-7MHz -- -- second digit 0-5KHz @@ -7766,35 +7592,38 @@ end -- -- times by 10000 -- function ctld.generateFMFrequencies() + ctld.freeFMFrequencies = {} + local _start = 220000000 - ctld.freeFMFrequencies = {} - local _start = 220000000 + while _start < 399000000 do + _start = _start + 500000 + end - while _start < 399000000 do - - _start = _start + 500000 - end - - for _first = 3, 7 do - for _second = 0, 5 do - for _third = 0, 9 do - local _frequency = ((100 * _first) + (10 * _second) + _third) * 100000 --extra 0 because we didnt bother with 4th digit - table.insert(ctld.freeFMFrequencies, _frequency) - end - end + for _first = 3, 7 do + for _second = 0, 5 do + for _third = 0, 9 do + local _frequency = ((100 * _first) + (10 * _second) + _third) * + 100000 --extra 0 because we didnt bother with 4th digit + table.insert(ctld.freeFMFrequencies, _frequency) + end end + end end function ctld.getPositionString(_unit) - if ctld.JTAC_location == false then - return "" - end + if ctld.JTAC_location == false then + return "" + end - local _lat, _lon = coord.LOtoLL(_unit:getPosition().p) - local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) - local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_unit:getPosition().p)), 5) - local _TargetAlti = land.getHeight(mist.utils.makeVec2(_unit:getPoint())) - return " @ " .. _latLngStr .. " - MGRS " .. _mgrsString .. " - ALTI: " .. mist.utils.round(_TargetAlti, 0) .. " m / " .. mist.utils.round(_TargetAlti/0.3048, 0) .. " ft" + local _lat, _lon = coord.LOtoLL(_unit:getPosition().p) + local _latLngStr = mist.tostringLL(_lat, _lon, 3, ctld.location_DMS) + local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_unit:getPosition().p)), 5) + local _TargetAlti = land.getHeight(mist.utils.makeVec2(_unit:getPoint())) + return " @ " .. + _latLngStr .. + " - MGRS " .. + _mgrsString .. + " - ALTI: " .. mist.utils.round(_TargetAlti, 0) .. " m / " .. mist.utils.round(_TargetAlti / 0.3048, 0) .. " ft" end --********************************************************************** @@ -7808,172 +7637,193 @@ end ---------------------------------------------------------------------- --if ctld == nil then ctld = {} end if ctld.lastMarkId == nil then - ctld.lastMarkId = 0 + ctld.lastMarkId = 0 end -- ***************** RECON CONFIGURATION ***************** -ctld.reconF10Menu = true -- enables F10 RECON menu -ctld.reconMenuName = ctld.i18n_translate("RECON") --name of the CTLD JTAC radio menu -ctld.reconRadioAdded = {} --stores the groups that have had the radio menu added -ctld.reconLosSearchRadius = 2000 -- search radius 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.reconF10Menu = true -- enables F10 RECON menu +ctld.reconMenuName = ctld.i18n_translate("RECON") --name of the CTLD JTAC radio menu +ctld.reconRadioAdded = {} --stores the groups that have had the radio menu added +ctld.reconLosSearchRadius = 2000 -- search radius 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.reconLastScheduleIdAutoRefresh = 0 ---- F10 RECON Menus ------------------------------------------------------------------ function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) - if ctld.reconF10Menu then - if _side == 1 or _side == 2 then - local _players = coalition.getPlayers(_side) - if _players ~= nil then - for _, _playerUnit in pairs(_players) do - local _groupId = ctld.getGroupId(_playerUnit) - if _groupId then - if ctld.reconRadioAdded[tostring(_groupId)] == nil then - ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", ctld.p(_playerUnit:getName())) - local RECONpath = missionCommands.addSubMenuForGroup(_groupId, ctld.reconMenuName) - missionCommands.addCommandForGroup(_groupId, - ctld.i18n_translate("Show targets in LOS (refresh)"), RECONpath, - ctld.reconRefreshTargetsInLosOnF10Map, { - _groupId = _groupId, - _playerUnit = _playerUnit, - _searchRadius = ctld.reconLosSearchRadius, - _markRadius = ctld.reconLosMarkRadius, - _boolRemove = true - }) - missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Hide targets in LOS"), RECONpath, ctld.reconRemoveTargetsInLosOnF10Map, _playerUnit) - if ctld.reconAutoRefreshLosTargetMarks then - missionCommands.addCommandForGroup(_groupId, - ctld.i18n_translate("STOP autoRefresh targets in LOS"), RECONpath, - ctld.reconStopAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - ctld.reconLosSearchRadius, - ctld.reconLosMarkRadius, - true) - else - missionCommands.addCommandForGroup(_groupId, - ctld.i18n_translate("START autoRefresh targets in LOS"), RECONpath, - ctld.reconStartAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - ctld.reconLosSearchRadius, - ctld.reconLosMarkRadius, - true - ) - end - ctld.reconRadioAdded[tostring(_groupId)] = timer.getTime() --fetch the time to check for a regular refresh - end - end - end + if ctld.reconF10Menu then + if _side == 1 or _side == 2 then + local _players = coalition.getPlayers(_side) + if _players ~= nil then + for _, _playerUnit in pairs(_players) do + local _groupId = ctld.getGroupId(_playerUnit) + if _groupId then + if ctld.reconRadioAdded[tostring(_groupId)] == nil then + ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", + ctld.p(_playerUnit:getName())) + local RECONpath = missionCommands.addSubMenuForGroup(_groupId, ctld.reconMenuName) + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("Show targets in LOS (refresh)"), RECONpath, + ctld.reconRefreshTargetsInLosOnF10Map, { + _groupId = _groupId, + _playerUnit = _playerUnit, + _searchRadius = ctld.reconLosSearchRadius, + _markRadius = ctld.reconLosMarkRadius, + _boolRemove = true + }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Hide targets in LOS"), + RECONpath, ctld.reconRemoveTargetsInLosOnF10Map, _playerUnit) + if ctld.reconAutoRefreshLosTargetMarks then + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("STOP autoRefresh targets in LOS"), RECONpath, + ctld.reconStopAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + ctld.reconLosSearchRadius, + ctld.reconLosMarkRadius, + true) + else + missionCommands.addCommandForGroup(_groupId, + ctld.i18n_translate("START autoRefresh targets in LOS"), RECONpath, + ctld.reconStartAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + ctld.reconLosSearchRadius, + ctld.reconLosMarkRadius, + true + ) + end + ctld.reconRadioAdded[tostring(_groupId)] = timer.getTime() --fetch the time to check for a regular refresh end + end end + end end + end end + -------------------------------------------------------------------- function ctld.reconStopAutorefreshTargetsInLosOnF10Map(_groupId, _playerUnit, _searchRadius, _markRadius, _boolRemove) - ctld.reconAutoRefreshLosTargetMarks = false + ctld.reconAutoRefreshLosTargetMarks = false - if ctld.reconLastScheduleIdAutoRefresh ~= 0 then - timer.removeFunction(ctld.reconLastScheduleIdAutoRefresh) -- reset last schedule - end + if ctld.reconLastScheduleIdAutoRefresh ~= 0 then + timer.removeFunction(ctld.reconLastScheduleIdAutoRefresh) -- reset last schedule + end - ctld.reconRemoveTargetsInLosOnF10Map(_playerUnit) - missionCommands.removeItemForGroup(_groupId, {ctld.reconMenuName, ctld.i18n_translate("STOP autoRefresh targets in LOS")}) - missionCommands.addCommandForGroup( _groupId, ctld.i18n_translate("START autoRefresh targets in LOS"), {ctld.reconMenuName}, - ctld.reconStartAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - _searchRadius, - _markRadius, - _boolRemove) + ctld.reconRemoveTargetsInLosOnF10Map(_playerUnit) + missionCommands.removeItemForGroup(_groupId, + { ctld.reconMenuName, ctld.i18n_translate("STOP autoRefresh targets in LOS") }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("START autoRefresh targets in LOS"), + { ctld.reconMenuName }, + ctld.reconStartAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + _searchRadius, + _markRadius, + _boolRemove) end + -------------------------------------------------------------------- function ctld.reconStartAutorefreshTargetsInLosOnF10Map(_groupId, _playerUnit, _searchRadius, _markRadius, _boolRemove) - ctld.reconAutoRefreshLosTargetMarks = true - ctld.reconRefreshTargetsInLosOnF10Map({ _groupId = _groupId, - _playerUnit = _playerUnit, - _searchRadius = _searchRadius or ctld.reconLosSearchRadius, - _markRadius = _markRadius or ctld.reconLosMarkRadius, - _boolRemove = _boolRemove or true}, - timer.getTime()) - missionCommands.removeItemForGroup( _groupId, {ctld.reconMenuName, ctld.i18n_translate("START autoRefresh targets in LOS")}) - missionCommands.addCommandForGroup( _groupId, ctld.i18n_translate("STOP autoRefresh targets in LOS"), {ctld.reconMenuName}, - ctld.reconStopAutorefreshTargetsInLosOnF10Map, - _groupId, - _playerUnit, - _searchRadius, - _markRadius, - _boolRemove) + ctld.reconAutoRefreshLosTargetMarks = true + ctld.reconRefreshTargetsInLosOnF10Map({ + _groupId = _groupId, + _playerUnit = _playerUnit, + _searchRadius = _searchRadius or ctld.reconLosSearchRadius, + _markRadius = _markRadius or ctld.reconLosMarkRadius, + _boolRemove = _boolRemove or true + }, + timer.getTime()) + missionCommands.removeItemForGroup(_groupId, + { ctld.reconMenuName, ctld.i18n_translate("START autoRefresh targets in LOS") }) + missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("STOP autoRefresh targets in LOS"), + { ctld.reconMenuName }, + ctld.reconStopAutorefreshTargetsInLosOnF10Map, + _groupId, + _playerUnit, + _searchRadius, + _markRadius, + _boolRemove) end + -------------------------------------------------------------------- -function ctld.reconShowTargetsInLosOnF10Map(_playerUnit, _searchRadius, _markRadius) -- _groupId targeting - -- _searchRadius and _markRadius in meters - if _playerUnit then - local TargetsInLOS = {} +function ctld.reconShowTargetsInLosOnF10Map(_playerUnit, _searchRadius, _markRadius) -- _groupId targeting + -- _searchRadius and _markRadius in meters + if _playerUnit then + local TargetsInLOS = {} - local enemyColor = "red" - local color = {1, 0, 0, 0.2} -- red + local enemyColor = "red" + local color = { 1, 0, 0, 0.2 } -- red - if _playerUnit:getCoalition() == 1 then - enemyColor = "blue" - color = {51/255, 51/255, 1, 0.2} -- blue - end - - local t = mist.getUnitsLOS({_playerUnit:getName()}, 180, mist.makeUnitTable({'['..enemyColor..'][vehicle]'}),180, _searchRadius) - - local MarkIds = {} - if t then - for i=1, #t do -- for each unit having los on enemies - for j=1, #t[i].vis do -- for each enemy unit in los - local targetPoint = t[i].vis[j]:getPoint() -- point of each target on LOS - ctld.lastMarkId = ctld.lastMarkId + 1 - trigger.action.circleToAll(_playerUnit:getCoalition(), ctld.lastMarkId, targetPoint, _markRadius , color, color, 1, false, nil) - MarkIds[#MarkIds+1] = ctld.lastMarkId - TargetsInLOS[#TargetsInLOS+1] = { targetObject = t[i].vis[j]:getName(), - targetTypeName = t[i].vis[j]:getTypeName(), - targetPoint = targetPoint} - end - end - end - mist.DBs.humansByName[_playerUnit:getName()].losMarkIds = MarkIds -- store list of marksIds generated and showed on F10 map - return TargetsInLOS - else - return nil + if _playerUnit:getCoalition() == 1 then + enemyColor = "blue" + color = { 51 / 255, 51 / 255, 1, 0.2 } -- blue end + + local t = mist.getUnitsLOS({ _playerUnit:getName() }, 180, mist.makeUnitTable({ '[' .. enemyColor .. '][vehicle]' }), + 180, _searchRadius) + + local MarkIds = {} + if t then + for i = 1, #t do -- for each unit having los on enemies + for j = 1, #t[i].vis do -- for each enemy unit in los + local targetPoint = t[i].vis[j]:getPoint() -- point of each target on LOS + ctld.lastMarkId = ctld.lastMarkId + 1 + trigger.action.circleToAll(_playerUnit:getCoalition(), ctld.lastMarkId, targetPoint, _markRadius, + color, color, 1, false, nil) + MarkIds[#MarkIds + 1] = ctld.lastMarkId + TargetsInLOS[#TargetsInLOS + 1] = { + targetObject = t[i].vis[j]:getName(), + targetTypeName = t[i].vis[j]:getTypeName(), + targetPoint = targetPoint + } + end + end + end + mist.DBs.humansByName[_playerUnit:getName()].losMarkIds = + MarkIds -- store list of marksIds generated and showed on F10 map + return TargetsInLOS + else + return nil + end end + --------------------------------------------------------- function ctld.reconRemoveTargetsInLosOnF10Map(_playerUnit) - local unitName = _playerUnit:getName() - if mist.DBs.humansByName[unitName].losMarkIds then - for i=1, #mist.DBs.humansByName[unitName].losMarkIds do -- for each unit having los on enemies - trigger.action.removeMark(mist.DBs.humansByName[unitName].losMarkIds[i]) - end - mist.DBs.humansByName[unitName].losMarkIds = nil + local unitName = _playerUnit:getName() + if mist.DBs.humansByName[unitName].losMarkIds then + for i = 1, #mist.DBs.humansByName[unitName].losMarkIds do -- for each unit having los on enemies + trigger.action.removeMark(mist.DBs.humansByName[unitName].losMarkIds[i]) end + mist.DBs.humansByName[unitName].losMarkIds = nil + end end + --------------------------------------------------------- -function ctld.reconRefreshTargetsInLosOnF10Map(_params, _t) -- _params._playerUnit targeting - -- _params._searchRadius and _params._markRadius in meters - -- _params._boolRemove = true to remove previous marksIds - if _t == nil then _t = timer.getTime() end +function ctld.reconRefreshTargetsInLosOnF10Map(_params, _t) -- _params._playerUnit targeting + -- _params._searchRadius and _params._markRadius in meters + -- _params._boolRemove = true to remove previous marksIds + if _t == nil then _t = timer.getTime() end - if ctld.reconAutoRefreshLosTargetMarks then -- to follow mobile enemy targets - ctld.reconLastScheduleIdAutoRefresh = timer.scheduleFunction(ctld.reconRefreshTargetsInLosOnF10Map, {_groupId = _params._groupId, - _playerUnit = _params._playerUnit, - _searchRadius = _params._searchRadius, - _markRadius = _params._markRadius, - _boolRemove = _params._boolRemove - }, - timer.getTime() + 10) - end + if ctld.reconAutoRefreshLosTargetMarks then -- to follow mobile enemy targets + ctld.reconLastScheduleIdAutoRefresh = timer.scheduleFunction(ctld.reconRefreshTargetsInLosOnF10Map, + { + _groupId = _params._groupId, + _playerUnit = _params._playerUnit, + _searchRadius = _params._searchRadius, + _markRadius = _params._markRadius, + _boolRemove = _params._boolRemove + }, + timer.getTime() + 10) + end - if _params._boolRemove == true then - ctld.reconRemoveTargetsInLosOnF10Map(_params._playerUnit) - end + if _params._boolRemove == true then + ctld.reconRemoveTargetsInLosOnF10Map(_params._playerUnit) + end - return ctld.reconShowTargetsInLosOnF10Map(_params._playerUnit, _params._searchRadius, _params._markRadius) -- returns TargetsInLOS table + return ctld.reconShowTargetsInLosOnF10Map(_params._playerUnit, _params._searchRadius, _params._markRadius) -- returns TargetsInLOS table end + --- test ------------------------------------------------------ --local unitName = "uh2-1" --"uh1-1" --"uh2-1" --ctld.reconShowTargetsInLosOnF10Map(Unit.getByName(unitName),2000,200) @@ -7983,438 +7833,429 @@ end -- ***************** SETUP SCRIPT **************** function ctld.initialize() - ctld.logInfo(string.format("Initializing version %s", ctld.Version)) + ctld.logInfo(string.format("Initializing version %s", ctld.Version)) - assert(mist ~= nil, "\n\n** HEY MISSION-DESIGNER! **\n\nMiST has not been loaded!\n\nMake sure MiST 3.6 or higher is running\n*before* running this script!\n") + assert(mist ~= nil, + "\n\n** HEY MISSION-DESIGNER! **\n\nMiST has not been loaded!\n\nMake sure MiST 3.6 or higher is running\n*before* running this script!\n") - ctld.addedTo = {} - ctld.spawnedCratesRED = {} -- use to store crates that have been spawned - ctld.spawnedCratesBLUE = {} -- use to store crates that have been spawned + ctld.addedTo = {} + ctld.spawnedCratesRED = {} -- use to store crates that have been spawned + ctld.spawnedCratesBLUE = {} -- use to store crates that have been spawned - ctld.droppedTroopsRED = {} -- stores dropped troop groups - ctld.droppedTroopsBLUE = {} -- stores dropped troop groups + ctld.droppedTroopsRED = {} -- stores dropped troop groups + ctld.droppedTroopsBLUE = {} -- stores dropped troop groups - ctld.droppedVehiclesRED = {} -- stores vehicle groups for c-130 / hercules - ctld.droppedVehiclesBLUE = {} -- stores vehicle groups for c-130 / hercules + ctld.droppedVehiclesRED = {} -- stores vehicle groups for c-130 / hercules + ctld.droppedVehiclesBLUE = {} -- stores vehicle groups for c-130 / hercules - ctld.inTransitTroops = {} + ctld.inTransitTroops = {} - ctld.inTransitFOBCrates = {} + ctld.inTransitFOBCrates = {} - ctld.inTransitSlingLoadCrates = {} -- stores crates that are being transported by helicopters for alternative to real slingload + ctld.inTransitSlingLoadCrates = {} -- stores crates that are being transported by helicopters for alternative to real slingload - ctld.droppedFOBCratesRED = {} - ctld.droppedFOBCratesBLUE = {} + ctld.droppedFOBCratesRED = {} + ctld.droppedFOBCratesBLUE = {} - ctld.builtFOBS = {} -- stores fully built fobs + ctld.builtFOBS = {} -- stores fully built fobs - ctld.completeAASystems = {} -- stores complete spawned groups from multiple crates + ctld.completeAASystems = {} -- stores complete spawned groups from multiple crates - ctld.fobBeacons = {} -- stores FOB radio beacon details, refreshed every 60 seconds + ctld.fobBeacons = {} -- stores FOB radio beacon details, refreshed every 60 seconds - ctld.deployedRadioBeacons = {} -- stores details of deployed radio beacons + ctld.deployedRadioBeacons = {} -- stores details of deployed radio beacons - ctld.beaconCount = 1 + ctld.beaconCount = 1 - ctld.usedUHFFrequencies = {} - ctld.usedVHFFrequencies = {} - ctld.usedFMFrequencies = {} + ctld.usedUHFFrequencies = {} + ctld.usedVHFFrequencies = {} + ctld.usedFMFrequencies = {} - ctld.freeUHFFrequencies = {} - ctld.freeVHFFrequencies = {} - ctld.freeFMFrequencies = {} + ctld.freeUHFFrequencies = {} + ctld.freeVHFFrequencies = {} + ctld.freeFMFrequencies = {} - --used to lookup what the crate will contain - ctld.crateLookupTable = {} + --used to lookup what the crate will contain + ctld.crateLookupTable = {} - ctld.extractZones = {} -- stored extract zones + ctld.extractZones = {} -- stored extract zones - ctld.missionEditorCargoCrates = {} --crates added by mission editor for triggering cratesinzone - ctld.hoverStatus = {} -- tracks status of a helis hover above a crate + ctld.missionEditorCargoCrates = {} --crates added by mission editor for triggering cratesinzone + ctld.hoverStatus = {} -- tracks status of a helis hover above a crate - ctld.callbacks = {} -- function callback + ctld.callbacks = {} -- function callback - -- Remove intransit troops when heli / cargo plane dies - --ctld.eventHandler = {} - --function ctld.eventHandler:onEvent(_event) - -- - -- if _event == nil or _event.initiator == nil then - -- env.info("CTLD null event") - -- elseif _event.id == 9 then - -- -- Pilot dead - -- ctld.inTransitTroops[_event.initiator:getName()] = nil - -- - -- elseif world.event.S_EVENT_EJECTION == _event.id or _event.id == 8 then - -- -- env.info("Event unit - Pilot Ejected or Unit Dead") - -- ctld.inTransitTroops[_event.initiator:getName()] = nil - -- - -- -- env.info(_event.initiator:getName()) - -- end - -- - --end + -- Remove intransit troops when heli / cargo plane dies + --ctld.eventHandler = {} + --function ctld.eventHandler:onEvent(_event) + -- + -- if _event == nil or _event.initiator == nil then + -- env.info("CTLD null event") + -- elseif _event.id == 9 then + -- -- Pilot dead + -- ctld.inTransitTroops[_event.initiator:getName()] = nil + -- + -- elseif world.event.S_EVENT_EJECTION == _event.id or _event.id == 8 then + -- -- env.info("Event unit - Pilot Ejected or Unit Dead") + -- ctld.inTransitTroops[_event.initiator:getName()] = nil + -- + -- -- env.info(_event.initiator:getName()) + -- end + -- + --end - -- create crate lookup table - for _subMenuName, _crates in pairs(ctld.spawnableCrates) do - - for _, _crate in pairs(_crates) do - -- convert number to string otherwise we'll have a pointless giant - -- table. String means 'hashmap' so it will only contain the right number of elements - if _crate.multiple then - local _totalWeight = 0 - for _, _weight in pairs(_crate.multiple) do - _totalWeight = _totalWeight + _weight - end - _crate.weight = _totalWeight - end - ctld.crateLookupTable[tostring(_crate.weight)] = _crate + -- create crate lookup table + for _subMenuName, _crates in pairs(ctld.spawnableCrates) do + for _, _crate in pairs(_crates) do + -- convert number to string otherwise we'll have a pointless giant + -- table. String means 'hashmap' so it will only contain the right number of elements + if _crate.multiple then + local _totalWeight = 0 + for _, _weight in pairs(_crate.multiple) do + _totalWeight = _totalWeight + _weight end + _crate.weight = _totalWeight + end + ctld.crateLookupTable[tostring(_crate.weight)] = _crate + end + end + + + --sort out pickup zones + for _, _zone in pairs(ctld.pickupZones) do + local _zoneName = _zone[1] + local _zoneColor = _zone[2] + local _zoneActive = _zone[4] + + if _zoneColor == "green" then + _zone[2] = trigger.smokeColor.Green + elseif _zoneColor == "red" then + _zone[2] = trigger.smokeColor.Red + elseif _zoneColor == "white" then + _zone[2] = trigger.smokeColor.White + elseif _zoneColor == "orange" then + _zone[2] = trigger.smokeColor.Orange + elseif _zoneColor == "blue" then + _zone[2] = trigger.smokeColor.Blue + else + _zone[2] = -1 -- no smoke colour + end + + -- add in counter for troops or units + if _zone[3] == -1 then + _zone[3] = 10000; + end + + -- change active to 1 / 0 + if _zoneActive == "yes" then + _zone[4] = 1 + else + _zone[4] = 0 + end + end + + --sort out dropoff zones + for _, _zone in pairs(ctld.dropOffZones) do + local _zoneColor = _zone[2] + + if _zoneColor == "green" then + _zone[2] = trigger.smokeColor.Green + elseif _zoneColor == "red" then + _zone[2] = trigger.smokeColor.Red + elseif _zoneColor == "white" then + _zone[2] = trigger.smokeColor.White + elseif _zoneColor == "orange" then + _zone[2] = trigger.smokeColor.Orange + elseif _zoneColor == "blue" then + _zone[2] = trigger.smokeColor.Blue + else + _zone[2] = -1 -- no smoke colour + end + + --mark as active for refresh smoke logic to work + _zone[4] = 1 + end + + --sort out waypoint zones + for _, _zone in pairs(ctld.wpZones) do + local _zoneColor = _zone[2] + + if _zoneColor == "green" then + _zone[2] = trigger.smokeColor.Green + elseif _zoneColor == "red" then + _zone[2] = trigger.smokeColor.Red + elseif _zoneColor == "white" then + _zone[2] = trigger.smokeColor.White + elseif _zoneColor == "orange" then + _zone[2] = trigger.smokeColor.Orange + elseif _zoneColor == "blue" then + _zone[2] = trigger.smokeColor.Blue + else + _zone[2] = -1 -- no smoke colour + end + + --mark as active for refresh smoke logic to work + -- change active to 1 / 0 + if _zone[3] == "yes" then + _zone[3] = 1 + else + _zone[3] = 0 + end + end + + -- Sort out extractable groups + for _, _groupName in pairs(ctld.extractableGroups) do + local _group = Group.getByName(_groupName) + + if _group ~= nil then + if _group:getCoalition() == 1 then + table.insert(ctld.droppedTroopsRED, _group:getName()) + else + table.insert(ctld.droppedTroopsBLUE, _group:getName()) + end + end + end + + + -- Seperate troop teams into red and blue for random AI pickups + if ctld.allowRandomAiTeamPickups == true then + ctld.redTeams = {} + ctld.blueTeams = {} + for _, _loadGroup in pairs(ctld.loadableGroups) do + if not _loadGroup.side then + table.insert(ctld.redTeams, _) + table.insert(ctld.blueTeams, _) + elseif _loadGroup.side == 1 then + table.insert(ctld.redTeams, _) + elseif _loadGroup.side == 2 then + table.insert(ctld.blueTeams, _) + end + end + end + + -- add total count + + for _, _loadGroup in pairs(ctld.loadableGroups) do + _loadGroup.total = 0 + if _loadGroup.aa then + _loadGroup.total = _loadGroup.aa + _loadGroup.total + end + + if _loadGroup.inf then + _loadGroup.total = _loadGroup.inf + _loadGroup.total end - --sort out pickup zones - for _, _zone in pairs(ctld.pickupZones) do - - local _zoneName = _zone[1] - local _zoneColor = _zone[2] - local _zoneActive = _zone[4] - - if _zoneColor == "green" then - _zone[2] = trigger.smokeColor.Green - elseif _zoneColor == "red" then - _zone[2] = trigger.smokeColor.Red - elseif _zoneColor == "white" then - _zone[2] = trigger.smokeColor.White - elseif _zoneColor == "orange" then - _zone[2] = trigger.smokeColor.Orange - elseif _zoneColor == "blue" then - _zone[2] = trigger.smokeColor.Blue - else - _zone[2] = -1 -- no smoke colour - end - - -- add in counter for troops or units - if _zone[3] == -1 then - _zone[3] = 10000; - end - - -- change active to 1 / 0 - if _zoneActive == "yes" then - _zone[4] = 1 - else - _zone[4] = 0 - end + if _loadGroup.mg then + _loadGroup.total = _loadGroup.mg + _loadGroup.total end - --sort out dropoff zones - for _, _zone in pairs(ctld.dropOffZones) do - - local _zoneColor = _zone[2] - - if _zoneColor == "green" then - _zone[2] = trigger.smokeColor.Green - elseif _zoneColor == "red" then - _zone[2] = trigger.smokeColor.Red - elseif _zoneColor == "white" then - _zone[2] = trigger.smokeColor.White - elseif _zoneColor == "orange" then - _zone[2] = trigger.smokeColor.Orange - elseif _zoneColor == "blue" then - _zone[2] = trigger.smokeColor.Blue - else - _zone[2] = -1 -- no smoke colour - end - - --mark as active for refresh smoke logic to work - _zone[4] = 1 + if _loadGroup.at then + _loadGroup.total = _loadGroup.at + _loadGroup.total end - --sort out waypoint zones - for _, _zone in pairs(ctld.wpZones) do - - local _zoneColor = _zone[2] - - if _zoneColor == "green" then - _zone[2] = trigger.smokeColor.Green - elseif _zoneColor == "red" then - _zone[2] = trigger.smokeColor.Red - elseif _zoneColor == "white" then - _zone[2] = trigger.smokeColor.White - elseif _zoneColor == "orange" then - _zone[2] = trigger.smokeColor.Orange - elseif _zoneColor == "blue" then - _zone[2] = trigger.smokeColor.Blue - else - _zone[2] = -1 -- no smoke colour - end - - --mark as active for refresh smoke logic to work - -- change active to 1 / 0 - if _zone[3] == "yes" then - _zone[3] = 1 - else - _zone[3] = 0 - end + if _loadGroup.mortar then + _loadGroup.total = _loadGroup.mortar + _loadGroup.total end + end - -- Sort out extractable groups - for _, _groupName in pairs(ctld.extractableGroups) do - local _group = Group.getByName(_groupName) + -- Scheduled functions (run cyclically) -- but hold execution for a second so we can override parts - if _group ~= nil then + timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 1) + timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 5) - if _group:getCoalition() == 1 then - table.insert(ctld.droppedTroopsRED, _group:getName()) - else - table.insert(ctld.droppedTroopsBLUE, _group:getName()) - end - end + timer.scheduleFunction(function() + timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.updateDynamicLogisticUnitsZones, nil, timer.getTime() + 5) + if ctld.enableCrates == true and ctld.hoverPickup == true then + timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end - - - -- Seperate troop teams into red and blue for random AI pickups - if ctld.allowRandomAiTeamPickups == true then - ctld.redTeams = {} - ctld.blueTeams = {} - for _,_loadGroup in pairs(ctld.loadableGroups) do - if not _loadGroup.side then - table.insert(ctld.redTeams, _) - table.insert(ctld.blueTeams, _) - elseif _loadGroup.side == 1 then - table.insert(ctld.redTeams, _) - elseif _loadGroup.side == 2 then - table.insert(ctld.blueTeams, _) - end - end + if ctld.enableRepackingVehicles == true then + timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end + end, nil, timer.getTime() + 1) - -- add total count + --event handler for deaths + --world.addEventHandler(ctld.eventHandler) - for _,_loadGroup in pairs(ctld.loadableGroups) do + --env.info("CTLD event handler added") - _loadGroup.total = 0 - if _loadGroup.aa then - _loadGroup.total = _loadGroup.aa + _loadGroup.total - end - - if _loadGroup.inf then - _loadGroup.total = _loadGroup.inf + _loadGroup.total - end - - - if _loadGroup.mg then - _loadGroup.total = _loadGroup.mg + _loadGroup.total - end - - if _loadGroup.at then - _loadGroup.total = _loadGroup.at + _loadGroup.total - end - - if _loadGroup.mortar then - _loadGroup.total = _loadGroup.mortar + _loadGroup.total - end - - end - - - -- Scheduled functions (run cyclically) -- but hold execution for a second so we can override parts - - timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 1) - timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 5) - - timer.scheduleFunction(function() - timer.scheduleFunction(ctld.refreshRadioBeacons, nil, timer.getTime() + 5) - timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 5) - timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 5) - timer.scheduleFunction(ctld.updateDynamicLogisticUnitsZones, nil, timer.getTime() + 5) - if ctld.enableCrates == true and ctld.hoverPickup == true then - timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) - end - if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) - end - end,nil, timer.getTime()+1 ) - - --event handler for deaths - --world.addEventHandler(ctld.eventHandler) - - --env.info("CTLD event handler added") - - env.info("Generating Laser Codes") - ctld.generateLaserCode() - env.info("Generated Laser Codes") + env.info("Generating Laser Codes") + ctld.generateLaserCode() + env.info("Generated Laser Codes") - env.info("Generating UHF Frequencies") - ctld.generateUHFrequencies() - env.info("Generated UHF Frequencies") + env.info("Generating UHF Frequencies") + ctld.generateUHFrequencies() + env.info("Generated UHF Frequencies") - env.info("Generating VHF Frequencies") - ctld.generateVHFrequencies() - env.info("Generated VHF Frequencies") + env.info("Generating VHF Frequencies") + ctld.generateVHFrequencies() + env.info("Generated VHF Frequencies") - env.info("Generating FM Frequencies") - ctld.generateFMFrequencies() - env.info("Generated FM Frequencies") + env.info("Generating FM Frequencies") + ctld.generateFMFrequencies() + env.info("Generated FM Frequencies") - -- Search for crates - -- Crates are NOT returned by coalition.getStaticObjects() for some reason - -- Search for crates in the mission editor instead - env.info("Searching for Crates") - for _coalitionName, _coalitionData in pairs(env.mission.coalition) do - - if (_coalitionName == 'red' or _coalitionName == 'blue') - and type(_coalitionData) == 'table' then - if _coalitionData.country then --there is a country table - for _, _countryData in pairs(_coalitionData.country) do - - if type(_countryData) == 'table' then - for _objectTypeName, _objectTypeData in pairs(_countryData) do - if _objectTypeName == "static" then - - if ((type(_objectTypeData) == 'table') - and _objectTypeData.group - and (type(_objectTypeData.group) == 'table') - and (#_objectTypeData.group > 0)) then - - for _groupId, _group in pairs(_objectTypeData.group) do - if _group and _group.units and type(_group.units) == 'table' then - for _unitNum, _unit in pairs(_group.units) do - if _unit.canCargo == true then - local _cargoName = env.getValueDictByKey(_unit.name) - ctld.missionEditorCargoCrates[_cargoName] = _cargoName - env.info("Crate Found: " .. _unit.name.." - Unit: ".._cargoName) - end - end - end - end - end + -- Search for crates + -- Crates are NOT returned by coalition.getStaticObjects() for some reason + -- Search for crates in the mission editor instead + env.info("Searching for Crates") + for _coalitionName, _coalitionData in pairs(env.mission.coalition) do + if (_coalitionName == 'red' or _coalitionName == 'blue') + and type(_coalitionData) == 'table' then + if _coalitionData.country then --there is a country table + for _, _countryData in pairs(_coalitionData.country) do + if type(_countryData) == 'table' then + for _objectTypeName, _objectTypeData in pairs(_countryData) do + if _objectTypeName == "static" then + if ((type(_objectTypeData) == 'table') + and _objectTypeData.group + and (type(_objectTypeData.group) == 'table') + and (#_objectTypeData.group > 0)) then + for _groupId, _group in pairs(_objectTypeData.group) do + if _group and _group.units and type(_group.units) == 'table' then + for _unitNum, _unit in pairs(_group.units) do + if _unit.canCargo == true then + local _cargoName = env.getValueDictByKey(_unit.name) + ctld.missionEditorCargoCrates[_cargoName] = _cargoName + env.info("Crate Found: " .. _unit.name .. " - Unit: " .. _cargoName) end + end end + end end + end end - end + end end + end end - env.info("END search for crates") + end + env.info("END search for crates") - -- register event handler - ctld.logInfo("registering event handler") - world.addEventHandler(ctld.eventHandler) - env.info("CTLD READY") + -- register event handler + ctld.logInfo("registering event handler") + world.addEventHandler(ctld.eventHandler) + env.info("CTLD READY") end --- Handle world events. ctld.eventHandler = {} function ctld.eventHandler:onEvent(event) - ctld.logTrace("ctld.eventHandler:onEvent()") - if event == nil then - ctld.logError("Event handler was called with a nil event!") - return - end + ctld.logTrace("ctld.eventHandler:onEvent()") + if event == nil then + ctld.logError("Event handler was called with a nil event!") + return + end - local eventName = "unknown" - -- check that we know the event - if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then - eventName = "S_EVENT_PLAYER_ENTER_UNIT" - elseif event.id == world.event.S_EVENT_BIRTH then - eventName = "S_EVENT_BIRTH" - else - ctld.logTrace("Ignoring event %s", ctld.p(event)) - return - end - ctld.logDebug("caught event %s: %s", ctld.p(eventName), ctld.p(event)) + local eventName = "unknown" + -- check that we know the event + if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then + eventName = "S_EVENT_PLAYER_ENTER_UNIT" + elseif event.id == world.event.S_EVENT_BIRTH then + eventName = "S_EVENT_BIRTH" + else + ctld.logTrace("Ignoring event %s", ctld.p(event)) + return + end + ctld.logDebug("caught event %s: %s", ctld.p(eventName), ctld.p(event)) - -- find the originator unit - local unitName = nil - if event.initiator ~= nil and event.initiator.getName then - unitName = event.initiator:getName() - ctld.logDebug("unitName = [%s]", ctld.p(unitName)) - end - if not unitName then - ctld.logInfo("no unitname found in event %s", ctld.p(event)) - return - end + -- find the originator unit + local unitName = nil + if event.initiator ~= nil and event.initiator.getName then + unitName = event.initiator:getName() + ctld.logDebug("unitName = [%s]", ctld.p(unitName)) + end + if not unitName then + ctld.logInfo("no unitname found in event %s", ctld.p(event)) + return + end - local function processHumanPlayer() - ctld.logTrace("in the 'processHumanPlayer' function") - if mist.DBs.humansByName[unitName] then -- it's a human unit - ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) - local _unit = Unit.getByName(unitName) - if _unit ~= nil then - -- assign transport pilot - ctld.logTrace("_unit = %s", ctld.p(_unit)) + local function processHumanPlayer() + ctld.logTrace("in the 'processHumanPlayer' function") + if mist.DBs.humansByName[unitName] then -- it's a human unit + ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) + local _unit = Unit.getByName(unitName) + if _unit ~= nil then + -- assign transport pilot + ctld.logTrace("_unit = %s", ctld.p(_unit)) - local playerTypeName = _unit:getTypeName() - ctld.logTrace("playerTypeName = %s", ctld.p(playerTypeName)) + local playerTypeName = _unit:getTypeName() + ctld.logTrace("playerTypeName = %s", ctld.p(playerTypeName)) - -- Allow units to CTLD by aircraft type and not by pilot name - if ctld.addPlayerAircraftByType then - for _,aircraftType in pairs(ctld.aircraftTypeTable) do - if aircraftType == playerTypeName then - ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName)) - -- add transport unit to the list - table.insert(ctld.transportPilotNames, unitName) - -- add transport radio menu - ctld.addTransportF10MenuOptions(unitName) - break - end - end - else - for _, _unitName in pairs(ctld.transportPilotNames) do - if _unitName == unitName then - ctld.logTrace("adding by transportPilotNames, unitName = %s", ctld.p(unitName)) - ctld.addTransportF10MenuOptions(unitName) -- add transport radio menu - break - end - end - end + -- Allow units to CTLD by aircraft type and not by pilot name + if ctld.addPlayerAircraftByType then + for _, aircraftType in pairs(ctld.aircraftTypeTable) do + if aircraftType == playerTypeName then + ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName)) + -- add transport unit to the list + table.insert(ctld.transportPilotNames, unitName) + -- add transport radio menu + ctld.addTransportF10MenuOptions(unitName) + break end + end + else + for _, _unitName in pairs(ctld.transportPilotNames) do + if _unitName == unitName then + ctld.logTrace("adding by transportPilotNames, unitName = %s", ctld.p(unitName)) + ctld.addTransportF10MenuOptions(unitName) -- add transport radio menu + break + end + end end + end end + end - if not mist.DBs.humansByName[unitName] then - -- give a few milliseconds for MiST to handle the BIRTH event too - ctld.logTrace("give MiST some time to handle the BIRTH event too") - timer.scheduleFunction(function() - ctld.logTrace("calling the 'processHumanPlayer' function in a timer") - processHumanPlayer() - end, nil, timer.getTime()+0.5) - else - ctld.logTrace("calling the 'processHumanPlayer' function immediately") - processHumanPlayer() - end - + if not mist.DBs.humansByName[unitName] then + -- give a few milliseconds for MiST to handle the BIRTH event too + ctld.logTrace("give MiST some time to handle the BIRTH event too") + timer.scheduleFunction(function() + ctld.logTrace("calling the 'processHumanPlayer' function in a timer") + processHumanPlayer() + end, nil, timer.getTime() + 0.5) + else + ctld.logTrace("calling the 'processHumanPlayer' function immediately") + processHumanPlayer() + end end function ctld.i18n_check(language, verbose) - local english = ctld.i18n["en"] - local tocheck = ctld.i18n[language] - if not tocheck then - ctld.logError(string.format("CTLD.i18n_check: Language %s not found", language)) - return false - end - local englishVersion = english.translation_version - local tocheckVersion = tocheck.translation_version - if englishVersion ~= tocheckVersion then - ctld.logError(string.format("CTLD.i18n_check: Language version mismatch: EN has version %s, %s has version %s", englishVersion, language, tocheckVersion)) - end - --ctld.logTrace(string.format("english = %s", ctld.p(english))) - for textRef, textEnglish in pairs(english) do - if textRef ~= "translation_version" then - local textTocheck = tocheck[textRef] - if not textTocheck then - ctld.logError(string.format( "CTLD.i18n_check: NOT FOUND: checking %s text [%s]", language, textRef)) - elseif textTocheck == textEnglish then - ctld.logWarning(string.format("CTLD.i18n_check: SAME: checking %s text [%s] as in EN", language, textRef)) - elseif verbose then - ctld.logInfo(string.format( "CTLD.i18n_check: OK: checking %s text [%s]", language, textRef)) - end - end + local english = ctld.i18n["en"] + local tocheck = ctld.i18n[language] + if not tocheck then + ctld.logError(string.format("CTLD.i18n_check: Language %s not found", language)) + return false + end + local englishVersion = english.translation_version + local tocheckVersion = tocheck.translation_version + if englishVersion ~= tocheckVersion then + ctld.logError(string.format("CTLD.i18n_check: Language version mismatch: EN has version %s, %s has version %s", + englishVersion, language, tocheckVersion)) + end + --ctld.logTrace(string.format("english = %s", ctld.p(english))) + for textRef, textEnglish in pairs(english) do + if textRef ~= "translation_version" then + local textTocheck = tocheck[textRef] + if not textTocheck then + ctld.logError(string.format("CTLD.i18n_check: NOT FOUND: checking %s text [%s]", language, textRef)) + elseif textTocheck == textEnglish then + ctld.logWarning(string.format("CTLD.i18n_check: SAME: checking %s text [%s] as in EN", language, + textRef)) + elseif verbose then + ctld.logInfo(string.format("CTLD.i18n_check: OK: checking %s text [%s]", language, textRef)) + end end + end end -- example of usage: @@ -8430,7 +8271,7 @@ env.setErrorMessageBoxEnabled(false) -- initialize CTLD -- if you need to have a chance to modify the configuration before initialization in your other scripts, please set ctld.dontInitialize to true and call ctld.initialize() manually if ctld.dontInitialize then - ctld.logInfo(string.format("Skipping initializion of version %s because ctld.dontInitialize is true", ctld.Version)) + ctld.logInfo(string.format("Skipping initializion of version %s because ctld.dontInitialize is true", ctld.Version)) else - ctld.initialize() -end \ No newline at end of file + ctld.initialize() +end From 0d9c01283ce0d6d64f1b44da3852642916da64ec Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 11:02:40 +0100 Subject: [PATCH 027/166] fixes --- CTLD.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 69823c8..e8b8e16 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6023,8 +6023,9 @@ function ctld.updateRepackMenu(_playerUnitName) end --****************************************************************************************************** -function ctld.autoUpdateRepackMenu() -- auto update repack menus for each transport unit - --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) +function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each transport unit + ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) + if t == nil then t = timer.getTime() end if ctld.enableRepackingVehicles then for _, _unitName in pairs(ctld.transportPilotNames) do local status, error = pcall( @@ -6048,6 +6049,7 @@ function ctld.autoUpdateRepackMenu() -- auto update repack menus for each transp end end end + return t + 3 -- reschedule every 3 seconds end --****************************************************************************************************** From 6996769cecb4915d5d948ce1c598d24973481357 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 11:04:00 +0100 Subject: [PATCH 028/166] fixes --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index e8b8e16..78b77d8 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6024,7 +6024,7 @@ end --****************************************************************************************************** function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each transport unit - ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) + ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.ctld.transportPilotNames = %s", ctld.p(mist.utils.tableShow(ctld.transportPilotNames))) if t == nil then t = timer.getTime() end if ctld.enableRepackingVehicles then for _, _unitName in pairs(ctld.transportPilotNames) do From 78aa8a010e12bc7919d3a260030281f397944d72 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 11:10:13 +0100 Subject: [PATCH 029/166] fixes --- CTLD.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 78b77d8..db0cadb 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5819,6 +5819,7 @@ function ctld.addTransportF10MenuOptions(_unitName) end if ctld.enableRepackingVehicles then +trigger.action.outText("5822: F10 menu if ctld.unitCanCarryVehicles".._unit, 10) ctld.updateRepackMenu(_unitName) end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then @@ -8077,7 +8078,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end end, nil, timer.getTime() + 1) From f4b81fd101194179c4d325f82319a116d0e150ea Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 11:20:40 +0100 Subject: [PATCH 030/166] fixes --- CTLD.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index db0cadb..54d7aea 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2014,7 +2014,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if t == nil then t = timer.getTime() end - ctld.logTrace("FG_ ctld.repackVehicle = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) + --ctld.logTrace("FG_ ctld.repackVehicle = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) for ii, v in ipairs(ctld.repackRequestsStack) do local repackableUnitName = v[1].repackableUnitName local crateWeight = v[1].weight @@ -5819,7 +5819,8 @@ function ctld.addTransportF10MenuOptions(_unitName) end if ctld.enableRepackingVehicles then -trigger.action.outText("5822: F10 menu if ctld.unitCanCarryVehicles".._unit, 10) +ctld.logTrace("ctld.unitCanCarryVehicles %s", _unitName) +trigger.action.outText("5822: F10 menu if ctld.unitCanCarryVehicles".._unitName, 10) ctld.updateRepackMenu(_unitName) end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then From b390d5dabf15aaef3f21960217c044491ff3d397 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 11:22:57 +0100 Subject: [PATCH 031/166] fixes --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 54d7aea..c385ddf 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2045,7 +2045,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end ctld.repackRequestsStack[ii] = nil end - ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units + --ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units end if ctld.enableRepackingVehicles == true then return t + 3 -- reschedule the function in 3 seconds From c618f4b282c77fce1b0ecf4e544f1d9e29b74f42 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 11:33:15 +0100 Subject: [PATCH 032/166] fixes --- CTLD.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index c385ddf..94e7be4 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5995,6 +5995,7 @@ end --****************************************************************************************************** function ctld.updateRepackMenu(_playerUnitName) + ctld.logTrace("FG_ ctld.updateRepackMenu = [%s]", ctld.p(_playerUnitName)) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then local _unitTypename = playerUnit:getTypeName() @@ -6056,7 +6057,7 @@ end --****************************************************************************************************** function ctld.addOtherF10MenuOptions() - ctld.logDebug("ctld.addOtherF10MenuOptions") + --ctld.logDebug("ctld.addOtherF10MenuOptions") -- reschedule every 10 seconds timer.scheduleFunction(ctld.addOtherF10MenuOptions, nil, timer.getTime() + 10) local status, error = pcall(function() @@ -6094,7 +6095,7 @@ function ctld.addRadioListCommand(_side) local _groupId = ctld.getGroupId(_playerUnit) if _groupId then - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) + --ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) if ctld.addedTo[tostring(_groupId)] == nil then ctld.logTrace("adding List Radio Beacons for _groupId = %s", ctld.p(_groupId)) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("List Radio Beacons"), nil, From 4d0062dee3adf160ed0ea756530745cc18df0ad8 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 11:37:16 +0100 Subject: [PATCH 033/166] fixes --- CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 94e7be4..9c79848 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6117,7 +6117,7 @@ function ctld.addJTACRadioCommand(_side) if _groupId then local newGroup = false if ctld.jtacRadioAdded[tostring(_groupId)] == nil then - ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", + --ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", ctld.p(_playerUnit:getName())) newGroup = true local JTACpath = missionCommands.addSubMenuForGroup(_groupId, ctld.jtacMenuName) @@ -7664,7 +7664,7 @@ function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) local _groupId = ctld.getGroupId(_playerUnit) if _groupId then if ctld.reconRadioAdded[tostring(_groupId)] == nil then - ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", + --ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", ctld.p(_playerUnit:getName())) local RECONpath = missionCommands.addSubMenuForGroup(_groupId, ctld.reconMenuName) missionCommands.addCommandForGroup(_groupId, From dcdf6438a183446e853d9d2e4655b48b9d35b2da Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 12:29:25 +0100 Subject: [PATCH 034/166] fixes --- CTLD.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 9c79848..a46975d 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6117,8 +6117,7 @@ function ctld.addJTACRadioCommand(_side) if _groupId then local newGroup = false if ctld.jtacRadioAdded[tostring(_groupId)] == nil then - --ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", - ctld.p(_playerUnit:getName())) + --ctld.logDebug("ctld.addJTACRadioCommand - adding JTAC radio menu for unit [%s]", ctld.p(_playerUnit:getName())) newGroup = true local JTACpath = missionCommands.addSubMenuForGroup(_groupId, ctld.jtacMenuName) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("JTAC Status"), JTACpath, From ff06dcecf7128cd4395400c6f4151fa84f6dca59 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 12:50:41 +0100 Subject: [PATCH 035/166] fixes --- CTLD.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index a46975d..0273513 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5768,7 +5768,7 @@ function ctld.addTransportF10MenuOptions(_unitName) local _groupId = ctld.getGroupId(_unit) if _groupId then ctld.logTrace("_groupId = %s", ctld.p(_groupId)) - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo)) + ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo[tostring(_groupId)])) if ctld.addedTo[tostring(_groupId)] == nil then ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) @@ -7663,8 +7663,7 @@ function ctld.addReconRadioCommand(_side) -- _side = 1 or 2 (red or blue) local _groupId = ctld.getGroupId(_playerUnit) if _groupId then if ctld.reconRadioAdded[tostring(_groupId)] == nil then - --ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", - ctld.p(_playerUnit:getName())) + --ctld.logDebug("ctld.addReconRadioCommand - adding RECON radio menu for unit [%s]", ctld.p(_playerUnit:getName())) local RECONpath = missionCommands.addSubMenuForGroup(_groupId, ctld.reconMenuName) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Show targets in LOS (refresh)"), RECONpath, @@ -8080,7 +8079,7 @@ function ctld.initialize() end if ctld.enableRepackingVehicles == true then --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) - timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) + --timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end end, nil, timer.getTime() + 1) From d1d9b55769f6cc62b9718767faa4970449d7bd8d Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 14:55:10 +0100 Subject: [PATCH 036/166] fixes --- CTLD.lua | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 0273513..364b65e 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5773,11 +5773,9 @@ function ctld.addTransportF10MenuOptions(_unitName) ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) 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 - 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"), _troopCommandsPath, ctld.unloadExtractTroops, { _unitName }) @@ -5995,7 +5993,6 @@ end --****************************************************************************************************** function ctld.updateRepackMenu(_playerUnitName) - ctld.logTrace("FG_ ctld.updateRepackMenu = [%s]", ctld.p(_playerUnitName)) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then local _unitTypename = playerUnit:getTypeName() @@ -6004,12 +6001,13 @@ function ctld.updateRepackMenu(_playerUnitName) local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then - missionCommands.removeItemForGroup(1, ctld.vehicleCommandsPath) -- remove the old menu - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, - ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) - local menuEntries = {} - local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) +ctld.logTrace("FG_ ctld.updateRepackMenu.ctld.vehicleCommandsPath = %s", ctld.p(ctld.vehicleCommandsPath)) + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") +ctld.logTrace("FG_ ctld.updateRepackMenu.RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) + missionCommands.removeItemForGroup(1, RepackCommandsPath) -- remove the old repack menu + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) + local menuEntries = {} for _, _vehicle in pairs(repackableVehicles) do table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, From baf1231141d82b28cc1d593d65e6e42e272a0d2e Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 14:57:32 +0100 Subject: [PATCH 037/166] fixes --- CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 364b65e..97c9dfc 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -8076,8 +8076,8 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) - --timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end end, nil, timer.getTime() + 1) From 8dcaa3e0bb39360a05252c2fdc29410191bf1b58 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 15:05:28 +0100 Subject: [PATCH 038/166] fixes --- CTLD.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 97c9dfc..50ded27 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2045,7 +2045,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end ctld.repackRequestsStack[ii] = nil end - --ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units + ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units end if ctld.enableRepackingVehicles == true then return t + 3 -- reschedule the function in 3 seconds @@ -5817,8 +5817,6 @@ function ctld.addTransportF10MenuOptions(_unitName) end if ctld.enableRepackingVehicles then -ctld.logTrace("ctld.unitCanCarryVehicles %s", _unitName) -trigger.action.outText("5822: F10 menu if ctld.unitCanCarryVehicles".._unitName, 10) ctld.updateRepackMenu(_unitName) end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then From d125cdbe8095a925140530f5f890446ca04ae317 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 15:14:03 +0100 Subject: [PATCH 039/166] fixes --- CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 50ded27..b619f76 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5999,10 +5999,10 @@ function ctld.updateRepackMenu(_playerUnitName) local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then -ctld.logTrace("FG_ ctld.updateRepackMenu.ctld.vehicleCommandsPath = %s", ctld.p(ctld.vehicleCommandsPath)) + --ctld.logTrace("FG_ ctld.updateRepackMenu.ctld.vehicleCommandsPath = %s", ctld.p(ctld.vehicleCommandsPath)) local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") -ctld.logTrace("FG_ ctld.updateRepackMenu.RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) + --ctld.logTrace("FG_ ctld.updateRepackMenu.RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) missionCommands.removeItemForGroup(1, RepackCommandsPath) -- remove the old repack menu local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) local menuEntries = {} From 8a17ff52aa5f9b721ca0864cf497667cbe7a4739 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 15:24:12 +0100 Subject: [PATCH 040/166] fixes --- CTLD.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index b619f76..0ac8556 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2004,13 +2004,13 @@ end -- *************************************************************** function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request + --ctld.logTrace("FG_ ctld.repackVehicleRequest") ctld.repackRequestsStack[#ctld.repackRequestsStack + 1] = _params - ctld.logTrace("FG_ ctld.repackVehicleRequest = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) end -- *************************************************************** function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request - trigger.action.outText("Repacking vehicle...", 10) + --ctld.logTrace("FG_ ctld.repackVehicle") if t == nil then t = timer.getTime() end @@ -5966,7 +5966,7 @@ end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) + --ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do @@ -6023,7 +6023,7 @@ end --****************************************************************************************************** function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each transport unit - ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.ctld.transportPilotNames = %s", ctld.p(mist.utils.tableShow(ctld.transportPilotNames))) + --ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.ctld.transportPilotNames = %s", ctld.p(mist.utils.tableShow(ctld.transportPilotNames))) if t == nil then t = timer.getTime() end if ctld.enableRepackingVehicles then for _, _unitName in pairs(ctld.transportPilotNames) do From 9c25240c43457fed743f4b0a7ddfbd21ad57fc63 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 16:33:09 +0100 Subject: [PATCH 041/166] fixes --- CTLD.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 0ac8556..b7b59fb 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2129,7 +2129,7 @@ end -- *************************************************************** function ctld.isUnitInNamedLogisticZone(_unitName, _logisticUnitName) -- check if a unit is in the named logistic zone - trigger.action.outText('ctld.isUnitInNamedLogisticZone._logisticUnitName = ' .. tostring(_logisticUnitName), 10) + --ctld.logTrace("FG_ ctld.isUnitInNamedLogisticZone._logisticUnitName = %s", ctld.p(_logisticUnitName)) local _unit = Unit.getByName(_unitName) if _unit == nil then return false @@ -2147,6 +2147,7 @@ end -- *************************************************************** function ctld.isUnitInALogisticZone(_unitName) -- check if a unit is in a logistic zone if true then return the logisticUnitName of the zone + --ctld.logTrace("FG_ ctld.isUnitInALogisticZone._unitName = %s", ctld.p(_unitName)) for i, logUnit in ipairs(ctld.logisticUnits) do if ctld.isUnitInNamedLogisticZone(_unitName, logUnit) then return logUnit From 9def2f1a3acc29fc7969dd5db5569a360a29b449 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 17:00:17 +0100 Subject: [PATCH 042/166] fixes --- CTLD.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index b7b59fb..991c323 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2029,8 +2029,8 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if repackableUnit then if repackableUnit:isExist() then --ici calculer le heading des spwans à effectuer - for i = 1, v[1].cratesRequired do - local _point = { x = spawnRefPoint.x + (i * 5), z = spawnRefPoint.z } + for i = 0, v[1].cratesRequired - 1 do + local _point = { x = spawnRefPoint.x + 5 + (i * 3), z = spawnRefPoint.z } -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) @@ -2113,7 +2113,7 @@ function ctld.updateDynamicLogisticUnitsZones() -- remove Dynamic Logistic Units end end end - return 5 -- reschedule the function in 5 second + return 5 -- reschedule the function in 5 seconds end -- *************************************************************** From 1181fcfff79ddd793d0c8e804cc9cd671b62fa81 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 17:22:18 +0100 Subject: [PATCH 043/166] fixes --- CTLD.lua | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 991c323..012e534 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2029,13 +2029,19 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if repackableUnit then if repackableUnit:isExist() then --ici calculer le heading des spwans à effectuer + local _playerHeading = mist.getHeading(PlayerTransportUnit) + local _offset = 5 + local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.randomValueBetweenMinAndMax(_playerHeading - math.pi(), _playerHeading + math.pi())) for i = 0, v[1].cratesRequired - 1 do - local _point = { x = spawnRefPoint.x + 5 + (i * 3), z = spawnRefPoint.z } + --local _point = { x = spawnRefPoint.x + 5 + (i * 3), z = spawnRefPoint.z } + local _point = { x = refPoint.x + 5 + (i * _offset), z = refPoint.z } + local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.randomValueBetweenMinAndMax(_playerHeading - math.pi(), _playerHeading + math.pi())) + -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, - PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) + PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end if ctld.isUnitInALogisticZone(repackableUnitName) == nil then @@ -5497,7 +5503,6 @@ function ctld.inLogisticsZone(_heli) if _logistic ~= nil and _logistic:getCoalition() == _heli:getCoalition() and _logistic:getLife() > 0 then --get distance local _dist = ctld.getDistance(_heliPoint, _logistic:getPoint()) - ctld.logDebug("_dist = %s", ctld.p(_dist)) if _dist <= ctld.maximumDistanceLogistic then return true end @@ -8264,6 +8269,11 @@ end -- initialize the random number generator to make it almost random math.random(); math.random(); math.random() +function ctld.randomValueBetweenMinAndMax(mini, maxi) + local aleatoire = math.random() -- math.random() génèrate ua random value between 0 eand 1 + local resultat = mini + aleatoire * (maxi - mini) -- Scales the random number to the interval [min, max] + return resultat + end --- Enable/Disable error boxes displayed on screen. env.setErrorMessageBoxEnabled(false) From 7517eaf18842650e0d03a7dec71519b10dd41c1a Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 17:39:58 +0100 Subject: [PATCH 044/166] fixes --- CTLD.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 012e534..d6aef42 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2031,12 +2031,13 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' --ici calculer le heading des spwans à effectuer local _playerHeading = mist.getHeading(PlayerTransportUnit) local _offset = 5 - local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.randomValueBetweenMinAndMax(_playerHeading - math.pi(), _playerHeading + math.pi())) + trigger.action.outText("heading = " .. _playerHeading, 5) + local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.randomValueBetweenMinAndMax(_playerHeading - math.pi/4, _playerHeading + math.pi/4)) for i = 0, v[1].cratesRequired - 1 do --local _point = { x = spawnRefPoint.x + 5 + (i * 3), z = spawnRefPoint.z } + --local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.randomValueBetweenMinAndMax(_playerHeading - math.pi/4, _playerHeading + math.pi/4)) local _point = { x = refPoint.x + 5 + (i * _offset), z = refPoint.z } - local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.randomValueBetweenMinAndMax(_playerHeading - math.pi(), _playerHeading + math.pi())) - + -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) From 75c9d0bf688f831fad4391d02098b976b3f431a4 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 18:10:39 +0100 Subject: [PATCH 045/166] fixes --- CTLD.lua | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index d6aef42..01f86f7 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2032,10 +2032,9 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local _playerHeading = mist.getHeading(PlayerTransportUnit) local _offset = 5 trigger.action.outText("heading = " .. _playerHeading, 5) - local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.randomValueBetweenMinAndMax(_playerHeading - math.pi/4, _playerHeading + math.pi/4)) + local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.random(_playerHeading - math.pi/4, _playerHeading + math.pi/4)) for i = 0, v[1].cratesRequired - 1 do --local _point = { x = spawnRefPoint.x + 5 + (i * 3), z = spawnRefPoint.z } - --local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.randomValueBetweenMinAndMax(_playerHeading - math.pi/4, _playerHeading + math.pi/4)) local _point = { x = refPoint.x + 5 + (i * _offset), z = refPoint.z } -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) @@ -8270,11 +8269,6 @@ end -- initialize the random number generator to make it almost random math.random(); math.random(); math.random() -function ctld.randomValueBetweenMinAndMax(mini, maxi) - local aleatoire = math.random() -- math.random() génèrate ua random value between 0 eand 1 - local resultat = mini + aleatoire * (maxi - mini) -- Scales the random number to the interval [min, max] - return resultat - end --- Enable/Disable error boxes displayed on screen. env.setErrorMessageBoxEnabled(false) From 888d949234188dab4ea73426c805f321b731f535 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 23:39:25 +0100 Subject: [PATCH 046/166] fixes --- CTLD.lua | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 01f86f7..b2c2ad3 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2030,17 +2030,16 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if repackableUnit:isExist() then --ici calculer le heading des spwans à effectuer local _playerHeading = mist.getHeading(PlayerTransportUnit) - local _offset = 5 - trigger.action.outText("heading = " .. _playerHeading, 5) - local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, _offset, ctld.random(_playerHeading - math.pi/4, _playerHeading + math.pi/4)) + local playerPoint = PlayerTransportUnit:getPoint() + local offset = 5 + --local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, offset, ctld.random(_playerHeading - math.pi/4, _playerHeading + math.pi/4)) + local randomHeading = RandomReal(_playerHeading - math.pi/4, _playerHeading + math.pi/4) for i = 0, v[1].cratesRequired - 1 do - --local _point = { x = spawnRefPoint.x + 5 + (i * 3), z = spawnRefPoint.z } - local _point = { x = refPoint.x + 5 + (i * _offset), z = refPoint.z } - - -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) + -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, offset, _directionInRadian) local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, + local relativePoint = ctld.getRelativePoint(playerPoint, i * offset, randomHeading) + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, relativePoint, _name, crateWeight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end @@ -2628,6 +2627,17 @@ function ctld.getPointAtDirection(_unit, _offset, _directionInRadian) return { x = _point.x + _xOffset, z = _point.z + _zOffset, y = _point.y } end +function ctld.getRelativePoint(_refPointXZTable, _distance, _angle_radians) -- return coord point at distance and angle from _refPoint + local relativePoint = {} + relativePoint.x = _refPointXZTable.x + _distance * math.cos(_angle_radians) + if _refPointXZTable.z == nil then + relativePoint.y = _refPointXZTable.y + _distance * math.sin(_angle_radians) + else + relativePoint.z = _refPointXZTable.z + _distance * math.sin(_angle_radians) + end + return relativePoint +end + function ctld.troopsOnboard(_heli, _troops) if ctld.inTransitTroops[_heli:getName()] ~= nil then local _onboard = ctld.inTransitTroops[_heli:getName()] @@ -8270,6 +8280,12 @@ end -- initialize the random number generator to make it almost random math.random(); math.random(); math.random() +function ctld.RandomReal(mini, maxi) + local rand = math.random() --random value between 0 and 1 + local result = mini + rand * (maxi - mini) -- scale the random value between [mini, maxi] + return result +end + --- Enable/Disable error boxes displayed on screen. env.setErrorMessageBoxEnabled(false) From 0b3b1ea1f87a24b24a3d01f9c729dcfc7b987df6 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 23:59:06 +0100 Subject: [PATCH 047/166] fixes --- CTLD.lua | 40 +++++++--------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index b2c2ad3..d2c1cfa 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2022,23 +2022,22 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local playerUnitName = v[2] local PlayerTransportUnit = Unit.getByName(playerUnitName) - local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) + local TransportUnit = ctld.getTransportUnit(playerUnitName) local spawnRefPoint = PlayerTransportUnit:getPoint() local refCountry = PlayerTransportUnit:getCountry() if repackableUnit then if repackableUnit:isExist() then - --ici calculer le heading des spwans à effectuer + -- calculate the heading of the spawns to be carried out local _playerHeading = mist.getHeading(PlayerTransportUnit) local playerPoint = PlayerTransportUnit:getPoint() local offset = 5 - --local refPoint = ctld.getPointAtDirection(PlayerTransportUnit, offset, ctld.random(_playerHeading - math.pi/4, _playerHeading + math.pi/4)) - local randomHeading = RandomReal(_playerHeading - math.pi/4, _playerHeading + math.pi/4) - for i = 0, v[1].cratesRequired - 1 do - -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, offset, _directionInRadian) + local randomHeading = ctld.RandomReal(_playerHeading - math.pi/4, _playerHeading + math.pi/4) + for i = 1, v[1].cratesRequired - 1 do + -- see to spawn the crate at random position heading the transport unnit local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) - local relativePoint = ctld.getRelativePoint(playerPoint, i * offset, randomHeading) + local relativePoint = ctld.getRelativePoint(playerPoint, 10 + i * offset, randomHeading) ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, relativePoint, _name, crateWeight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end @@ -2059,31 +2058,6 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end end ---[[ *************************************************************** -function ctld.repackVehicle_old(_params) - local repackableUnit = _params[1] - local PlayerTransportUnitName = _params[2] - local PlayerTransportUnit = Unit.getByName(PlayerTransportUnitName) - ctld.logTrace(" ctld.repackVehicle.repackableUnit = %s", ctld.p(mist.utils.tableShow(repackableUnit))) - local TransportUnit = ctld.getTransportUnit(PlayerTransportUnitName) - local spawnRefPoint = PlayerTransportUnit:getPoint() - local refCountry = PlayerTransportUnit:getCountry() - if repackableUnit then - --ici calculer le heading des spwan à effectuer - for i=1, repackableUnit.cratesRequired do - --local _point = mist.utils.makeVec3GL(spawnRefPoint.x+(i*5), spawnRefPoint.z, spawnRefPoint.y) - local _point = {x = spawnRefPoint.x+(i*5), z = spawnRefPoint.z} - -- see to spawn the crate at random position heading the transport unit with ctld.getPointAtDirection(_unit, _offset, _directionInRadian) - local _unitId = ctld.getNextUnitId() - local _name = string.format("%s #%i", repackableUnit.desc, _unitId) - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, repackableUnit.weight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) - end - - ctld.addStaticLogisticUnit({x = spawnRefPoint.x+5, z = spawnRefPoint.z+10}, refCountry) -- create a temporary logistic unit to be able to repack the vehicle - repackableUnit.vehicleId:destroy() -- destroy repacked unit - return - end -end ]] -- -- *************************************************************** function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit to be able to repack the vehicle local dynamicLogisticUnitName = "%dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) @@ -2627,7 +2601,7 @@ function ctld.getPointAtDirection(_unit, _offset, _directionInRadian) return { x = _point.x + _xOffset, z = _point.z + _zOffset, y = _point.y } end -function ctld.getRelativePoint(_refPointXZTable, _distance, _angle_radians) -- return coord point at distance and angle from _refPoint +function ctld.getRelativePoint(_refPointXZTable, _distance, _angle_radians) -- return coord point at distance and angle from _refPointXZTable local relativePoint = {} relativePoint.x = _refPointXZTable.x + _distance * math.cos(_angle_radians) if _refPointXZTable.z == nil then From f83365ec63b22bd528b89312dcd66c072e0d94f7 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 25 Mar 2025 23:59:26 +0100 Subject: [PATCH 048/166] fixes --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index d2c1cfa..9117df3 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2033,7 +2033,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local playerPoint = PlayerTransportUnit:getPoint() local offset = 5 local randomHeading = ctld.RandomReal(_playerHeading - math.pi/4, _playerHeading + math.pi/4) - for i = 1, v[1].cratesRequired - 1 do + for i = 1, v[1].cratesRequired do -- see to spawn the crate at random position heading the transport unnit local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) From 3eaa08b74e050b81506369666b402043606335a7 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Mar 2025 00:01:15 +0100 Subject: [PATCH 049/166] fixes --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 9117df3..3d2f271 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2037,7 +2037,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' -- see to spawn the crate at random position heading the transport unnit local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) - local relativePoint = ctld.getRelativePoint(playerPoint, 10 + i * offset, randomHeading) + local relativePoint = ctld.getRelativePoint(playerPoint, 7 + i * offset, randomHeading) -- 7 meters from the transport unit ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, relativePoint, _name, crateWeight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end From 80fe5d59b0f519b5abea829bee3ee43706ec1415 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Mar 2025 01:00:37 +0100 Subject: [PATCH 050/166] readme update for repack --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index b34ffd6..913c002 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ This script is a rewrite of some of the functionality of the original Complete C * [Simulated Sling Loading](#simulated-sling-loading) * [Real Sling Loading](#real-sling-loading) * [Crate Unpacking](#crate-unpacking) +* [Crate Repacking](#crate-repacking) * [Forward Operating Base (FOB) Construction](#forward-operating-base-fob-construction) * [Radio Beacon Deployment](#radio-beacon-deployment) * [A10\-C UHF ADF Radio Setup](#a10-c-uhf-adf-radio-setup) @@ -1006,6 +1007,11 @@ 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. +## Crate Repacking +The F10 menu allows you to repack units with 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. +If repacking is possible, a logistics zone is automatically created around your aircraft to allow you to load and handle the produced crates. + ## 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. From d80a70c0ee0b1c750ef3da6ab867191eb46c9b10 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Mar 2025 01:18:09 +0100 Subject: [PATCH 051/166] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 913c002..a61c2ce 100644 --- a/README.md +++ b/README.md @@ -1008,7 +1008,7 @@ 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. ## Crate Repacking -The F10 menu allows you to repack units with associated crate types in the "ctld.spawnableCrates" table. +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. If repacking is possible, a logistics zone is automatically created around your aircraft to allow you to load and handle the produced crates. From b69f20eb6bf54b84047b8bb425933aabd20b2651 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Mar 2025 01:31:53 +0100 Subject: [PATCH 052/166] add ctld.getSecureDistanceFromUnit fct --- CTLD.lua | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 3d2f271..f0f2d69 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1922,6 +1922,20 @@ function ctld.spawnCrateAtPoint(_side, _weight, _point, _hdg) ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side, _hdg) end +function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName + if Unit.getByName(_unitName) then + local unitBoundingBox = Unit.getByName(_unitName):getDesc().box + local deltaX = box.max.x - box.min.x + local deltaZ = box.max.z - box.min.z + if deltaX > deltaZ then + return deltaX/2 + else + return deltaZ/2 + end + end + return nil +end + -- *************************************************************** -- Repack vehicules crates functions -- *************************************************************** @@ -2037,7 +2051,8 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' -- see to spawn the crate at random position heading the transport unnit local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) - local relativePoint = ctld.getRelativePoint(playerPoint, 7 + i * offset, randomHeading) -- 7 meters from the transport unit + local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) + local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, relativePoint, _name, crateWeight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end From ef1d3d57e562e8edef4e331987761a755e9d0785 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Mar 2025 01:49:45 +0100 Subject: [PATCH 053/166] add ctld.getSecureDistanceFromUnit --- CTLD.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index f0f2d69..cfc0554 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1925,8 +1925,8 @@ end function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName if Unit.getByName(_unitName) then local unitBoundingBox = Unit.getByName(_unitName):getDesc().box - local deltaX = box.max.x - box.min.x - local deltaZ = box.max.z - box.min.z + local deltaX = unitBoundingBox.max.x - unitBoundingBox.min.x + local deltaZ = unitBoundingBox.max.z - unitBoundingBox.min.z if deltaX > deltaZ then return deltaX/2 else @@ -2052,6 +2052,10 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) + if secureDistance == nil then + secureDistance = 7 + end + local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, relativePoint, _name, crateWeight, PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) From 0c74729837473d35994534ad787c56f41be3bbcb Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Mar 2025 10:32:10 +0100 Subject: [PATCH 054/166] fixes ctld.getSecureDistanceFromUnit --- CTLD.lua | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index cfc0554..8de5b17 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1923,6 +1923,22 @@ function ctld.spawnCrateAtPoint(_side, _weight, _point, _hdg) end function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName + if Unit.getByName(_unitName) then + local unitBoundingBox = Unit.getByName(_unitName):getDesc().box + local widht = unitBoundingBox.max.x - unitBoundingBox.min.x + local longer = unitBoundingBox.max.z - unitBoundingBox.min.z + local hight = unitBoundingBox.max.y - unitBoundingBox.min.y + + -- distanceFromCenterToCorner = 1/2√(l² + w² + h²) + local squaresSum = longer^2 + widht^2 + hight^2 -- sum of squares + local squareRoots = math.sqrt(squaresSum) -- square roots + local distanceFromCenterToCorner = squareRoots / 2 -- Calculating distance (half square root) + return distanceFromCenterToCorner + end + return nil +end +--[[ +function ctld.getSecureDistanceFromUnit_old(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName if Unit.getByName(_unitName) then local unitBoundingBox = Unit.getByName(_unitName):getDesc().box local deltaX = unitBoundingBox.max.x - unitBoundingBox.min.x @@ -1934,10 +1950,10 @@ function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between end end return nil -end +end ]]-- -- *************************************************************** --- Repack vehicules crates functions +-- Repack vehicules crates functions -- *************************************************************** ctld.repackRequestsStack = {} -- table to store the repack request From ecc47bb247856a8b026e054afc68f42c729219d0 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 26 Mar 2025 10:32:35 +0100 Subject: [PATCH 055/166] fixes --- CTLD.lua | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 8de5b17..306c9a3 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1937,20 +1937,6 @@ function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between end return nil end ---[[ -function ctld.getSecureDistanceFromUnit_old(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName - if Unit.getByName(_unitName) then - local unitBoundingBox = Unit.getByName(_unitName):getDesc().box - local deltaX = unitBoundingBox.max.x - unitBoundingBox.min.x - local deltaZ = unitBoundingBox.max.z - unitBoundingBox.min.z - if deltaX > deltaZ then - return deltaX/2 - else - return deltaZ/2 - end - end - return nil -end ]]-- -- *************************************************************** -- Repack vehicules crates functions From f0adb72c49f5488ad9bcb93395fc4ea8f84d8f27 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Wed, 26 Mar 2025 17:36:56 +0100 Subject: [PATCH 056/166] comiit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 306c9a3..9f75c99 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1,4 +1,4 @@ ---[[ +--[[toto Combat Troop and Logistics Drop Allows Huey, Mi-8 and C130 to transport troops internally and Helicopters to transport Logistic / Vehicle units to the field via sling-loads From f3a9b4438873f05067a63009a99dbf68946824f6 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Wed, 26 Mar 2025 18:22:20 +0100 Subject: [PATCH 057/166] commit --- CTLD.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 9f75c99..76c3219 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1,4 +1,4 @@ ---[[toto +--[[ Combat Troop and Logistics Drop Allows Huey, Mi-8 and C130 to transport troops internally and Helicopters to transport Logistic / Vehicle units to the field via sling-loads @@ -5823,9 +5823,10 @@ function ctld.addTransportF10MenuOptions(_unitName) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), _vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true }) - if ctld.vehicleCommandsPath == nil then - ctld.vehicleCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) - end + if ctld.vehicleCommandsPath[_unitName] == nil then + ctld.vehicleCommandsPath[_unitName] = {} + ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) + end if ctld.enableRepackingVehicles then ctld.updateRepackMenu(_unitName) @@ -6010,12 +6011,11 @@ function ctld.updateRepackMenu(_playerUnitName) local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then - --ctld.logTrace("FG_ ctld.updateRepackMenu.ctld.vehicleCommandsPath = %s", ctld.p(ctld.vehicleCommandsPath)) - local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") --ctld.logTrace("FG_ ctld.updateRepackMenu.RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) missionCommands.removeItemForGroup(1, RepackCommandsPath) -- remove the old repack menu - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} for _, _vehicle in pairs(repackableVehicles) do table.insert(menuEntries, { @@ -7894,7 +7894,7 @@ function ctld.initialize() ctld.hoverStatus = {} -- tracks status of a helis hover above a crate ctld.callbacks = {} -- function callback - + ctld.vehicleCommandsPath = {} -- Vehicles commands path for each playerTransportUnit -- Remove intransit troops when heli / cargo plane dies --ctld.eventHandler = {} From 251467fee45ba752c59a15a67b2d9645592b2007 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Wed, 26 Mar 2025 18:39:35 +0100 Subject: [PATCH 058/166] update dictionary --- CTLD-i18n.lua | 2 ++ CTLD.lua | 1 + 2 files changed, 3 insertions(+) diff --git a/CTLD-i18n.lua b/CTLD-i18n.lua index 33475e6..1256ebf 100644 --- a/CTLD-i18n.lua +++ b/CTLD-i18n.lua @@ -269,6 +269,7 @@ ctld.i18n["fr"]["Vehicle / FOB Crates / Drone"] = "Caisses Vehicule / FOB / Dron ctld.i18n["fr"]["Unload Vehicles"] = "Décharger Vehicles" ctld.i18n["fr"]["Load / Extract Vehicles"] = "Chargt / Déchargt Vehicules" ctld.i18n["fr"]["Load / Unload FOB Crate"] = "Chargt / Déchargt Caisse FOB" +ctld.i18n["fr"]["Repack Vehicles"] = "Ré-emballer véhicules" ctld.i18n["fr"]["CTLD Commands"] = "Commandes CTLD" ctld.i18n["fr"]["CTLD"] = "CTLD" ctld.i18n["fr"]["Check Cargo"] = "Vérif° chargement" @@ -568,6 +569,7 @@ ctld.i18n["es"]["Vehicle / FOB Crates / Drone"] = "Cajas Vehículo / FOB / Dron" ctld.i18n["es"]["Unload Vehicles"] = "Descargar vehículos" ctld.i18n["es"]["Load / Extract Vehicles"] = "Cargar/Extraer vehículos" ctld.i18n["es"]["Load / Unload FOB Crate"] = "Cargar/Descargar caja FOB" +ctld.i18n["es"]["Repack Vehicles"] = "Reenvolver vehículos" ctld.i18n["es"]["CTLD Commands"] = "Comandos CTLD" ctld.i18n["es"]["CTLD"] = "CTLD" ctld.i18n["es"]["Check Cargo"] = "Verificar carga" diff --git a/CTLD.lua b/CTLD.lua index 76c3219..1b4abf1 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -331,6 +331,7 @@ ctld.i18n["en"]["Vehicle / FOB Crates / Drone"] = "" ctld.i18n["en"]["Unload Vehicles"] = "" ctld.i18n["en"]["Load / Extract Vehicles"] = "" ctld.i18n["en"]["Load / Unload FOB Crate"] = "" +ctld.i18n["en"]["Repack Vehicles"] = "" ctld.i18n["en"]["CTLD Commands"] = "" ctld.i18n["en"]["CTLD"] = "" ctld.i18n["en"]["Check Cargo"] = "" From d029e2479b9e05ed85199eb31e9544e6219bc851 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Wed, 26 Mar 2025 18:43:54 +0100 Subject: [PATCH 059/166] commit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 1b4abf1..d105751 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2065,7 +2065,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end if ctld.isUnitInALogisticZone(repackableUnitName) == nil then - ctld.addStaticLogisticUnit({ x = spawnRefPoint.x + 5, z = spawnRefPoint.z + 10 }, refCountry) -- create a temporary logistic unit to be able to repack the vehicle + --ctld.addStaticLogisticUnit({ x = spawnRefPoint.x + 5, z = spawnRefPoint.z + 10 }, refCountry) -- create a temporary logistic unit to be able to repack the vehicle end repackableUnit:destroy() -- destroy repacked unit end From 159915a6ac0a27b52f8118a4f99246e87c793de6 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Wed, 26 Mar 2025 18:51:16 +0100 Subject: [PATCH 060/166] commit --- CTLD.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index d105751..c0a246c 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1029,13 +1029,13 @@ ctld.spawnableCrates = { --- BLUE { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types - { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, - { multiple = { 1000.02, 1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, + { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 1 }, + { multiple = { 1000.02, 1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 1 }, { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit = "MaxxPro_MRAP", side = 2, cratesRequired = 2 }, { multiple = { 1000.03, 1000.03 }, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit = "LAV-25", side = 2, cratesRequired = 3 }, { multiple = { 1000.04, 1000.04, 1000.04 }, desc = ctld.i18n_translate("Med Tank - LAV-25 - All crates"), side = 2 }, - { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit = "M1A2C_SEP_V3", side = 2, cratesRequired = 4 }, + { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit = "M-1 Abrams", side = 2, cratesRequired = 4 }, { multiple = { 1000.05, 1000.05, 1000.05, 1000.05 }, desc = ctld.i18n_translate("Heavy Tank - Abrams - All crates"), side = 2 }, --- RED From 954a6b0fada2956f6a944739a1bf26920a0e4f8c Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 29 Mar 2025 09:48:35 +0100 Subject: [PATCH 061/166] update dictionary --- CTLD.lua | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index c0a246c..7f011db 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1029,13 +1029,13 @@ ctld.spawnableCrates = { --- BLUE { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types - { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 1 }, - { multiple = { 1000.02, 1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 1 }, + { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, + { multiple = { 1000.02, 1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit = "MaxxPro_MRAP", side = 2, cratesRequired = 2 }, { multiple = { 1000.03, 1000.03 }, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit = "LAV-25", side = 2, cratesRequired = 3 }, { multiple = { 1000.04, 1000.04, 1000.04 }, desc = ctld.i18n_translate("Med Tank - LAV-25 - All crates"), side = 2 }, - { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit = "M-1 Abrams", side = 2, cratesRequired = 4 }, + { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit = "M1A2C_SEP_V3", side = 2, cratesRequired = 4 }, { multiple = { 1000.05, 1000.05, 1000.05, 1000.05 }, desc = ctld.i18n_translate("Heavy Tank - Abrams - All crates"), side = 2 }, --- RED @@ -1936,7 +1936,7 @@ function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between local distanceFromCenterToCorner = squareRoots / 2 -- Calculating distance (half square root) return distanceFromCenterToCorner end - return nil + return nil_old end -- *************************************************************** @@ -2065,7 +2065,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end if ctld.isUnitInALogisticZone(repackableUnitName) == nil then - --ctld.addStaticLogisticUnit({ x = spawnRefPoint.x + 5, z = spawnRefPoint.z + 10 }, refCountry) -- create a temporary logistic unit to be able to repack the vehicle + ctld.addStaticLogisticUnit({ x = spawnRefPoint.x + 5, z = spawnRefPoint.z + 10 }, refCountry) -- create a temporary logistic unit to be able to repack the vehicle end repackableUnit:destroy() -- destroy repacked unit end @@ -5824,10 +5824,9 @@ function ctld.addTransportF10MenuOptions(_unitName) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), _vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true }) - if ctld.vehicleCommandsPath[_unitName] == nil then - ctld.vehicleCommandsPath[_unitName] = {} - ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) - end + if ctld.vehicleCommandsPath == nil then + ctld.vehicleCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) + end if ctld.enableRepackingVehicles then ctld.updateRepackMenu(_unitName) @@ -6012,11 +6011,12 @@ function ctld.updateRepackMenu(_playerUnitName) local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then - local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) + --ctld.logTrace("FG_ ctld.updateRepackMenu.ctld.vehicleCommandsPath = %s", ctld.p(ctld.vehicleCommandsPath)) + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") --ctld.logTrace("FG_ ctld.updateRepackMenu.RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) missionCommands.removeItemForGroup(1, RepackCommandsPath) -- remove the old repack menu - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) local menuEntries = {} for _, _vehicle in pairs(repackableVehicles) do table.insert(menuEntries, { @@ -7895,7 +7895,7 @@ function ctld.initialize() ctld.hoverStatus = {} -- tracks status of a helis hover above a crate ctld.callbacks = {} -- function callback - ctld.vehicleCommandsPath = {} -- Vehicles commands path for each playerTransportUnit + -- Remove intransit troops when heli / cargo plane dies --ctld.eventHandler = {} From 61a1fd42ee0c547a5db0bcd1db2e99e91be4621e Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 09:54:52 +0100 Subject: [PATCH 062/166] update dict versionnumber --- CTLD-i18n.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD-i18n.lua b/CTLD-i18n.lua index 1256ebf..9adaf2e 100644 --- a/CTLD-i18n.lua +++ b/CTLD-i18n.lua @@ -18,7 +18,7 @@ ctld.i18n = {} --======== FRENCH - FRANCAIS ===================================================================================== ctld.i18n["fr"] = {} -ctld.i18n["fr"].translation_version = "1.4" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) +ctld.i18n["fr"].translation_version = "1.5" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) local lang="fr";env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) --- groups names From 2bc6e765168eb0d034fd9898472e555cd9af9b72 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 09:57:33 +0100 Subject: [PATCH 063/166] update fr & es dict version number --- CTLD-i18n.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD-i18n.lua b/CTLD-i18n.lua index 9adaf2e..d48dab4 100644 --- a/CTLD-i18n.lua +++ b/CTLD-i18n.lua @@ -302,7 +302,7 @@ ctld.i18n["fr"]["STOP autoRefresh targets in LOS"] = "Stopper suivi automatique --====== SPANISH : ESPAÑOL==================================================================================== ctld.i18n["es"] = {} -ctld.i18n["es"].translation_version = "1.4" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) +ctld.i18n["es"].translation_version = "1.5" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) local lang="es";env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) --- groups names From b1818d9fb36b091a028ee04311a3079b4f44e0ea Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 09:57:57 +0100 Subject: [PATCH 064/166] update en dict version number --- CTLD.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index c0a246c..ec726ab 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -71,8 +71,7 @@ end -- If a string is not found in the current language then it will default to this language -- Note that no translation is provided for this language (obviously) but that we'll maintain this table to help the translators. ctld.i18n["en"] = {} -ctld.i18n["en"].translation_version = -"1.4" -- make sure that all the translations are compatible with this version of the english language texts +ctld.i18n["en"].translation_version = "1.5" -- make sure that all the translations are compatible with this version of the english language texts local lang = "en"; env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) From 96d801c3421f860370e0834a9f0b203e1f06ffd9 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 10:19:27 +0100 Subject: [PATCH 065/166] commit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index ec726ab..fa6ddb0 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -422,7 +422,7 @@ ctld.enableSmokeDrop = true -- ctld.maxExtractDistance = 125 -- max distance from vehicle to troops to allow a group extraction ctld.maximumDistanceLogistic = 200 -- max distance from vehicle to logistics to allow a loading or spawning operation ctld.enableRepackingVehicles = true -- if true, vehicles can be repacked into crates -ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units +ctld.maximumDistanceRepackableUnitsSearch = 200 -- max distance from transportUnit to search force repackable units in meters ctld.maximumSearchDistance = 4000 -- max distance for troops to search for enemy ctld.maximumMoveDistance = 2000 -- max distance for troops to move from drop point if no enemy is nearby ctld.minimumDeployDistance = 1000 -- minimum distance from a friendly pickup zone where you can deploy a crate From 23e83bf53a2f0baa601afd6c2294b9eb3388a1f1 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 10:32:31 +0100 Subject: [PATCH 066/166] commit --- CTLD.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index e56877d..4005a26 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -809,6 +809,7 @@ ctld.vehicleTransportEnabled = { "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR "Hercules", "UH-1H", + "CH-47Fbl1", } -- ************** Units able to use DCS dynamic cargo system ****************** @@ -816,7 +817,7 @@ ctld.vehicleTransportEnabled = { -- Units listed here will spawn a cargo static that can be loaded with the standard DCS cargo system -- We will also use this to make modifications to the menu and other checks and messages ctld.dynamicCargoUnits = { - "CH-47Fbl1", + -- "CH-47Fbl1", } -- ************** Maximum Units SETUP for UNITS ****************** From 546f3feaaeb49327c54de8839b0af04ce32b4865 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 10:53:04 +0100 Subject: [PATCH 067/166] commit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 4005a26..5e95a1f 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2065,7 +2065,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end if ctld.isUnitInALogisticZone(repackableUnitName) == nil then - ctld.addStaticLogisticUnit({ x = spawnRefPoint.x + 5, z = spawnRefPoint.z + 10 }, refCountry) -- create a temporary logistic unit to be able to repack the vehicle + --ctld.addStaticLogisticUnit({ x = spawnRefPoint.x + 5, z = spawnRefPoint.z + 10 }, refCountry) -- create a temporary logistic unit to be able to repack the vehicle end repackableUnit:destroy() -- destroy repacked unit end From 69f04dfd5794e2f2e22094e3405700eee455f783 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 10:53:37 +0100 Subject: [PATCH 068/166] commit --- CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 5e95a1f..e13eda8 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2064,9 +2064,9 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) end - if ctld.isUnitInALogisticZone(repackableUnitName) == nil then + --if ctld.isUnitInALogisticZone(repackableUnitName) == nil then --ctld.addStaticLogisticUnit({ x = spawnRefPoint.x + 5, z = spawnRefPoint.z + 10 }, refCountry) -- create a temporary logistic unit to be able to repack the vehicle - end + --end repackableUnit:destroy() -- destroy repacked unit end ctld.repackRequestsStack[ii] = nil From f0f095e2748247401b7dcaacc428aa9d7902fa4d Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 11:07:52 +0100 Subject: [PATCH 069/166] commit --- CTLD.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/CTLD.lua b/CTLD.lua index e13eda8..ecb15d3 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -809,6 +809,7 @@ ctld.vehicleTransportEnabled = { "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR "Hercules", "UH-1H", + "Mi-8MT", "CH-47Fbl1", } From d50bcfccd9f75459939517e05816cc06cd4498e1 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 16:18:08 +0100 Subject: [PATCH 070/166] commit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index ecb15d3..1a5fcdc 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1036,7 +1036,7 @@ ctld.spawnableCrates = { { multiple = { 1000.03, 1000.03 }, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit = "LAV-25", side = 2, cratesRequired = 3 }, { multiple = { 1000.04, 1000.04, 1000.04 }, desc = ctld.i18n_translate("Med Tank - LAV-25 - All crates"), side = 2 }, - { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit = "M1A2C_SEP_V3", side = 2, cratesRequired = 4 }, + { weight = 1000.05, desc = ctld.i18n_translate("Heavy Tank - Abrams"), unit = "M-1 Abrams", side = 2, cratesRequired = 4 }, { multiple = { 1000.05, 1000.05, 1000.05, 1000.05 }, desc = ctld.i18n_translate("Heavy Tank - Abrams - All crates"), side = 2 }, --- RED From c1aba95b6f605f3276afa5f9f0f711e2cb517f15 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 16:24:36 +0100 Subject: [PATCH 071/166] for debug --- CTLD.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 1a5fcdc..23c9d0d 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2032,7 +2032,8 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if t == nil then t = timer.getTime() end - --ctld.logTrace("FG_ ctld.repackVehicle = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) + ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) + ctld.logTrace("FG_ ctld.repackVehicle._params = %s", ctld.p(mist.utils.tableShow(_params))) for ii, v in ipairs(ctld.repackRequestsStack) do local repackableUnitName = v[1].repackableUnitName local crateWeight = v[1].weight From e4f13f98b1d6548fbe58eac71ae9b1dd05e2dccc Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 19:36:58 +0100 Subject: [PATCH 072/166] commit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 23c9d0d..cfca636 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -8088,7 +8088,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end end, nil, timer.getTime() + 1) From d8db662dc52042c4cb5678f3968e6301d517e03f Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:07:45 +0100 Subject: [PATCH 073/166] debug --- CTLD.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index cfca636..e67cc39 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6037,7 +6037,7 @@ end --****************************************************************************************************** function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each transport unit - --ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.ctld.transportPilotNames = %s", ctld.p(mist.utils.tableShow(ctld.transportPilotNames))) + ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.ctld.transportPilotNames = %s", ctld.p(mist.utils.tableShow(ctld.transportPilotNames))) if t == nil then t = timer.getTime() end if ctld.enableRepackingVehicles then for _, _unitName in pairs(ctld.transportPilotNames) do @@ -8088,7 +8088,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end end, nil, timer.getTime() + 1) @@ -8207,6 +8207,7 @@ function ctld.eventHandler:onEvent(event) -- Allow units to CTLD by aircraft type and not by pilot name if ctld.addPlayerAircraftByType then for _, aircraftType in pairs(ctld.aircraftTypeTable) do + ctld.logTrace("FG_ XXXXXXXXXXXXXXXXXXX aircraftType == playerTypeName XXXXXXXXXXXXXXXXXXXXXXXX %s - %s", ctld.p(aircraftType), ctld.p(playerTypeName)) if aircraftType == playerTypeName then ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName)) -- add transport unit to the list From aef146a6d881b69b4713ec9dcc7dc7a8720fd7f5 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:16:48 +0100 Subject: [PATCH 074/166] debug --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index e67cc39..4989694 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -8193,7 +8193,7 @@ function ctld.eventHandler:onEvent(event) end local function processHumanPlayer() - ctld.logTrace("in the 'processHumanPlayer' function") + ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- unitName = %s", unitName) if mist.DBs.humansByName[unitName] then -- it's a human unit ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) local _unit = Unit.getByName(unitName) From 72aab26e43a82462dfc1d273d04f73e436e19d3d Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:39:04 +0100 Subject: [PATCH 075/166] debug --- CTLD.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 4989694..e4e327e 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -8193,7 +8193,9 @@ function ctld.eventHandler:onEvent(event) end local function processHumanPlayer() - ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- unitName = %s", unitName) + ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- unitName = %s", ctld.p(unitName)) + ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName[unitName] = %s", ctld.p(mist.DBs.humansByName[unitName])) + ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName = %s", ctld.p(mist.DBs.humansByName)) if mist.DBs.humansByName[unitName] then -- it's a human unit ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) local _unit = Unit.getByName(unitName) From 9023c571bab0f86e0e604362d4342edd43e95f5e Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 22:54:18 +0100 Subject: [PATCH 076/166] debug --- CTLD.lua | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index e4e327e..fb9323b 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -8192,11 +8192,14 @@ function ctld.eventHandler:onEvent(event) return end - local function processHumanPlayer() + local function processHumanPlayer(mistDBsHumansByName) ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- unitName = %s", ctld.p(unitName)) ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName[unitName] = %s", ctld.p(mist.DBs.humansByName[unitName])) ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName = %s", ctld.p(mist.DBs.humansByName)) - if mist.DBs.humansByName[unitName] then -- it's a human unit + ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mistDBsHumansByName[unitName] = %s", ctld.p(mistDBsHumansByName[unitName])) + ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mistDBsHumansByName = %s", ctld.p(mistDBsHumansByName)) + --if mist.DBs.humansByName[unitName] then -- it's a human unit + if mistDBsHumansByName[unitName] then -- it's a human unit ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) local _unit = Unit.getByName(unitName) if _unit ~= nil then @@ -8237,11 +8240,11 @@ function ctld.eventHandler:onEvent(event) ctld.logTrace("give MiST some time to handle the BIRTH event too") timer.scheduleFunction(function() ctld.logTrace("calling the 'processHumanPlayer' function in a timer") - processHumanPlayer() + processHumanPlayer(mist.DBs.humansByName) end, nil, timer.getTime() + 0.5) else ctld.logTrace("calling the 'processHumanPlayer' function immediately") - processHumanPlayer() + processHumanPlayer(mist.DBs.humansByName) end end From e0e4279f20f3cffaeec9f43358de87089c8eb098 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 23:00:17 +0100 Subject: [PATCH 077/166] debug --- CTLD.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index fb9323b..7f9d0fd 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -8238,10 +8238,10 @@ function ctld.eventHandler:onEvent(event) if not mist.DBs.humansByName[unitName] then -- give a few milliseconds for MiST to handle the BIRTH event too ctld.logTrace("give MiST some time to handle the BIRTH event too") - timer.scheduleFunction(function() + timer.scheduleFunction(function(mistDBsHumansByName, t) ctld.logTrace("calling the 'processHumanPlayer' function in a timer") - processHumanPlayer(mist.DBs.humansByName) - end, nil, timer.getTime() + 0.5) + processHumanPlayer(mistDBsHumansByName) + end, {mist.DBs.humansByName}, timer.getTime() + 0.5) else ctld.logTrace("calling the 'processHumanPlayer' function immediately") processHumanPlayer(mist.DBs.humansByName) From 601805abb03681e15502b1b246bb876fa667fe1c Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 23:06:27 +0100 Subject: [PATCH 078/166] debug --- CTLD.lua | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 7f9d0fd..78e7bb9 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -8192,14 +8192,11 @@ function ctld.eventHandler:onEvent(event) return end - local function processHumanPlayer(mistDBsHumansByName) + local function processHumanPlayer() ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- unitName = %s", ctld.p(unitName)) ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName[unitName] = %s", ctld.p(mist.DBs.humansByName[unitName])) ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName = %s", ctld.p(mist.DBs.humansByName)) - ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mistDBsHumansByName[unitName] = %s", ctld.p(mistDBsHumansByName[unitName])) - ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mistDBsHumansByName = %s", ctld.p(mistDBsHumansByName)) - --if mist.DBs.humansByName[unitName] then -- it's a human unit - if mistDBsHumansByName[unitName] then -- it's a human unit + if mist.DBs.humansByName[unitName] then -- it's a human unit ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) local _unit = Unit.getByName(unitName) if _unit ~= nil then @@ -8238,13 +8235,13 @@ function ctld.eventHandler:onEvent(event) if not mist.DBs.humansByName[unitName] then -- give a few milliseconds for MiST to handle the BIRTH event too ctld.logTrace("give MiST some time to handle the BIRTH event too") - timer.scheduleFunction(function(mistDBsHumansByName, t) + timer.scheduleFunction(function() ctld.logTrace("calling the 'processHumanPlayer' function in a timer") - processHumanPlayer(mistDBsHumansByName) - end, {mist.DBs.humansByName}, timer.getTime() + 0.5) + processHumanPlayer() + end, nil, timer.getTime() + 1) else ctld.logTrace("calling the 'processHumanPlayer' function immediately") - processHumanPlayer(mist.DBs.humansByName) + processHumanPlayer() end end From eb22f5dc67a236b0817a31bd50d634fb4af1c684 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 23:20:10 +0100 Subject: [PATCH 079/166] debug --- CTLD.lua | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 78e7bb9..718e380 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -8210,7 +8210,7 @@ function ctld.eventHandler:onEvent(event) if ctld.addPlayerAircraftByType then for _, aircraftType in pairs(ctld.aircraftTypeTable) do ctld.logTrace("FG_ XXXXXXXXXXXXXXXXXXX aircraftType == playerTypeName XXXXXXXXXXXXXXXXXXXXXXXX %s - %s", ctld.p(aircraftType), ctld.p(playerTypeName)) - if aircraftType == playerTypeName then + if aircraftType == playerTypeName and ctld.isValueInIpairTable(ctld.transportPilotNames, unitName) == false then ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName)) -- add transport unit to the list table.insert(ctld.transportPilotNames, unitName) @@ -8238,7 +8238,7 @@ function ctld.eventHandler:onEvent(event) timer.scheduleFunction(function() ctld.logTrace("calling the 'processHumanPlayer' function in a timer") processHumanPlayer() - end, nil, timer.getTime() + 1) + end, nil, timer.getTime() + 1.5) else ctld.logTrace("calling the 'processHumanPlayer' function immediately") processHumanPlayer() @@ -8287,6 +8287,16 @@ function ctld.RandomReal(mini, maxi) return result end +-- Tools +function ctld.isValueInIpairTable(tab, value) + for i, v in ipairs(tab) do + if v == value then + return true -- La valeur existe + end + end + return false -- La valeur n'existe pas + end + --- Enable/Disable error boxes displayed on screen. env.setErrorMessageBoxEnabled(false) From 6fa12ca46fba319afc3cbd5b2cc7563cae69caaa Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 29 Mar 2025 23:49:50 +0100 Subject: [PATCH 080/166] debug --- CTLD.lua | 61 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 718e380..f760f24 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2073,7 +2073,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end ctld.repackRequestsStack[ii] = nil end - ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units + --ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units end if ctld.enableRepackingVehicles == true then return t + 3 -- reschedule the function in 3 seconds @@ -5826,8 +5826,8 @@ function ctld.addTransportF10MenuOptions(_unitName) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), _vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true }) - if ctld.vehicleCommandsPath == nil then - ctld.vehicleCommandsPath = mist.utils.deepCopy(_vehicleCommandsPath) + if ctld.vehicleCommandsPath[_unitName] == nil then + ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) end if ctld.enableRepackingVehicles then @@ -6005,26 +6005,26 @@ end --****************************************************************************************************** function ctld.updateRepackMenu(_playerUnitName) + ctld.logTrace("FG_ ctld.updateRepackMenu._playerUnitName = %s", ctld.p(_playerUnitName)) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then local _unitTypename = playerUnit:getTypeName() local _groupId = ctld.getGroupId(playerUnit) if ctld.enableRepackingVehicles then - local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, - ctld.maximumDistanceRepackableUnitsSearch) + local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then - --ctld.logTrace("FG_ ctld.updateRepackMenu.ctld.vehicleCommandsPath = %s", ctld.p(ctld.vehicleCommandsPath)) - local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath) + ctld.logTrace("FG_ ctld.updateRepackMenu.ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") - --ctld.logTrace("FG_ ctld.updateRepackMenu.RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) + ctld.logTrace("FG_ ctld.updateRepackMenu.RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) missionCommands.removeItemForGroup(1, RepackCommandsPath) -- remove the old repack menu - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath) + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} for _, _vehicle in pairs(repackableVehicles) do table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, groupId = _groupId, - subMenuPath = RepackCommandsPath, + subMenuPath = RepackmenuPath, menuFunction = ctld.repackVehicleRequest, menuArgsTable = { _vehicle, _playerUnitName } }) @@ -6037,26 +6037,27 @@ end --****************************************************************************************************** function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each transport unit - ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.ctld.transportPilotNames = %s", ctld.p(mist.utils.tableShow(ctld.transportPilotNames))) + --ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.ctld.transportPilotNames = %s", ctld.p(mist.utils.tableShow(ctld.transportPilotNames))) if t == nil then t = timer.getTime() end if ctld.enableRepackingVehicles then for _, _unitName in pairs(ctld.transportPilotNames) do local status, error = pcall( - function() - local _unit = ctld.getTransportUnit(_unitName) - if _unit then - -- if transport unit landed => update repack menus - if (ctld.inAir(_unit) == false or (ctld.heightDiff(_unit) <= 0.1 + 3.0 and mist.vec.mag(_unit:getVelocity()) < 0.1)) then - local _unitTypename = _unit:getTypeName() - local _groupId = ctld.getGroupId(_unit) - if _groupId then - if ctld.addedTo[tostring(_groupId)] ~= nil then - ctld.updateRepackMenu(_unitName) - end - end - end - end - end) + function() + local _unit = ctld.getTransportUnit(_unitName) + if _unit then + -- if transport unit landed => update repack menus + if (ctld.inAir(_unit) == false or (ctld.heightDiff(_unit) <= 0.1 + 3.0 and mist.vec.mag(_unit:getVelocity()) < 0.1)) then + local _unitTypename = _unit:getTypeName() + local _groupId = ctld.getGroupId(_unit) + if _groupId then + if ctld.addedTo[tostring(_groupId)] ~= nil then + ctld.logTrace("FG_ ctld.autoUpdateRepackMenu call ctld.updateRepackMenu ofr = %s", ctld.p(_unitName)) + ctld.updateRepackMenu(_unitName) + end + end + end + end + end) if (not status) then env.error(string.format("Error in ctld.autoUpdateRepackMenu : %s", error), false) end @@ -7893,11 +7894,11 @@ function ctld.initialize() ctld.extractZones = {} -- stored extract zones - ctld.missionEditorCargoCrates = {} --crates added by mission editor for triggering cratesinzone + ctld.missionEditorCargoCrates = {} -- crates added by mission editor for triggering cratesinzone ctld.hoverStatus = {} -- tracks status of a helis hover above a crate ctld.callbacks = {} -- function callback - + ctld.vehicleCommandsPath = {} -- memory of F10 c=CTLD menu path bay unitNames -- Remove intransit troops when heli / cargo plane dies --ctld.eventHandler = {} @@ -8194,8 +8195,7 @@ function ctld.eventHandler:onEvent(event) local function processHumanPlayer() ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- unitName = %s", ctld.p(unitName)) - ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName[unitName] = %s", ctld.p(mist.DBs.humansByName[unitName])) - ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName = %s", ctld.p(mist.DBs.humansByName)) + --ctld.logTrace("in the 'processHumanPlayer' function processHumanPlayer()- mist.DBs.humansByName[unitName] = %s", ctld.p(mist.DBs.humansByName[unitName])) if mist.DBs.humansByName[unitName] then -- it's a human unit ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) local _unit = Unit.getByName(unitName) @@ -8209,7 +8209,6 @@ function ctld.eventHandler:onEvent(event) -- Allow units to CTLD by aircraft type and not by pilot name if ctld.addPlayerAircraftByType then for _, aircraftType in pairs(ctld.aircraftTypeTable) do - ctld.logTrace("FG_ XXXXXXXXXXXXXXXXXXX aircraftType == playerTypeName XXXXXXXXXXXXXXXXXXXXXXXX %s - %s", ctld.p(aircraftType), ctld.p(playerTypeName)) if aircraftType == playerTypeName and ctld.isValueInIpairTable(ctld.transportPilotNames, unitName) == false then ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName)) -- add transport unit to the list From 267156e04e5f2bef3cd9b9f43ed257d29322eed0 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 30 Mar 2025 00:00:34 +0100 Subject: [PATCH 081/166] debug --- CTLD.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/CTLD.lua b/CTLD.lua index f760f24..7edc0c5 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6020,6 +6020,7 @@ function ctld.updateRepackMenu(_playerUnitName) missionCommands.removeItemForGroup(1, RepackCommandsPath) -- remove the old repack menu local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} + ctld.logTrace("FG_ ctld.updateRepackMenu.RepackmenuPath = %s", ctld.p(RepackmenuPath)) for _, _vehicle in pairs(repackableVehicles) do table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, From 879272a2e85d61ef280eaf6bf787596f05eab3cc Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 30 Mar 2025 00:31:35 +0100 Subject: [PATCH 082/166] debug --- CTLD.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 7edc0c5..8fb62f7 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5980,26 +5980,26 @@ end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - --ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) + ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do - --ctld.logTrace("FG ctld.buildPaginatedMenu. [%s] mist.utils.tableShow(menu) = [%s]", i, mist.utils.tableShow(menu)) + ctld.logTrace("FG ctld.buildPaginatedMenu.boucle[%s].menu = %s", i, ctld.p(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then menu.subMenuPath = nextSubMenuPath end -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 - if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached - menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), - menu.subMenuPath) + if itemNbSubmenu == 11 and i < #_menuEntries then -- page limit reached + menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) nextSubMenuPath = menu.subMenuPath itemNbSubmenu = 1 end menu.menuArgsTable.subMenuPath = menu.subMenuPath menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu - missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, - menu.menuArgsTable) + ctld.logTrace("FG ctld.buildPaginatedMenu.boucle[%s].menu.subMenuPath = %s", i, ctld.p(menu.subMenuPath)) + + --missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) end end From ca99c58f9850325fb85ddda50d4388cd83d9e004 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 30 Mar 2025 12:21:43 +0200 Subject: [PATCH 083/166] commit --- CTLD.lua | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 8fb62f7..9654adb 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2032,8 +2032,8 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if t == nil then t = timer.getTime() end - ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) - ctld.logTrace("FG_ ctld.repackVehicle._params = %s", ctld.p(mist.utils.tableShow(_params))) + --ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) + --ctld.logTrace("FG_ ctld.repackVehicle._params = %s", ctld.p(mist.utils.tableShow(_params))) for ii, v in ipairs(ctld.repackRequestsStack) do local repackableUnitName = v[1].repackableUnitName local crateWeight = v[1].weight @@ -5831,7 +5831,7 @@ function ctld.addTransportF10MenuOptions(_unitName) end if ctld.enableRepackingVehicles then - ctld.updateRepackMenu(_unitName) + --ctld.updateRepackMenu(_unitName) end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then missionCommands.addCommandForGroup(_groupId, @@ -5980,32 +5980,38 @@ end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - ctld.logTrace("FG_ ctld.buildPaginatedMenu._menuEntries = [%s]", ctld.p(_menuEntries)) + ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do - ctld.logTrace("FG ctld.buildPaginatedMenu.boucle[%s].menu = %s", i, ctld.p(menu)) + ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then + ctld.logTrace("FG_ boucle[%s].nextSubMenuPath = %s", i, ctld.p(nextSubMenuPath)) menu.subMenuPath = nextSubMenuPath end -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 - if itemNbSubmenu == 11 and i < #_menuEntries then -- page limit reached + if itemNbSubmenu == 10 then + ctld.logTrace("FG_ boucle[%s].menu.subMenuPath avant = %s", i, ctld.p(menu.subMenuPath)) menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) nextSubMenuPath = menu.subMenuPath - itemNbSubmenu = 1 + ctld.logTrace("FG_ boucle[%s].menu.subMenuPath apres = %s", i, ctld.p(menu.subMenuPath)) + if i == #_menuEntries then -- page limit reached + itemNbSubmenu = 10 + else + itemNbSubmenu = 1 + end end menu.menuArgsTable.subMenuPath = menu.subMenuPath menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu - ctld.logTrace("FG ctld.buildPaginatedMenu.boucle[%s].menu.subMenuPath = %s", i, ctld.p(menu.subMenuPath)) - - --missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) + ctld.logTrace("FG_ boucle[%s].menu.subMenuPath = %s", i, ctld.p(menu.subMenuPath)) + missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) end end --****************************************************************************************************** function ctld.updateRepackMenu(_playerUnitName) - ctld.logTrace("FG_ ctld.updateRepackMenu._playerUnitName = %s", ctld.p(_playerUnitName)) + ctld.logTrace("FG_ _playerUnitName = %s", ctld.p(_playerUnitName)) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then local _unitTypename = playerUnit:getTypeName() @@ -6013,14 +6019,14 @@ function ctld.updateRepackMenu(_playerUnitName) if ctld.enableRepackingVehicles then local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then - ctld.logTrace("FG_ ctld.updateRepackMenu.ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) + ctld.logTrace("FG__ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") - ctld.logTrace("FG_ ctld.updateRepackMenu.RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) - missionCommands.removeItemForGroup(1, RepackCommandsPath) -- remove the old repack menu + --ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) + missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove the old repack menu local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} - ctld.logTrace("FG_ ctld.updateRepackMenu.RepackmenuPath = %s", ctld.p(RepackmenuPath)) + --ctld.logTrace("FG_ RepackmenuPath = %s", ctld.p(RepackmenuPath)) for _, _vehicle in pairs(repackableVehicles) do table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, @@ -6053,7 +6059,7 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr if _groupId then if ctld.addedTo[tostring(_groupId)] ~= nil then ctld.logTrace("FG_ ctld.autoUpdateRepackMenu call ctld.updateRepackMenu ofr = %s", ctld.p(_unitName)) - ctld.updateRepackMenu(_unitName) + --ctld.updateRepackMenu(_unitName) end end end @@ -8090,7 +8096,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end end, nil, timer.getTime() + 1) From 6df583b9f1e596d9a397804a72d6d919f345573c Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 30 Mar 2025 12:23:40 +0200 Subject: [PATCH 084/166] commit --- CTLD.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 9654adb..220740b 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5831,7 +5831,7 @@ function ctld.addTransportF10MenuOptions(_unitName) end if ctld.enableRepackingVehicles then - --ctld.updateRepackMenu(_unitName) + ctld.updateRepackMenu(_unitName) end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then missionCommands.addCommandForGroup(_groupId, @@ -6059,7 +6059,7 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr if _groupId then if ctld.addedTo[tostring(_groupId)] ~= nil then ctld.logTrace("FG_ ctld.autoUpdateRepackMenu call ctld.updateRepackMenu ofr = %s", ctld.p(_unitName)) - --ctld.updateRepackMenu(_unitName) + ctld.updateRepackMenu(_unitName) end end end @@ -8096,7 +8096,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end end, nil, timer.getTime() + 1) From 12741d05328cbe56cefb22bde5615e17ef14dc6f Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 30 Mar 2025 12:55:22 +0200 Subject: [PATCH 085/166] commit --- CTLD.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 220740b..1d74697 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5982,7 +5982,7 @@ end function ctld.buildPaginatedMenu(_menuEntries) ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) local nextSubMenuPath = "" - local itemNbSubmenu = 0 + local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then @@ -5991,16 +5991,12 @@ function ctld.buildPaginatedMenu(_menuEntries) end -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 - if itemNbSubmenu == 10 then + if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached ctld.logTrace("FG_ boucle[%s].menu.subMenuPath avant = %s", i, ctld.p(menu.subMenuPath)) menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) nextSubMenuPath = menu.subMenuPath ctld.logTrace("FG_ boucle[%s].menu.subMenuPath apres = %s", i, ctld.p(menu.subMenuPath)) - if i == #_menuEntries then -- page limit reached - itemNbSubmenu = 10 - else - itemNbSubmenu = 1 - end + itemNbSubmenu = 1 end menu.menuArgsTable.subMenuPath = menu.subMenuPath menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu From 48aca9d4884fd060413273a3dc230c706a01a213 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 30 Mar 2025 15:07:49 +0200 Subject: [PATCH 086/166] commit --- CTLD.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 1d74697..4d994de 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2073,7 +2073,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end ctld.repackRequestsStack[ii] = nil end - --ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units + ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units end if ctld.enableRepackingVehicles == true then return t + 3 -- reschedule the function in 3 seconds @@ -5863,15 +5863,14 @@ function ctld.addTransportF10MenuOptions(_unitName) -- add the submenu item itemNbMain = itemNbMain + 1 - if itemNbMain == 10 and _i < #crateCategories then -- page limit reached + 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) + 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 @@ -5899,7 +5898,7 @@ function ctld.addTransportF10MenuOptions(_unitName) ctld.logTrace("_menu = [%s]", ctld.p(_menu)) -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 - if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached + if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Next page"), _subMenuPath) itemNbSubmenu = 1 @@ -6015,11 +6014,11 @@ function ctld.updateRepackMenu(_playerUnitName) if ctld.enableRepackingVehicles then local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then - ctld.logTrace("FG__ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) + ctld.logTrace("FG_ ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") --ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) - missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove the old repack menu + missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} --ctld.logTrace("FG_ RepackmenuPath = %s", ctld.p(RepackmenuPath)) From 29b19d5262e94facb6365ce20c17ce6f760a6332 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 30 Mar 2025 15:24:20 +0200 Subject: [PATCH 087/166] commit --- CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 4d994de..67a4d4c 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1030,8 +1030,8 @@ ctld.spawnableCrates = { --- BLUE { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types - { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, - { multiple = { 1000.02, 1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, + { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 1 }, + { multiple = { 1000.02, 1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 1 }, { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit = "MaxxPro_MRAP", side = 2, cratesRequired = 2 }, { multiple = { 1000.03, 1000.03 }, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit = "LAV-25", side = 2, cratesRequired = 3 }, From f8df24304328c61a65b7a655761ff22c28f3fd6e Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 30 Mar 2025 15:42:07 +0200 Subject: [PATCH 088/166] commit --- CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 67a4d4c..b565e91 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -810,7 +810,7 @@ ctld.vehicleTransportEnabled = { "Hercules", "UH-1H", "Mi-8MT", - "CH-47Fbl1", + --"CH-47Fbl1", } -- ************** Units able to use DCS dynamic cargo system ****************** @@ -818,7 +818,7 @@ ctld.vehicleTransportEnabled = { -- Units listed here will spawn a cargo static that can be loaded with the standard DCS cargo system -- We will also use this to make modifications to the menu and other checks and messages ctld.dynamicCargoUnits = { - -- "CH-47Fbl1", + "CH-47Fbl1", } -- ************** Maximum Units SETUP for UNITS ****************** From 91f74b73edb37656e2206c791c1bad2ae67eafb9 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 30 Mar 2025 16:25:31 +0200 Subject: [PATCH 089/166] commit --- CTLD.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index b565e91..f961709 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1977,12 +1977,16 @@ function ctld.getNearbyUnits(_point, _radius, _coalition) local _unitList = mist.DBs.unitsByName for k, _unit in pairs(mist.DBs.unitsByName) do local u = Unit.getByName(k) - if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then - --local _dist = ctld.getDistance(u:getPoint(), _point) - local _dist = mist.utils.get2DDist(u:getPoint(), _point) - if _dist <= _radius then - table.insert(_units, k) -- insert nearby unitName + if u then + if u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then + --local _dist = ctld.getDistance(u:getPoint(), _point) + local _dist = mist.utils.get2DDist(u:getPoint(), _point) + if _dist <= _radius then + table.insert(_units, k) -- insert nearby unitName + end end + else + ctld.logTrace("FG_ unitName(k) = %s", ctld.p(k)) end end return _units From ca976f4387a8450e0f220b11cd74e16ecd0f5d01 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 30 Mar 2025 23:42:53 +0200 Subject: [PATCH 090/166] commit --- CTLD.lua | 49 ++++++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index b565e91..52b41bd 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1896,8 +1896,8 @@ end -- -- Weights can be found in the ctld.spawnableCrates list -- Points can be made by hand or obtained from a Unit position by Unit.getByName("PilotName"):getPoint() --- e.g. ctld.spawnCrateAtZone("red", 500,{x=1,y=2,z=3}) -- spawn a humvee at triggerzone 1 for red side at a specified point --- e.g. ctld.spawnCrateAtZone("blue", 505,{x=1,y=2,z=3}) -- spawn a tow humvee at triggerzone1 for blue side at a specified point +-- e.g. ctld.spawnCrateAtPoint("red", 500,{x=1,y=2,z=3}) -- spawn a humvee at triggerzone 1 for red side at a specified point +-- e.g. ctld.spawnCrateAtPoint("blue", 505,{x=1,y=2,z=3}) -- spawn a tow humvee at triggerzone1 for blue side at a specified point -- -- function ctld.spawnCrateAtPoint(_side, _weight, _point, _hdg) @@ -2062,13 +2062,16 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, relativePoint, _name, crateWeight, - PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) + if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, relativePoint, _name, crateWeight, + PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) + else + local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) + ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, + PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, "dynamic")) + end end - --if ctld.isUnitInALogisticZone(repackableUnitName) == nil then - --ctld.addStaticLogisticUnit({ x = spawnRefPoint.x + 5, z = spawnRefPoint.z + 10 }, refCountry) -- create a temporary logistic unit to be able to repack the vehicle - --end repackableUnit:destroy() -- destroy repacked unit end ctld.repackRequestsStack[ii] = nil @@ -2083,7 +2086,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end -- *************************************************************** -function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit to be able to repack the vehicle +function ctld.addStaticLogisticUnit(_point, _country) -- create a temporary logistic unit with a Windsock object local dynamicLogisticUnitName = "%dynLogisticName_" .. tostring(ctld.getNextDynamicLogisticUnitIndex()) ctld.logisticUnits[#ctld.logisticUnits + 1] = dynamicLogisticUnitName local LogUnit = { @@ -2503,7 +2506,6 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) local _status, _err = pcall(function(_args) -- use the cargo weight to guess the type of unit as no way to add description :( local _crateType = ctld.crateLookupTable[tostring(_args[2])] - local _heli = ctld.getTransportUnit(_args[1]) if not _heli then return @@ -2531,7 +2533,6 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) if ctld.inLogisticsZone(_heli) == false then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not close enough to friendly logistics to get a crate!"), 10) - return end @@ -5907,9 +5908,20 @@ function ctld.addTransportF10MenuOptions(_unitName) ctld.spawnCrate, { _unitName, _menu.crate.weight }) end end + if ctld.unitDynamicCargoCapable(_unit) then + if ctld.vehicleCommandsPath[_unitName] == nil then + ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_rootPath) + end + if ctld.enableRepackingVehicles then + ctld.updateRepackMenu(_unitName) + 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) @@ -6015,7 +6027,7 @@ function ctld.updateRepackMenu(_playerUnitName) local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then ctld.logTrace("FG_ ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) - local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") --ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu @@ -6023,13 +6035,12 @@ function ctld.updateRepackMenu(_playerUnitName) local menuEntries = {} --ctld.logTrace("FG_ RepackmenuPath = %s", ctld.p(RepackmenuPath)) for _, _vehicle in pairs(repackableVehicles) do - table.insert(menuEntries, { - text = ctld.i18n_translate("repack ") .. _vehicle.unit, - groupId = _groupId, - subMenuPath = RepackmenuPath, - menuFunction = ctld.repackVehicleRequest, - menuArgsTable = { _vehicle, _playerUnitName } - }) + table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, + groupId = _groupId, + subMenuPath = RepackmenuPath, + menuFunction = ctld.repackVehicleRequest, + menuArgsTable = { _vehicle, _playerUnitName } + }) end ctld.buildPaginatedMenu(menuEntries) end From e1b9e1d7d8f11d7919c96e458c51f5bafb606078 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 31 Mar 2025 12:19:41 +0200 Subject: [PATCH 091/166] commit --- CTLD.lua | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 52b41bd..fac9a31 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1973,18 +1973,26 @@ function ctld.getNearbyUnits(_point, _radius, _coalition) if _coalition == nil then _coalition = 4 -- all coalitions end + local unitsByDistance = {} + local cpt = 1 local _units = {} local _unitList = mist.DBs.unitsByName - for k, _unit in pairs(mist.DBs.unitsByName) do + for _unitName, _unit in pairs(mist.DBs.unitsByName) do local u = Unit.getByName(k) if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then --local _dist = ctld.getDistance(u:getPoint(), _point) local _dist = mist.utils.get2DDist(u:getPoint(), _point) if _dist <= _radius then - table.insert(_units, k) -- insert nearby unitName + unitsByDistance[cpt] = {id =c pt, dist = _dist, unit = _unitName} + cpt = cpt + 1 end end end + + table.sort(unitsByDistance, function(a,b) return a.dist < b.dist end) -- sort the table by distance + for i, v in ipairs(unitsByDistance) do + table.insert(_units, v.unit) -- insert nearby unitName + end return _units end @@ -5006,8 +5014,7 @@ function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) local _spreadMult = 1 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) } + local _details = { type = _types[_i], unitId = _unitId, name = string.format("Unpacked %s #%i", _types[_i], _unitId) } if _hdgs and _hdgs[_i] then _hdg = _hdgs[_i] @@ -5164,7 +5171,7 @@ function ctld.spawnDroppedGroup(_point, _details, _spawnBehind, _maxSearch) local _pos = _point --try to spawn at 6 oclock to us - local _angle = math.atan2(_pos.z, _pos.x) + local _angle = math.atan(_pos.z, _pos.x) local _xOffset = math.cos(_angle) * -30 local _yOffset = math.sin(_angle) * -30 From 3eef8d9a5d5adeeff27b04b9a395b1ddccb03f85 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 31 Mar 2025 12:22:39 +0200 Subject: [PATCH 092/166] ctld.getNearbyUnits : sort by distance --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index fac9a31..3590d43 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1989,7 +1989,7 @@ function ctld.getNearbyUnits(_point, _radius, _coalition) end end - table.sort(unitsByDistance, function(a,b) return a.dist < b.dist end) -- sort the table by distance + table.sort(unitsByDistance, function(a,b) return a.dist < b.dist end) -- sort the table by distance (the nearest first) for i, v in ipairs(unitsByDistance) do table.insert(_units, v.unit) -- insert nearby unitName end From f9cd8b8b51c2edf658035e39689a7b4b9df0e96d Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 31 Mar 2025 18:05:13 +0200 Subject: [PATCH 093/166] commit --- CTLD.lua | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 3590d43..047a550 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1924,20 +1924,29 @@ function ctld.spawnCrateAtPoint(_side, _weight, _point, _hdg) ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side, _hdg) end -function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName +-- *************************************************************** +function ctld.getUnitDimensions(_unitName) -- return unit dimùension (widht,longer,hight) if Unit.getByName(_unitName) then + local dimensions = {} local unitBoundingBox = Unit.getByName(_unitName):getDesc().box - local widht = unitBoundingBox.max.x - unitBoundingBox.min.x - local longer = unitBoundingBox.max.z - unitBoundingBox.min.z - local hight = unitBoundingBox.max.y - unitBoundingBox.min.y - + dimensions.widht = unitBoundingBox.max.x - unitBoundingBox.min.x + dimensions.longer = unitBoundingBox.max.z - unitBoundingBox.min.z + dimensions.hight = unitBoundingBox.max.y - unitBoundingBox.min.y + return dimensions + end +end + +-- *************************************************************** +function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName + if Unit.getByName(_unitName) then + local dim = ctld.getUnitDimensions(_unitName) + -- distanceFromCenterToCorner = 1/2√(l² + w² + h²) - local squaresSum = longer^2 + widht^2 + hight^2 -- sum of squares - local squareRoots = math.sqrt(squaresSum) -- square roots - local distanceFromCenterToCorner = squareRoots / 2 -- Calculating distance (half square root) - return distanceFromCenterToCorner + local squaresSum = dim.longer^2 + dim.widht^2 + dim.hight^2 -- sum of squares + local distanceFromCenterToCorner = math.sqrt(squaresSum) / 2 -- Calculating distance (half square root) + return distanceFromCenterToCorner end - return nil_old + return nil end -- *************************************************************** @@ -1983,7 +1992,7 @@ function ctld.getNearbyUnits(_point, _radius, _coalition) --local _dist = ctld.getDistance(u:getPoint(), _point) local _dist = mist.utils.get2DDist(u:getPoint(), _point) if _dist <= _radius then - unitsByDistance[cpt] = {id =c pt, dist = _dist, unit = _unitName} + unitsByDistance[cpt] = {id =cpt, dist = _dist, unit = _unitName} cpt = cpt + 1 end end From 0a1f4de471b62a9361214b18cf14b28ff6a03811 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 1 Apr 2025 02:22:35 +0200 Subject: [PATCH 094/166] commit --- CTLD.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 047a550..6897777 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2618,7 +2618,7 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) end --*************************************************************** -ctld.randomCrateSpacing = 12 -- meters +ctld.randomCrateSpacing = 20 -- meters function ctld.getPointAt12Oclock(_unit, _offset) return ctld.getPointAtDirection(_unit, _offset, 0) end @@ -2629,16 +2629,15 @@ end function ctld.getPointAtDirection(_unit, _offset, _directionInRadian) ctld.logTrace("_offset = %s", ctld.p(_offset)) - local _randomOffsetX = math.random(ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing + local _SecureDistanceFromUnit = ctld.getSecureDistanceFromUnit(_unit:getName()) + local _randomOffsetX = math.random(_SecureDistanceFromUnit, ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing local _randomOffsetZ = math.random(0, ctld.randomCrateSpacing) ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) local _position = _unit:getPosition() - --local _angle = math.atan2(_position.x.z, _position.x.x) + _directionInRadian local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian - local _xOffset = math.cos(_angle) * _offset + _randomOffsetX - local _zOffset = math.sin(_angle) * _offset + _randomOffsetZ - + local _xOffset = math.cos(_angle) * (_offset + _randomOffsetX) + local _zOffset = math.sin(_angle) * (_offset + _randomOffsetZ) local _point = _unit:getPoint() return { x = _point.x + _xOffset, z = _point.z + _zOffset, y = _point.y } end From 774e499759123277e70fec90c6902f7d5c5844d2 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 1 Apr 2025 02:48:10 +0200 Subject: [PATCH 095/166] commit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 6897777..8b2466b 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1987,7 +1987,7 @@ function ctld.getNearbyUnits(_point, _radius, _coalition) local _units = {} local _unitList = mist.DBs.unitsByName for _unitName, _unit in pairs(mist.DBs.unitsByName) do - local u = Unit.getByName(k) + local u = Unit.getByName(_unitName) if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then --local _dist = ctld.getDistance(u:getPoint(), _point) local _dist = mist.utils.get2DDist(u:getPoint(), _point) From e834dd13956435bf1d4406b0c4d7d443a0839594 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Tue, 1 Apr 2025 03:04:26 +0200 Subject: [PATCH 096/166] debug --- CTLD.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 8b2466b..f4a85db 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5841,8 +5841,10 @@ function ctld.addTransportF10MenuOptions(_unitName) _vehicleCommandsPath, ctld.unloadTroops, { _unitName, false }) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), _vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true }) - + ctld.logDebug("PASS..1", ctld.p(_unitName)) + if ctld.vehicleCommandsPath[_unitName] == nil then + ctld.logDebug("PASS..2", ctld.p(_unitName)) ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) end From aaa25dcd41e4128e269adffa260c153c874dad45 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:28:50 +0200 Subject: [PATCH 097/166] commit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index f961709..e86710a 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1031,7 +1031,7 @@ ctld.spawnableCrates = { --- BLUE { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 1 }, - { multiple = { 1000.02, 1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 1 }, + { multiple = { 1000.02}, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 1 }, { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit = "MaxxPro_MRAP", side = 2, cratesRequired = 2 }, { multiple = { 1000.03, 1000.03 }, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit = "LAV-25", side = 2, cratesRequired = 3 }, From e6b8c9ba149d075a7bc97a0f43454589c06e00d0 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:34:00 +0200 Subject: [PATCH 098/166] ebug --- CTLD.lua | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 006d06b..8fc1965 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1986,18 +1986,15 @@ function ctld.getNearbyUnits(_point, _radius, _coalition) local cpt = 1 local _units = {} local _unitList = mist.DBs.unitsByName - for k, _unit in pairs(mist.DBs.unitsByName) do - local u = Unit.getByName(k) - if u then - if u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then - --local _dist = ctld.getDistance(u:getPoint(), _point) - local _dist = mist.utils.get2DDist(u:getPoint(), _point) - if _dist <= _radius then - table.insert(_units, k) -- insert nearby unitName - end + for _unitName, _unit in pairs(mist.DBs.unitsByName) do + local u = Unit.getByName(_unitName) + if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then + --local _dist = ctld.getDistance(u:getPoint(), _point) + local _dist = mist.utils.get2DDist(u:getPoint(), _point) + if _dist <= _radius then + unitsByDistance[cpt] = {id =cpt, dist = _dist, unit = _unitName} + cpt = cpt + 1 end - else - ctld.logTrace("FG_ unitName(k) = %s", ctld.p(k)) end end @@ -6084,7 +6081,7 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr local _groupId = ctld.getGroupId(_unit) if _groupId then if ctld.addedTo[tostring(_groupId)] ~= nil then - ctld.logTrace("FG_ ctld.autoUpdateRepackMenu call ctld.updateRepackMenu ofr = %s", ctld.p(_unitName)) + ctld.logTrace("FG_ ctld.autoUpdateRepackMenu call ctld.updateRepackMenu for = %s", ctld.p(_unitName)) ctld.updateRepackMenu(_unitName) end end @@ -8233,6 +8230,7 @@ function ctld.eventHandler:onEvent(event) ctld.logDebug("caught event %s for human unit [%s]", ctld.p(eventName), ctld.p(unitName)) local _unit = Unit.getByName(unitName) if _unit ~= nil then + local _groupId = _unit:getGroup():getID() -- assign transport pilot ctld.logTrace("_unit = %s", ctld.p(_unit)) @@ -8242,13 +8240,15 @@ function ctld.eventHandler:onEvent(event) -- Allow units to CTLD by aircraft type and not by pilot name if ctld.addPlayerAircraftByType then for _, aircraftType in pairs(ctld.aircraftTypeTable) do - if aircraftType == playerTypeName and ctld.isValueInIpairTable(ctld.transportPilotNames, unitName) == false then + if aircraftType == playerTypeName then ctld.logTrace("adding by aircraft type, unitName = %s", ctld.p(unitName)) - -- add transport unit to the list - table.insert(ctld.transportPilotNames, unitName) - -- add transport radio menu - ctld.addTransportF10MenuOptions(unitName) - break + if ctld.isValueInIpairTable(ctld.transportPilotNames, unitName) == false then + table.insert(ctld.transportPilotNames, unitName) -- add transport unit to the list + end + if ctld.addedTo[tostring(_groupId)] == nil then -- only if menu not already set up + ctld.addTransportF10MenuOptions(unitName) -- add transport radio menu + break + end end end else From 2524320096281a0260a09fede48f8338fffbcd26 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Tue, 1 Apr 2025 17:49:52 +0200 Subject: [PATCH 099/166] debug --- CTLD.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 8fc1965..f0762a1 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1988,7 +1988,7 @@ function ctld.getNearbyUnits(_point, _radius, _coalition) local _unitList = mist.DBs.unitsByName for _unitName, _unit in pairs(mist.DBs.unitsByName) do local u = Unit.getByName(_unitName) - if u and u:isActive() and (_coalition == 4 or u:getCoalition() == _coalition) then + if u and u:isExist() and (_coalition == 4 or u:getCoalition() == _coalition) then --local _dist = ctld.getDistance(u:getPoint(), _point) local _dist = mist.utils.get2DDist(u:getPoint(), _point) if _dist <= _radius then @@ -6008,11 +6008,11 @@ end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) + --ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do - ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) + --ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then ctld.logTrace("FG_ boucle[%s].nextSubMenuPath = %s", i, ctld.p(nextSubMenuPath)) menu.subMenuPath = nextSubMenuPath @@ -6020,15 +6020,15 @@ function ctld.buildPaginatedMenu(_menuEntries) -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached - ctld.logTrace("FG_ boucle[%s].menu.subMenuPath avant = %s", i, ctld.p(menu.subMenuPath)) + --ctld.logTrace("FG_ boucle[%s].menu.subMenuPath avant = %s", i, ctld.p(menu.subMenuPath)) menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) nextSubMenuPath = menu.subMenuPath - ctld.logTrace("FG_ boucle[%s].menu.subMenuPath apres = %s", i, ctld.p(menu.subMenuPath)) + --ctld.logTrace("FG_ boucle[%s].menu.subMenuPath apres = %s", i, ctld.p(menu.subMenuPath)) itemNbSubmenu = 1 end menu.menuArgsTable.subMenuPath = menu.subMenuPath menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu - ctld.logTrace("FG_ boucle[%s].menu.subMenuPath = %s", i, ctld.p(menu.subMenuPath)) + --ctld.logTrace("FG_ boucle[%s].menu.subMenuPath = %s", i, ctld.p(menu.subMenuPath)) missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) end end From 16f366d039628f7b142d810b1e8da1d99711aad7 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Tue, 1 Apr 2025 19:43:24 +0200 Subject: [PATCH 100/166] debug --- CTLD.lua | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index f0762a1..7a45940 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -3893,6 +3893,7 @@ function ctld.getCrateObject(_name) end function ctld.unpackCrates(_arguments) + ctld.logTrace("_arguments = %s", ctld.p(_arguments)) local _status, _err = pcall(function(_args) local _heli = ctld.getTransportUnit(_args[1]) @@ -3904,7 +3905,6 @@ function ctld.unpackCrates(_arguments) if ctld.inLogisticsZone(_heli) == true or ctld.farEnoughFromLogisticZone(_heli) == false then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) - return end @@ -3941,6 +3941,7 @@ function ctld.unpackCrates(_arguments) return else + ctld.logTrace("single crate = %s", ctld.p(_arguments)) -- single crate local _cratePoint = _crate.crateUnit:getPoint() local _crateName = _crate.crateUnit:getName() @@ -3950,10 +3951,11 @@ function ctld.unpackCrates(_arguments) -- if ctld.slingLoad == false then _crate.crateUnit:destroy() -- end - + ctld.logTrace("_crate = %s", ctld.p(_crate)) local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }, { _crateHdg }) - + ctld.logTrace("_spawnedGroups = %s", ctld.p(_arg_spawnedGroupsuments)) + if _heli:getCoalition() == 1 then ctld.spawnedCratesRED[_crateName] = nil else @@ -4998,6 +5000,11 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) end function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) + ctld.logTrace("_heli = %s", ctld.p(_heli)) + ctld.logTrace("_positions = %s", ctld.p(_positions)) + ctld.logTrace("_types = %s", ctld.p(_types)) + ctld.logTrace("_hdgs = %s", ctld.p(_hdgs)) + local _id = ctld.getNextGroupId() local _groupName = _types[1] .. " #" .. _id local _side = _heli:getCoalition() @@ -5023,7 +5030,7 @@ function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) 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) } - + ctld.logTrace("Group._details = %s", ctld.p(_details)) if _hdgs and _hdgs[_i] then _hdg = _hdgs[_i] end @@ -5143,6 +5150,7 @@ function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) end _group.country = _heli:getCountry() + ctld.logTrace("mist.dynAdd(") local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) return _spawnedGroup end @@ -6014,7 +6022,7 @@ function ctld.buildPaginatedMenu(_menuEntries) for i, menu in ipairs(_menuEntries) do --ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then - ctld.logTrace("FG_ boucle[%s].nextSubMenuPath = %s", i, ctld.p(nextSubMenuPath)) + --ctld.logTrace("FG_ boucle[%s].nextSubMenuPath = %s", i, ctld.p(nextSubMenuPath)) menu.subMenuPath = nextSubMenuPath end -- add the submenu item @@ -6035,7 +6043,7 @@ end --****************************************************************************************************** function ctld.updateRepackMenu(_playerUnitName) - ctld.logTrace("FG_ _playerUnitName = %s", ctld.p(_playerUnitName)) + --ctld.logTrace("FG_ _playerUnitName = %s", ctld.p(_playerUnitName)) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then local _unitTypename = playerUnit:getTypeName() @@ -6043,7 +6051,7 @@ function ctld.updateRepackMenu(_playerUnitName) if ctld.enableRepackingVehicles then local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then - ctld.logTrace("FG_ ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) + --ctld.logTrace("FG_ ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") --ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) @@ -6081,7 +6089,7 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr local _groupId = ctld.getGroupId(_unit) if _groupId then if ctld.addedTo[tostring(_groupId)] ~= nil then - ctld.logTrace("FG_ ctld.autoUpdateRepackMenu call ctld.updateRepackMenu for = %s", ctld.p(_unitName)) + --ctld.logTrace("FG_ ctld.autoUpdateRepackMenu call ctld.updateRepackMenu for = %s", ctld.p(_unitName)) ctld.updateRepackMenu(_unitName) end end From 8f34ad3902532eee6143fa4aa84198f6422ff566 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 2 Apr 2025 22:19:34 +0200 Subject: [PATCH 101/166] debug --- CTLD.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 7a45940..478e5dd 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2079,13 +2079,13 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit + local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, relativePoint, _name, crateWeight, - PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, true)) - else - local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) + PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit), nil) + else ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, - PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit, "dynamic")) + PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit), "dynamic") end end From 819ba2de53464d5cca296a9979e39ab06d3cc88a Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 2 Apr 2025 22:54:17 +0200 Subject: [PATCH 102/166] debug --- CTLD.lua | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 478e5dd..2d89a7e 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2049,7 +2049,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if t == nil then t = timer.getTime() end - --ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(mist.utils.tableShow(ctld.repackRequestsStack))) + ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) --ctld.logTrace("FG_ ctld.repackVehicle._params = %s", ctld.p(mist.utils.tableShow(_params))) for ii, v in ipairs(ctld.repackRequestsStack) do local repackableUnitName = v[1].repackableUnitName @@ -2058,8 +2058,9 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local playerUnitName = v[2] local PlayerTransportUnit = Unit.getByName(playerUnitName) + local playerCoa = PlayerTransportUnit:getCoalition() local TransportUnit = ctld.getTransportUnit(playerUnitName) - local spawnRefPoint = PlayerTransportUnit:getPoint() + local playerHeading = mist.getHeading(PlayerTransportUnit) local refCountry = PlayerTransportUnit:getCountry() if repackableUnit then @@ -2069,7 +2070,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local playerPoint = PlayerTransportUnit:getPoint() local offset = 5 local randomHeading = ctld.RandomReal(_playerHeading - math.pi/4, _playerHeading + math.pi/4) - for i = 1, v[1].cratesRequired do + for i = 1, v[1].cratesRequired or 1 do -- see to spawn the crate at random position heading the transport unnit local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v[1].desc, _unitId) @@ -2079,13 +2080,11 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit - local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) + local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, relativePoint, _name, crateWeight, - PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit), nil) + ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) else - ctld.spawnCrateStatic(PlayerTransportUnit:getCountry(), _unitId, _point, _name, crateWeight, - PlayerTransportUnit:getCoalition(), mist.getHeading(PlayerTransportUnit), "dynamic") + ctld.spawnCrateStatic(refCountry, _unitId, _point, _name, crateWeight, playerCoa, playerHeading, "dynamic") end end From 85275ad84bcd6699bbeb37837f14574f82a864ef Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 3 Apr 2025 00:50:19 +0200 Subject: [PATCH 103/166] debug --- CTLD.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 2d89a7e..bf94bc4 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2049,7 +2049,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if t == nil then t = timer.getTime() end - ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) + --ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) --ctld.logTrace("FG_ ctld.repackVehicle._params = %s", ctld.p(mist.utils.tableShow(_params))) for ii, v in ipairs(ctld.repackRequestsStack) do local repackableUnitName = v[1].repackableUnitName @@ -2059,7 +2059,6 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local playerUnitName = v[2] local PlayerTransportUnit = Unit.getByName(playerUnitName) local playerCoa = PlayerTransportUnit:getCoalition() - local TransportUnit = ctld.getTransportUnit(playerUnitName) local playerHeading = mist.getHeading(PlayerTransportUnit) local refCountry = PlayerTransportUnit:getCountry() @@ -3892,7 +3891,7 @@ function ctld.getCrateObject(_name) end function ctld.unpackCrates(_arguments) - ctld.logTrace("_arguments = %s", ctld.p(_arguments)) + ctld.logTrace("FG_ _arguments = %s", ctld.p(_arguments)) local _status, _err = pcall(function(_args) local _heli = ctld.getTransportUnit(_args[1]) From 1a32ce6b57fba175fcae85c50e51a832d975a11c Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 3 Apr 2025 00:50:30 +0200 Subject: [PATCH 104/166] debug --- CTLD.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CTLD.lua b/CTLD.lua index bf94bc4..7c095b5 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -4939,6 +4939,7 @@ function ctld.repairAASystem(_heli, _nearestCrate, _aaSystem) end function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) + ctld.logTrace("FG_ ctld.unpackMultiCrate, _nearestCrate = %s", ctld.p(_nearestCrate)) -- unpack multi crate local _nearbyMultiCrates = {} @@ -4998,6 +4999,8 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) end function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) + ctld.logTrace("FG_ ctld.spawnCrateGroup _positions = %s", ctld.p(_positions)) + ctld.logTrace("_heli = %s", ctld.p(_heli)) ctld.logTrace("_positions = %s", ctld.p(_positions)) ctld.logTrace("_types = %s", ctld.p(_types)) From d071fba20ffec74ff1b188ddfa5f7787f67dffbf Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 3 Apr 2025 01:10:03 +0200 Subject: [PATCH 105/166] debug --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 7c095b5..fb0d1bc 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1031,7 +1031,7 @@ ctld.spawnableCrates = { --- BLUE { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 1 }, - { multiple = { 1000.02}, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 1 }, + { multiple = { 1000.02}, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit = "MaxxPro_MRAP", side = 2, cratesRequired = 2 }, { multiple = { 1000.03, 1000.03 }, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit = "LAV-25", side = 2, cratesRequired = 3 }, From 0345d1615078f72a76df74c63751bccead53d6a0 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 3 Apr 2025 02:02:41 +0200 Subject: [PATCH 106/166] debug --- CTLD.lua | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index fb0d1bc..2387d31 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -3899,7 +3899,6 @@ function ctld.unpackCrates(_arguments) local _crates = ctld.getCratesAndDistance(_heli) local _crate = ctld.getClosestCrate(_heli, _crates) - if ctld.inLogisticsZone(_heli) == true or ctld.farEnoughFromLogisticZone(_heli) == false then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) @@ -3942,17 +3941,16 @@ function ctld.unpackCrates(_arguments) ctld.logTrace("single crate = %s", ctld.p(_arguments)) -- single crate local _cratePoint = _crate.crateUnit:getPoint() - local _crateName = _crate.crateUnit:getName() - local _crateHdg = mist.getHeading(_crate.crateUnit, true) + local _crateName = _crate.crateUnit:getName() + local _crateHdg = mist.getHeading(_crate.crateUnit, true) --remove crate -- if ctld.slingLoad == false then _crate.crateUnit:destroy() -- end ctld.logTrace("_crate = %s", ctld.p(_crate)) - local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }, - { _crateHdg }) - ctld.logTrace("_spawnedGroups = %s", ctld.p(_arg_spawnedGroupsuments)) + local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }, { _crateHdg }) + ctld.logTrace("_spawnedGroups = %s", ctld.p(_spawnedGroups)) if _heli:getCoalition() == 1 then ctld.spawnedCratesRED[_crateName] = nil @@ -4999,8 +4997,6 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) end function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) - ctld.logTrace("FG_ ctld.spawnCrateGroup _positions = %s", ctld.p(_positions)) - ctld.logTrace("_heli = %s", ctld.p(_heli)) ctld.logTrace("_positions = %s", ctld.p(_positions)) ctld.logTrace("_types = %s", ctld.p(_types)) From 9a69d4b52caf1b17649337219e37d089e3e8b2cf Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 3 Apr 2025 16:32:55 +0200 Subject: [PATCH 107/166] debug --- CTLD.lua | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 2387d31..c220d5d 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2039,17 +2039,16 @@ end -- *************************************************************** function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request - --ctld.logTrace("FG_ ctld.repackVehicleRequest") + ctld.logTrace("FG_ ctld.repackVehicleRequest._params = " .. ctld.p(_params)) ctld.repackRequestsStack[#ctld.repackRequestsStack + 1] = _params end -- *************************************************************** function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request - --ctld.logTrace("FG_ ctld.repackVehicle") if t == nil then t = timer.getTime() end - --ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) + ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) --ctld.logTrace("FG_ ctld.repackVehicle._params = %s", ctld.p(mist.utils.tableShow(_params))) for ii, v in ipairs(ctld.repackRequestsStack) do local repackableUnitName = v[1].repackableUnitName @@ -5846,10 +5845,7 @@ function ctld.addTransportF10MenuOptions(_unitName) _vehicleCommandsPath, ctld.unloadTroops, { _unitName, false }) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), _vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true }) - ctld.logDebug("PASS..1", ctld.p(_unitName)) - if ctld.vehicleCommandsPath[_unitName] == nil then - ctld.logDebug("PASS..2", ctld.p(_unitName)) ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) end @@ -6055,8 +6051,9 @@ function ctld.updateRepackMenu(_playerUnitName) missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} - --ctld.logTrace("FG_ RepackmenuPath = %s", ctld.p(RepackmenuPath)) - for _, _vehicle in pairs(repackableVehicles) do + ctld.logTrace("FG_ ctld.updateRepackMenu.repackableVehicles = %s", ctld.p(repackableVehicles)) + --for _, _vehicle in pairs(repackableVehicles) do + for i, _vehicle in ipairs(repackableVehicles) do table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, groupId = _groupId, subMenuPath = RepackmenuPath, @@ -6064,6 +6061,7 @@ function ctld.updateRepackMenu(_playerUnitName) menuArgsTable = { _vehicle, _playerUnitName } }) end + ctld.logTrace("FG_ menuEntries = %s", ctld.p(menuEntries)) ctld.buildPaginatedMenu(menuEntries) end end From 3a7ab508347a70f1852835c6bfa258c65d144420 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 3 Apr 2025 20:36:50 +0200 Subject: [PATCH 108/166] debug --- CTLD.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index c220d5d..94f3b12 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2065,8 +2065,8 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if repackableUnit:isExist() then -- calculate the heading of the spawns to be carried out local _playerHeading = mist.getHeading(PlayerTransportUnit) - local playerPoint = PlayerTransportUnit:getPoint() - local offset = 5 + local playerPoint = PlayerTransportUnit:getPoint() + local offset = 5 local randomHeading = ctld.RandomReal(_playerHeading - math.pi/4, _playerHeading + math.pi/4) for i = 1, v[1].cratesRequired or 1 do -- see to spawn the crate at random position heading the transport unnit @@ -6009,11 +6009,11 @@ end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - --ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) + ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do - --ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) + ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then --ctld.logTrace("FG_ boucle[%s].nextSubMenuPath = %s", i, ctld.p(nextSubMenuPath)) menu.subMenuPath = nextSubMenuPath From 951ee379037afaf7d067e8983b6d8aaa84af8a6c Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 3 Apr 2025 21:45:46 +0200 Subject: [PATCH 109/166] debbug --- CTLD.lua | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 94f3b12..daadcfe 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2052,33 +2052,26 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' --ctld.logTrace("FG_ ctld.repackVehicle._params = %s", ctld.p(mist.utils.tableShow(_params))) for ii, v in ipairs(ctld.repackRequestsStack) do local repackableUnitName = v[1].repackableUnitName - local crateWeight = v[1].weight local repackableUnit = Unit.getByName(repackableUnitName) - - local playerUnitName = v[2] - local PlayerTransportUnit = Unit.getByName(playerUnitName) - local playerCoa = PlayerTransportUnit:getCoalition() - local playerHeading = mist.getHeading(PlayerTransportUnit) - local refCountry = PlayerTransportUnit:getCountry() - + local crateWeight = v[1].weight + local playerUnitName = v[1].playerUnitName if repackableUnit then if repackableUnit:isExist() then + local PlayerTransportUnit = Unit.getByName(playerUnitName) + local playerCoa = PlayerTransportUnit:getCoalition() + local refCountry = PlayerTransportUnit:getCountry() -- calculate the heading of the spawns to be carried out - local _playerHeading = mist.getHeading(PlayerTransportUnit) + local playerHeading = mist.getHeading(PlayerTransportUnit) local playerPoint = PlayerTransportUnit:getPoint() local offset = 5 - local randomHeading = ctld.RandomReal(_playerHeading - math.pi/4, _playerHeading + math.pi/4) + local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) for i = 1, v[1].cratesRequired or 1 do -- see to spawn the crate at random position heading the transport unnit - local _unitId = ctld.getNextUnitId() - local _name = string.format("%s_%i", v[1].desc, _unitId) - local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) - if secureDistance == nil then - secureDistance = 7 - end - - local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit - local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) + local _unitId = ctld.getNextUnitId() + local _name = string.format("%s_%i", v[1].desc, _unitId) + local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 7 + local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit + local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) else @@ -2086,9 +2079,9 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end end - repackableUnit:destroy() -- destroy repacked unit + repackableUnit:destroy() -- destroy repacked unit end - ctld.repackRequestsStack[ii] = nil + ctld.repackRequestsStack[ii] = nil -- remove the request from the stacking table end ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units end @@ -6029,7 +6022,7 @@ function ctld.buildPaginatedMenu(_menuEntries) end menu.menuArgsTable.subMenuPath = menu.subMenuPath menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu - --ctld.logTrace("FG_ boucle[%s].menu.subMenuPath = %s", i, ctld.p(menu.subMenuPath)) + ctld.logTrace("FG_ boucle[%s] ctld.buildPaginatedMenu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) end end @@ -6054,11 +6047,12 @@ function ctld.updateRepackMenu(_playerUnitName) ctld.logTrace("FG_ ctld.updateRepackMenu.repackableVehicles = %s", ctld.p(repackableVehicles)) --for _, _vehicle in pairs(repackableVehicles) do for i, _vehicle in ipairs(repackableVehicles) do + _vehicle.playerUnitName = _playerUnitName table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, groupId = _groupId, subMenuPath = RepackmenuPath, menuFunction = ctld.repackVehicleRequest, - menuArgsTable = { _vehicle, _playerUnitName } + menuArgsTable = { _vehicle} }) end ctld.logTrace("FG_ menuEntries = %s", ctld.p(menuEntries)) From 7b7eb62bf03e91864f9537cb901fa675cff71a09 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 3 Apr 2025 23:48:00 +0200 Subject: [PATCH 110/166] debug --- CTLD.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index daadcfe..a901c1c 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6015,13 +6015,13 @@ function ctld.buildPaginatedMenu(_menuEntries) itemNbSubmenu = itemNbSubmenu + 1 if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached --ctld.logTrace("FG_ boucle[%s].menu.subMenuPath avant = %s", i, ctld.p(menu.subMenuPath)) - menu.subMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) - nextSubMenuPath = menu.subMenuPath + nextSubMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) + menu.subMenuPath = mist.utils.deepCopy(nextSubMenuPath) --ctld.logTrace("FG_ boucle[%s].menu.subMenuPath apres = %s", i, ctld.p(menu.subMenuPath)) itemNbSubmenu = 1 end - menu.menuArgsTable.subMenuPath = menu.subMenuPath - menu.menuArgsTable.subMenuLineIndex = menu.itemNbSubmenu + menu.menuArgsTable[1].subMenuPath = mist.utils.deepCopy(menu.subMenuPath) -- copy the table to avoid overwriting the same table in the next loop + menu.menuArgsTable[1].subMenuLineIndex = itemNbSubmenu ctld.logTrace("FG_ boucle[%s] ctld.buildPaginatedMenu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) end @@ -6052,7 +6052,7 @@ function ctld.updateRepackMenu(_playerUnitName) groupId = _groupId, subMenuPath = RepackmenuPath, menuFunction = ctld.repackVehicleRequest, - menuArgsTable = { _vehicle} + menuArgsTable = {_vehicle} }) end ctld.logTrace("FG_ menuEntries = %s", ctld.p(menuEntries)) From 3c261e8dc2981261dd6fc283e36525644443a276 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Fri, 4 Apr 2025 21:52:40 +0200 Subject: [PATCH 111/166] debug --- CTLD.lua | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index a901c1c..2e91ca1 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2045,16 +2045,21 @@ end -- *************************************************************** function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request + ctld.logTrace("FG_ XXXXXXXXXXXXXXXXXXXXXXXXXXX ctld.repackVehicle.ctld.repackRequestsStack XXXXXXXXXXXXXXXXXXXXXXXXXXX") + if t == nil then t = timer.getTime() end + if #ctld.repackRequestsStack ~= 0 then ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) + end --ctld.logTrace("FG_ ctld.repackVehicle._params = %s", ctld.p(mist.utils.tableShow(_params))) for ii, v in ipairs(ctld.repackRequestsStack) do - local repackableUnitName = v[1].repackableUnitName + ctld.logTrace("FG_ ctld.repackVehicle.v[%s] = %s", ii, ctld.p(v)) + local repackableUnitName = v.repackableUnitName local repackableUnit = Unit.getByName(repackableUnitName) - local crateWeight = v[1].weight - local playerUnitName = v[1].playerUnitName + local crateWeight = v.weight + local playerUnitName = v.playerUnitName if repackableUnit then if repackableUnit:isExist() then local PlayerTransportUnit = Unit.getByName(playerUnitName) @@ -2065,10 +2070,10 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local playerPoint = PlayerTransportUnit:getPoint() local offset = 5 local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) - for i = 1, v[1].cratesRequired or 1 do + for i = 1, v.cratesRequired or 1 do -- see to spawn the crate at random position heading the transport unnit local _unitId = ctld.getNextUnitId() - local _name = string.format("%s_%i", v[1].desc, _unitId) + local _name = string.format("%s_%i", v.desc, _unitId) local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 7 local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) @@ -6002,11 +6007,11 @@ end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) + --ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do - ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) + --ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then --ctld.logTrace("FG_ boucle[%s].nextSubMenuPath = %s", i, ctld.p(nextSubMenuPath)) menu.subMenuPath = nextSubMenuPath @@ -6020,9 +6025,9 @@ function ctld.buildPaginatedMenu(_menuEntries) --ctld.logTrace("FG_ boucle[%s].menu.subMenuPath apres = %s", i, ctld.p(menu.subMenuPath)) itemNbSubmenu = 1 end - menu.menuArgsTable[1].subMenuPath = mist.utils.deepCopy(menu.subMenuPath) -- copy the table to avoid overwriting the same table in the next loop - menu.menuArgsTable[1].subMenuLineIndex = itemNbSubmenu - ctld.logTrace("FG_ boucle[%s] ctld.buildPaginatedMenu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) + menu.menuArgsTable.subMenuPath = mist.utils.deepCopy(menu.subMenuPath) -- copy the table to avoid overwriting the same table in the next loop + menu.menuArgsTable.subMenuLineIndex = itemNbSubmenu + --ctld.logTrace("FG_ boucle[%s] ctld.buildPaginatedMenu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) end end @@ -6044,7 +6049,7 @@ function ctld.updateRepackMenu(_playerUnitName) missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} - ctld.logTrace("FG_ ctld.updateRepackMenu.repackableVehicles = %s", ctld.p(repackableVehicles)) + --ctld.logTrace("FG_ ctld.updateRepackMenu.repackableVehicles = %s", ctld.p(repackableVehicles)) --for _, _vehicle in pairs(repackableVehicles) do for i, _vehicle in ipairs(repackableVehicles) do _vehicle.playerUnitName = _playerUnitName @@ -6052,10 +6057,10 @@ function ctld.updateRepackMenu(_playerUnitName) groupId = _groupId, subMenuPath = RepackmenuPath, menuFunction = ctld.repackVehicleRequest, - menuArgsTable = {_vehicle} + menuArgsTable = mist.utils.deepCopy(_vehicle) }) end - ctld.logTrace("FG_ menuEntries = %s", ctld.p(menuEntries)) + --ctld.logTrace("FG_ menuEntries = %s", ctld.p(menuEntries)) ctld.buildPaginatedMenu(menuEntries) end end From 93260908c4a379f47accb0353904b7854ea2f1de Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 5 Apr 2025 19:39:42 +0200 Subject: [PATCH 112/166] debug --- CTLD.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 2e91ca1..543af56 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1030,8 +1030,8 @@ ctld.spawnableCrates = { --- BLUE { weight = 1000.01, desc = ctld.i18n_translate("Humvee - MG"), unit = "M1043 HMMWV Armament", side = 2 }, --careful with the names as the script matches the desc to JTAC types - { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 1 }, - { multiple = { 1000.02}, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, + { weight = 1000.02, desc = ctld.i18n_translate("Humvee - TOW"), unit = "M1045 HMMWV TOW", side = 2, cratesRequired = 2 }, + { multiple = { 1000.02,1000.02 }, desc = ctld.i18n_translate("Humvee - TOW - All crates"), side = 2 }, { weight = 1000.03, desc = ctld.i18n_translate("Light Tank - MRAP"), unit = "MaxxPro_MRAP", side = 2, cratesRequired = 2 }, { multiple = { 1000.03, 1000.03 }, desc = ctld.i18n_translate("Light Tank - MRAP - All crates"), side = 2 }, { weight = 1000.04, desc = ctld.i18n_translate("Med Tank - LAV-25"), unit = "LAV-25", side = 2, cratesRequired = 3 }, @@ -3895,15 +3895,14 @@ function ctld.unpackCrates(_arguments) if _heli ~= nil and ctld.inAir(_heli) == false then local _crates = ctld.getCratesAndDistance(_heli) local _crate = ctld.getClosestCrate(_heli, _crates) - + ctld.logTrace("FG_ ctld.unpackCrates._crate = %s", ctld.p(_crate)) + if ctld.inLogisticsZone(_heli) == true or ctld.farEnoughFromLogisticZone(_heli) == false then ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You can't unpack that here! Take it to where it's needed!"), 20) return end - - if _crate ~= nil and _crate.dist < 750 and (_crate.details.unit == "FOB" or _crate.details.unit == "FOB-SMALL") then ctld.unpackFOBCrates(_crates, _heli) @@ -5512,7 +5511,7 @@ end -- are we near friendly logistics zone function ctld.inLogisticsZone(_heli) - ctld.logDebug("ctld.inLogisticsZone(), _heli = %s", ctld.p(_heli)) + --ctld.logDebug("ctld.inLogisticsZone(), _heli = %s", ctld.p(_heli)) if ctld.inAir(_heli) then return false @@ -6028,7 +6027,7 @@ function ctld.buildPaginatedMenu(_menuEntries) menu.menuArgsTable.subMenuPath = mist.utils.deepCopy(menu.subMenuPath) -- copy the table to avoid overwriting the same table in the next loop menu.menuArgsTable.subMenuLineIndex = itemNbSubmenu --ctld.logTrace("FG_ boucle[%s] ctld.buildPaginatedMenu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) - missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, menu.menuArgsTable) + missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, mist.utils.deepCopy(menu.menuArgsTable)) end end From da80f9ea064665e4b0885547841ee0cb0e60b511 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 6 Apr 2025 11:09:38 +0200 Subject: [PATCH 113/166] commit --- CTLD.lua | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 543af56..16fe24d 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2045,15 +2045,14 @@ end -- *************************************************************** function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request - ctld.logTrace("FG_ XXXXXXXXXXXXXXXXXXXXXXXXXXX ctld.repackVehicle.ctld.repackRequestsStack XXXXXXXXXXXXXXXXXXXXXXXXXXX") + --ctld.logTrace("FG_ XXXXXXXXXXXXXXXXXXXXXXXXXXX ctld.repackVehicle.ctld.repackRequestsStack XXXXXXXXXXXXXXXXXXXXXXXXXXX") if t == nil then t = timer.getTime() end - if #ctld.repackRequestsStack ~= 0 then - ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) - end - --ctld.logTrace("FG_ ctld.repackVehicle._params = %s", ctld.p(mist.utils.tableShow(_params))) + -- if #ctld.repackRequestsStack ~= 0 then + -- ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) + -- end for ii, v in ipairs(ctld.repackRequestsStack) do ctld.logTrace("FG_ ctld.repackVehicle.v[%s] = %s", ii, ctld.p(v)) local repackableUnitName = v.repackableUnitName @@ -2623,12 +2622,12 @@ function ctld.getPointAt6Oclock(_unit, _offset) end function ctld.getPointAtDirection(_unit, _offset, _directionInRadian) - ctld.logTrace("_offset = %s", ctld.p(_offset)) + --ctld.logTrace("_offset = %s", ctld.p(_offset)) local _SecureDistanceFromUnit = ctld.getSecureDistanceFromUnit(_unit:getName()) local _randomOffsetX = math.random(_SecureDistanceFromUnit, ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing local _randomOffsetZ = math.random(0, ctld.randomCrateSpacing) - ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) - ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) + --ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) + --ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) local _position = _unit:getPosition() local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian local _xOffset = math.cos(_angle) * (_offset + _randomOffsetX) @@ -5666,7 +5665,7 @@ function ctld.unitDynamicCargoCapable(_unit) local result = cache[_type] if result == nil then result = false - ctld.logDebug("ctld.unitDynamicCargoCapable(_type=[%s])", ctld.p(_type)) + --ctld.logDebug("ctld.unitDynamicCargoCapable(_type=[%s])", ctld.p(_type)) for _, _name in ipairs(ctld.dynamicCargoUnits) do local _nameLower = string.lower(_name) if string.find(_type, _nameLower, 1, true) then --string.match does not work with patterns containing '-' as it is a magic character @@ -5675,7 +5674,6 @@ function ctld.unitDynamicCargoCapable(_unit) end end cache[_type] = result - ctld.logDebug("result=[%s]", ctld.p(result)) end return result end @@ -8120,7 +8118,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 5) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end end, nil, timer.getTime() + 1) From 4d704a489e3199f10b76a2c55117a24aa56c723c Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 6 Apr 2025 11:13:50 +0200 Subject: [PATCH 114/166] commit --- CTLD.lua | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 16fe24d..b2a0d0c 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -4992,10 +4992,10 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) end function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) - ctld.logTrace("_heli = %s", ctld.p(_heli)) - ctld.logTrace("_positions = %s", ctld.p(_positions)) - ctld.logTrace("_types = %s", ctld.p(_types)) - ctld.logTrace("_hdgs = %s", ctld.p(_hdgs)) + -- ctld.logTrace("_heli = %s", ctld.p(_heli)) + -- ctld.logTrace("_positions = %s", ctld.p(_positions)) + -- ctld.logTrace("_types = %s", ctld.p(_types)) + -- ctld.logTrace("_hdgs = %s", ctld.p(_hdgs)) local _id = ctld.getNextGroupId() local _groupName = _types[1] .. " #" .. _id @@ -5022,7 +5022,7 @@ function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) 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) } - ctld.logTrace("Group._details = %s", ctld.p(_details)) + --ctld.logTrace("Group._details = %s", ctld.p(_details)) if _hdgs and _hdgs[_i] then _hdg = _hdgs[_i] end @@ -5142,7 +5142,6 @@ function ctld.spawnCrateGroup(_heli, _positions, _types, _hdgs) end _group.country = _heli:getCountry() - ctld.logTrace("mist.dynAdd(") local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) return _spawnedGroup end @@ -5796,8 +5795,8 @@ function ctld.addTransportF10MenuOptions(_unitName) local _unitTypename = _unit:getTypeName() local _groupId = ctld.getGroupId(_unit) if _groupId then - ctld.logTrace("_groupId = %s", ctld.p(_groupId)) - ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo[tostring(_groupId)])) + -- ctld.logTrace("_groupId = %s", ctld.p(_groupId)) + -- ctld.logTrace("ctld.addedTo = %s", ctld.p(ctld.addedTo[tostring(_groupId)])) if ctld.addedTo[tostring(_groupId)] == nil then ctld.logTrace("adding CTLD menu for _groupId = %s", ctld.p(_groupId)) local _rootPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("CTLD")) From 1fce52088d4ec3888d74af0facb18b270474ff51 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 6 Apr 2025 11:15:16 +0200 Subject: [PATCH 115/166] commit --- CTLD.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index b2a0d0c..62af979 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2039,7 +2039,7 @@ end -- *************************************************************** function ctld.repackVehicleRequest(_params) -- update rrs table 'repackRequestsStack' with the request - ctld.logTrace("FG_ ctld.repackVehicleRequest._params = " .. ctld.p(_params)) + --ctld.logTrace("FG_ ctld.repackVehicleRequest._params = " .. ctld.p(_params)) ctld.repackRequestsStack[#ctld.repackRequestsStack + 1] = _params end @@ -2054,7 +2054,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' -- ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) -- end for ii, v in ipairs(ctld.repackRequestsStack) do - ctld.logTrace("FG_ ctld.repackVehicle.v[%s] = %s", ii, ctld.p(v)) + --ctld.logTrace("FG_ ctld.repackVehicle.v[%s] = %s", ii, ctld.p(v)) local repackableUnitName = v.repackableUnitName local repackableUnit = Unit.getByName(repackableUnitName) local crateWeight = v.weight From 574217d93157c90e5285f125f6bb8b40d51ea762 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 17:02:08 +0200 Subject: [PATCH 116/166] commit --- CTLD.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 62af979..1035a7f 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2583,7 +2583,7 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) if ctld.unitDynamicCargoCapable(_heli) then _model_type = "dynamic" - _point = ctld.getPointAt6Oclock(_heli, 15) + _point = ctld.getPointAt6Oclock(_heli, 30) _position = "6" end @@ -4950,7 +4950,10 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) --- check crate count if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then - local _point = _nearestCrate.crateUnit:getPoint() + --local _point = _nearestCrate.crateUnit:getPoint() + local _point = _heli:getPoint() + local secureDistanceFromUnit = ctld.getSecureDistanceFromUnit(_heli:getName()) + _point.x = _point.x + secureDistanceFromUnit local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true) -- destroy crates From 540a31675488c50bcb3c722fcae2140f37fe2b55 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 17:12:44 +0200 Subject: [PATCH 117/166] commit --- CTLD.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 1035a7f..5b0733a 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2077,7 +2077,8 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) 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) + ctld.spawnCrateStatic(refCountry, _unitId, _point, _name, crateWeight, playerCoa, playerHeading, nil) else ctld.spawnCrateStatic(refCountry, _unitId, _point, _name, crateWeight, playerCoa, playerHeading, "dynamic") end From bbde4ae62bd41ed692af23059a7a609c35aee61c Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 17:16:28 +0200 Subject: [PATCH 118/166] comiit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 5b0733a..7b38a83 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2078,7 +2078,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then --ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) - ctld.spawnCrateStatic(refCountry, _unitId, _point, _name, crateWeight, playerCoa, playerHeading, nil) + ctld.spawnCrateStatic(refCountry, _unitId, _point, _name, crateWeight, playerCoa, playerHeading-math.pi, nil) else ctld.spawnCrateStatic(refCountry, _unitId, _point, _name, crateWeight, playerCoa, playerHeading, "dynamic") end From b3470250e4bcafca3e8793e4078072c0a19ee6e7 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 17:28:54 +0200 Subject: [PATCH 119/166] commit --- CTLD.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 7b38a83..05445cd 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -808,7 +808,7 @@ ctld.logisticUnits = { ctld.vehicleTransportEnabled = { "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR "Hercules", - "UH-1H", + --"UH-1H", "Mi-8MT", --"CH-47Fbl1", } @@ -819,6 +819,7 @@ ctld.vehicleTransportEnabled = { -- We will also use this to make modifications to the menu and other checks and messages ctld.dynamicCargoUnits = { "CH-47Fbl1", + "UH-1H", } -- ************** Maximum Units SETUP for UNITS ****************** @@ -2073,14 +2074,14 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' -- see to spawn the crate at random position heading the transport unnit local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v.desc, _unitId) - local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 7 + local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 10 local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit - local _point = ctld.getPointAt6Oclock(PlayerTransportUnit, 15) if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then - --ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) - ctld.spawnCrateStatic(refCountry, _unitId, _point, _name, crateWeight, playerCoa, playerHeading-math.pi, nil) + local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit + ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) else - ctld.spawnCrateStatic(refCountry, _unitId, _point, _name, crateWeight, playerCoa, playerHeading, "dynamic") + local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading+math.pi) -- 7 meters from the transport unit + ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, "dynamic") end end From 82a330e61ad02ec335f8d17e683c6a94f3cb6581 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 19:04:35 +0200 Subject: [PATCH 120/166] commit --- CTLD.lua | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 05445cd..447aa3e 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1925,27 +1925,18 @@ function ctld.spawnCrateAtPoint(_side, _weight, _point, _hdg) ctld.spawnCrateStatic(_country, _unitId, _point, _name, _crateType.weight, _side, _hdg) end --- *************************************************************** -function ctld.getUnitDimensions(_unitName) -- return unit dimùension (widht,longer,hight) - if Unit.getByName(_unitName) then - local dimensions = {} - local unitBoundingBox = Unit.getByName(_unitName):getDesc().box - dimensions.widht = unitBoundingBox.max.x - unitBoundingBox.min.x - dimensions.longer = unitBoundingBox.max.z - unitBoundingBox.min.z - dimensions.hight = unitBoundingBox.max.y - unitBoundingBox.min.y - return dimensions - end -end - -- *************************************************************** function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName + local rotorDiameter = 19 -- meters -- õk for UH & CH47 if Unit.getByName(_unitName) then - local dim = ctld.getUnitDimensions(_unitName) - - -- distanceFromCenterToCorner = 1/2√(l² + w² + h²) - local squaresSum = dim.longer^2 + dim.widht^2 + dim.hight^2 -- sum of squares - local distanceFromCenterToCorner = math.sqrt(squaresSum) / 2 -- Calculating distance (half square root) - return distanceFromCenterToCorner + local unitUserBox = Unit.getByName(_unitName):getDesc().box + local SecureDistanceFromUnit = 0 + if math.abs(unitUserBox.max.x) >= math.abs(unitUserBox.min.x) then + SecureDistanceFromUnit = math.abs(unitUserBox.max.x) + rotorDiameter + else + SecureDistanceFromUnit = math.abs(unitUserBox.min.x) + rotorDiameter + end + return SecureDistanceFromUnit end return nil end @@ -2080,7 +2071,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) else - local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading+math.pi) -- 7 meters from the transport unit + local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, "dynamic") end end From 026099bd12bfb3f0ab16db0ca7dbcb22594edf95 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 19:19:45 +0200 Subject: [PATCH 121/166] commit --- CTLD.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 447aa3e..78b7af4 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2060,19 +2060,19 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local playerHeading = mist.getHeading(PlayerTransportUnit) local playerPoint = PlayerTransportUnit:getPoint() local offset = 5 - local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) for i = 1, v.cratesRequired or 1 do -- see to spawn the crate at random position heading the transport unnit local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v.desc, _unitId) local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 10 - local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then + local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) - else + else + local randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit - ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, "dynamic") + ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading+math.pi, "dynamic") end end @@ -8113,7 +8113,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 5) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end end, nil, timer.getTime() + 1) From 7db8e2d7729186fd1cbab5b9758987846b753e58 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 19:19:57 +0200 Subject: [PATCH 122/166] commit --- CTLD.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 78b7af4..773e379 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2060,19 +2060,22 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local playerHeading = mist.getHeading(PlayerTransportUnit) local playerPoint = PlayerTransportUnit:getPoint() local offset = 5 + if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then + local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) + else + local randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) + end for i = 1, v.cratesRequired or 1 do -- see to spawn the crate at random position heading the transport unnit local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v.desc, _unitId) local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 10 if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then - local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) else - local randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit - ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading+math.pi, "dynamic") + ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, "dynamic") end end From 99f2c7aec0fc0145cb6a1fd9238c484cd6f1c104 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 19:26:06 +0200 Subject: [PATCH 123/166] commit --- CTLD.lua | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 773e379..c333d2e 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2060,10 +2060,9 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local playerHeading = mist.getHeading(PlayerTransportUnit) local playerPoint = PlayerTransportUnit:getPoint() local offset = 5 - if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then - local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) - else - local randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) + local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) + if ctld.unitDynamicCargoCapable(PlayerTransportUnit) ~= false then + randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) end for i = 1, v.cratesRequired or 1 do -- see to spawn the crate at random position heading the transport unnit From a468faefecb574927f6d1a311b8d9a973c7aa92c Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 21:53:15 +0200 Subject: [PATCH 124/166] commit --- CTLD.lua | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index c333d2e..d3f01e8 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2069,11 +2069,11 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v.desc, _unitId) local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 10 + local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit + if ctld.unitDynamicCargoCapable(PlayerTransportUnit) == false then - local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, nil) else - local relativePoint = ctld.getRelativePoint(playerPoint, secureDistance + (i * offset), randomHeading) -- 7 meters from the transport unit ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, "dynamic") end end @@ -2616,18 +2616,42 @@ function ctld.getPointAt6Oclock(_unit, _offset) return ctld.getPointAtDirection(_unit, _offset, math.pi) end +function ctld.getPointInFrontSector(_unit, _offset) + if _unit then + local playerHeading = mist.getHeading(_unit) + local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) + if _offset == nil then + _offset = 30 + end + return ctld.getPointAtDirection(_unit, _offset, randomHeading) + end +end + +function ctld.getPointInRearSector(_unit, _offset) + if _unit then + local playerHeading = mist.getHeading(_unit) + local randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) + if _offset == nil then + _offset = 30 + end + return ctld.getPointAtDirection(_unit, _offset, randomHeading) + end +end + function ctld.getPointAtDirection(_unit, _offset, _directionInRadian) + if _offset == nil then + _offset = ctld.getSecureDistanceFromUnit(_unit:getName()) + end --ctld.logTrace("_offset = %s", ctld.p(_offset)) - local _SecureDistanceFromUnit = ctld.getSecureDistanceFromUnit(_unit:getName()) - local _randomOffsetX = math.random(_SecureDistanceFromUnit, ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing + local _randomOffsetX = math.random(0, ctld.randomCrateSpacing * 2) - ctld.randomCrateSpacing local _randomOffsetZ = math.random(0, ctld.randomCrateSpacing) --ctld.logTrace("_randomOffsetX = %s", ctld.p(_randomOffsetX)) --ctld.logTrace("_randomOffsetZ = %s", ctld.p(_randomOffsetZ)) local _position = _unit:getPosition() - local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian - local _xOffset = math.cos(_angle) * (_offset + _randomOffsetX) - local _zOffset = math.sin(_angle) * (_offset + _randomOffsetZ) - local _point = _unit:getPoint() + local _angle = math.atan(_position.x.z, _position.x.x) + _directionInRadian + local _xOffset = math.cos(_angle) * (_offset + _randomOffsetX) + local _zOffset = math.sin(_angle) * (_offset + _randomOffsetZ) + local _point = _unit:getPoint() return { x = _point.x + _xOffset, z = _point.z + _zOffset, y = _point.y } end @@ -4948,6 +4972,7 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) --local _point = _nearestCrate.crateUnit:getPoint() local _point = _heli:getPoint() local secureDistanceFromUnit = ctld.getSecureDistanceFromUnit(_heli:getName()) + _point.x = _point.x + secureDistanceFromUnit local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true) @@ -6001,14 +6026,14 @@ end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - --ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) + ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do --ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then --ctld.logTrace("FG_ boucle[%s].nextSubMenuPath = %s", i, ctld.p(nextSubMenuPath)) - menu.subMenuPath = nextSubMenuPath + menu.subMenuPath = mist.utils.deepCopy(nextSubMenuPath) end -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 From f71e7f3213740907ba80de9499043e910e8ccadd Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 6 Apr 2025 22:31:37 +0200 Subject: [PATCH 125/166] commit --- CTLD.lua | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index d3f01e8..07a5b6a 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -808,7 +808,7 @@ ctld.logisticUnits = { ctld.vehicleTransportEnabled = { "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR "Hercules", - --"UH-1H", + "UH-1H", "Mi-8MT", --"CH-47Fbl1", } @@ -819,7 +819,7 @@ ctld.vehicleTransportEnabled = { -- We will also use this to make modifications to the menu and other checks and messages ctld.dynamicCargoUnits = { "CH-47Fbl1", - "UH-1H", + --"UH-1H", } -- ************** Maximum Units SETUP for UNITS ****************** @@ -2659,7 +2659,7 @@ function ctld.getRelativePoint(_refPointXZTable, _distance, _angle_radians) -- local relativePoint = {} relativePoint.x = _refPointXZTable.x + _distance * math.cos(_angle_radians) if _refPointXZTable.z == nil then - relativePoint.y = _refPointXZTable.y + _distance * math.sin(_angle_radians) + relativePoint.y = _refPointXZTable.y + _distance * math.sin(_angle_radians) else relativePoint.z = _refPointXZTable.z + _distance * math.sin(_angle_radians) end @@ -3954,7 +3954,11 @@ function ctld.unpackCrates(_arguments) else ctld.logTrace("single crate = %s", ctld.p(_arguments)) -- single crate - local _cratePoint = _crate.crateUnit:getPoint() + --local _cratePoint = _crate.crateUnit:getPoint() + local _point = ctld.getPointInFrontSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName())) + if ctld.unitDynamicCargoCapable(_heli) == true then + _point = ctld.getPointInRearSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName())) + end local _crateName = _crate.crateUnit:getName() local _crateHdg = mist.getHeading(_crate.crateUnit, true) @@ -3963,7 +3967,7 @@ function ctld.unpackCrates(_arguments) _crate.crateUnit:destroy() -- end ctld.logTrace("_crate = %s", ctld.p(_crate)) - local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }, { _crateHdg }) + local _spawnedGroups = ctld.spawnCrateGroup(_heli, { _point }, { _crate.details.unit }, { _crateHdg }) ctld.logTrace("_spawnedGroups = %s", ctld.p(_spawnedGroups)) if _heli:getCoalition() == 1 then @@ -4959,7 +4963,6 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) if _nearbyCrate.dist < 300 then if _nearbyCrate.details.unit == _nearestCrate.details.unit then table.insert(_nearbyMultiCrates, _nearbyCrate) - if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then break end @@ -4970,10 +4973,14 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) --- check crate count if #_nearbyMultiCrates == _nearestCrate.details.cratesRequired then --local _point = _nearestCrate.crateUnit:getPoint() - local _point = _heli:getPoint() - local secureDistanceFromUnit = ctld.getSecureDistanceFromUnit(_heli:getName()) - - _point.x = _point.x + secureDistanceFromUnit + --local _point = _heli:getPoint() + --local secureDistanceFromUnit = ctld.getSecureDistanceFromUnit(_heli:getName()) + --_point.x = _point.x + secureDistanceFromUnit + local _point = ctld.getPointInFrontSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName())) + if ctld.unitDynamicCargoCapable(_heli) == true then + _point = ctld.getPointInRearSector(_heli, ctld.getSecureDistanceFromUnit(_heli:getName())) + end + local _crateHdg = mist.getHeading(_nearestCrate.crateUnit, true) -- destroy crates @@ -6026,7 +6033,7 @@ end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) + --ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) local nextSubMenuPath = "" local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do From a35e023752a1b24b57879788ecb2b037607e78df Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 7 Apr 2025 23:46:37 +0200 Subject: [PATCH 126/166] #149 Flying JTAC orbit on target --- CTLD.lua | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 137 insertions(+), 5 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 62af979..cfc51a7 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6415,9 +6415,7 @@ function ctld.JTACStart(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _rad end function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio) - ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), - ctld.p(_laserCode))) - + ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), ctld.p(_laserCode))) local _radio = _radio if not _radio then _radio = {} @@ -7668,6 +7666,138 @@ function ctld.getPositionString(_unit) _mgrsString .. " - ALTI: " .. mist.utils.round(_TargetAlti, 0) .. " m / " .. mist.utils.round(_TargetAlti / 0.3048, 0) .. " ft" end +--********************************************************************** +-- Automaticaly put in orbit over his target a flying JTAC +-- +-- Objective : This script put in orbit each flying JTAC over his detected target +-- Associated with CTLD/JTAC function, you can assign a fly route to the JTAC (a drone for example), +-- 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 +-- Use : In mission editor: +-- 0> Set ctld.enableAutoOrbitingFlyinfJtacOnTarget = true +-- 1> Load MIST + CTLD +-- 2> Create a TRIGGER (once) at Time sup à 6, and a ACTION.EXECUTE SCRIPT : +-- ctld.JTACAutoLase("gdrone1", 1688,false) -- défine group "gdrone1" as a JTAC +------------------------------------------------------------------------------------ +ctld.OrbitInUse = {} -- for each Orbit group in use, indicates the time of the run +ctld.enableAutoOrbitingFlyinfJtacOnTarget = true -- if true activate the AutoOrbitingFlyinfJtacOnTarget function for all flying JTACS +------------------------------------------------------------------------------------ +-- Automatic JTAC orbit on target detect +function ctld.TreatOrbitJTAC(params, t) + if t == nil then t = timer.getTime() end + + for k,v in pairs(ctld.jtacUnits) do -- vérify state of each active JTAC + if ctld.isFlyingJtac(k) then + if ctld.jtacCurrentTargets[k] ~= nil then -- if detected target by JTAC + if ctld.InOrbitList(k) == false then -- JTAC have a target but isn't in orbit => put it in orbit + --ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, 2000, 100) -- do orbit JTAC + local droneAlti = Unit.getByName(k):getPoint().y + ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, droneAlti, 100) -- do orbit JTAC + ctld.OrbitInUse[k] = timer.getTime() -- memorise time of start new orbiting + else -- JTAC already is orbiting => update coord for following the target mouvements + if timer.getTime() > (ctld.OrbitInUse[k] + 60) then -- each 60" update orbit coord + ctld.AjustRoute(k, ctld.NearWP(ctld.jtacCurrentTargets[k].name, k)) -- ajust JTAC route for the orbit follow the target + end + end + elseif ctld.jtacCurrentTargets[k] == nil then -- if JTAC hav no target + if ctld.InOrbitList(k) == true then -- JTAC orbiting, without target => stop orbit + Group.getByName(k):getController():resetTask() -- stop orbit JTAC + ctld.OrbitInUse[k] = nil -- Reset orbit + end + end + end + end + return t + 3 --reschedule in 3" + +end +------------------------------------------------------------------------------------ +-- Make orbit the group "_grpName", on target "_unitTargetName". _alti in meters, speed in km/h +function ctld.StartOrbitGroup(_grpName, _unitTargetName, _alti, _speed) + if (Unit.getByName(_unitTargetName) ~= nil) and (Group.getByName(_grpName) ~= nil) then -- si target unit and JTAC group exist + local orbit = { + id = 'Orbit', + params = { + pattern = 'Circle', + point = mist.utils.makeVec2(mist.getAvgPos(mist.makeUnitTable({_unitTargetName}))), + speed = _speed, + altitude = _alti, + } + } + Group.getByName(_grpName):getController():pushTask(orbit) + ctld.OrbitInUse[_grpName] = true + end +end +------------------------------------------------------------------------------------------- +-- test if one unitName already is targeted by a JTAC +function ctld.InOrbitList(_grpName) + for k, v in pairs(ctld.OrbitInUse) do -- for each orbit in use + if k == _grpName then + return true + end + end + return false +end +------------------------------------------------------------------------------------------- +-- return the WayPoint number (on the JTAC route) the most near from the target +function ctld.NearWP(_unitTargetName, _grpName) + local WP = 0 + local memoDist = nil -- Lower distance checked + local JTACRoute = mist.getGroupRoute (_grpName, true) -- get the initial editor route of the current group + + if Group.getByName(_grpName):getUnit(1) ~= nil and Unit.getByName(_unitTargetName) ~= nil then --JTAC et unit must exist + for i=1, #JTACRoute do + local ptJTAC = {x = JTACRoute[i].x, y = JTACRoute[i].y} + local ptTarget = mist.utils.makeVec2(Unit.getByName(_unitTargetName):getPoint()) + local dist = mist.utils.get2DDist(ptJTAC, ptTarget) -- distance between 2 points + if memoDist == nil then + memoDist = dist + WP = i + elseif dist < memoDist then + memoDist = dist + WP = i + end + end + end + return WP +end +---------------------------------------------------------------------------- +-- Modify the route deleting all the WP before "firstWP" param, for aligne the orbit on the nearest WP of the target +function ctld.AjustRoute(_grpName, firstWP) + local JTACRoute = mist.getGroupRoute (_grpName, true) -- get the initial editor route of the current group + for i=0, #JTACRoute-1 do + if firstWP+i <= #JTACRoute then + JTACRoute[i+1] = JTACRoute[firstWP+i] -- replace keeped WP at start of new route + else + JTACRoute[i+1] = nil -- delete useless WP + end + end + + local Mission = {} + Mission = { + id = 'Mission', + params = { + route = {points = JTACRoute + } + } + } + -- unactive orbit mode if it's on + if ctld.InOrbitList(_grpName) == true then -- if JTAC orbiting => stop it + Group.getByName(_grpName):getController():resetTask() -- stop JTAC orbiting + ctld.OrbitInUse[_grpName] = nil + end + + Group.getByName(_grpName):getController():setTask(Mission) -- submit the new route + return Mission +end +---------------------------------------------------------------------------- +function ctld.isFlyingJtac(_jtacUnitName) + if Unit.getByName(_jtacUnitName) then + if Unit.getByName(_jtacUnitName):getCategoryEx() == 0 then -- it's an airplane JTAC + return true + end + end + return false +end --********************************************************************** -- RECOGNITION SUPPORT FUNCTIONS @@ -8102,9 +8232,8 @@ function ctld.initialize() end end - + --************************************************************************************************* -- Scheduled functions (run cyclically) -- but hold execution for a second so we can override parts - timer.scheduleFunction(ctld.checkAIStatus, nil, timer.getTime() + 1) timer.scheduleFunction(ctld.checkTransportStatus, nil, timer.getTime() + 5) @@ -8120,6 +8249,9 @@ function ctld.initialize() timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 5) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end + if ctld.enableAutoOrbitingFlyinfJtacOnTarget then + timer.scheduleFunction(ctld.TreatOrbitJTAC, {}, timer.getTime()+3) + end end, nil, timer.getTime() + 1) --event handler for deaths From cc8b9168701bae51e86a5739c6502e14dd3e9102 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Wed, 9 Apr 2025 00:54:01 +0200 Subject: [PATCH 127/166] debug --- CTLD.lua | 71 +++++++++++++++++++++++--------------------------------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 927476c..3b748d7 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7704,13 +7704,13 @@ end -- 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 -- Use : In mission editor: --- 0> Set ctld.enableAutoOrbitingFlyinfJtacOnTarget = true +-- 0> Set ctld.enableAutoOrbitingFlyingJtacOnTarget = true -- 1> Load MIST + CTLD -- 2> Create a TRIGGER (once) at Time sup à 6, and a ACTION.EXECUTE SCRIPT : -- ctld.JTACAutoLase("gdrone1", 1688,false) -- défine group "gdrone1" as a JTAC ------------------------------------------------------------------------------------ ctld.OrbitInUse = {} -- for each Orbit group in use, indicates the time of the run -ctld.enableAutoOrbitingFlyinfJtacOnTarget = true -- if true activate the AutoOrbitingFlyinfJtacOnTarget function for all flying JTACS +ctld.enableAutoOrbitingFlyingJtacOnTarget = true -- if true activate the AutoOrbitingFlyinfJtacOnTarget function for all flying JTACS ------------------------------------------------------------------------------------ -- Automatic JTAC orbit on target detect function ctld.TreatOrbitJTAC(params, t) @@ -7719,40 +7719,41 @@ function ctld.TreatOrbitJTAC(params, t) for k,v in pairs(ctld.jtacUnits) do -- vérify state of each active JTAC if ctld.isFlyingJtac(k) then if ctld.jtacCurrentTargets[k] ~= nil then -- if detected target by JTAC - if ctld.InOrbitList(k) == false then -- JTAC have a target but isn't in orbit => put it in orbit - --ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, 2000, 100) -- do orbit JTAC - local droneAlti = Unit.getByName(k):getPoint().y - ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, droneAlti, 100) -- do orbit JTAC - ctld.OrbitInUse[k] = timer.getTime() -- memorise time of start new orbiting - else -- JTAC already is orbiting => update coord for following the target mouvements - if timer.getTime() > (ctld.OrbitInUse[k] + 60) then -- each 60" update orbit coord - ctld.AjustRoute(k, ctld.NearWP(ctld.jtacCurrentTargets[k].name, k)) -- ajust JTAC route for the orbit follow the target + local droneAlti = Unit.getByName(k):getPoint().y + if ctld.InOrbitList(k) == false then -- JTAC lase a target but isn't in orbit => start orbiting + ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, droneAlti, 100) -- do orbit JTAC + ctld.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run + else -- JTAC already is orbiting => update coord for following the target mouvements + 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.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run end end - elseif ctld.jtacCurrentTargets[k] == nil then -- if JTAC hav no target - if ctld.InOrbitList(k) == true then -- JTAC orbiting, without target => stop orbit - Group.getByName(k):getController():resetTask() -- stop orbit JTAC - ctld.OrbitInUse[k] = nil -- Reset orbit - end + else -- if JTAC have no target + -- if ctld.InOrbitList(k) == true then -- JTAC orbiting, without target => stop orbit + -- Group.getByName(k):getController():resetTask() -- stop orbiting JTAC + -- --Group.getByName(k):getController():popTask() -- stop orbiting JTAC + -- ctld.OrbitInUse[k] = nil -- Reset orbit + -- end + Group.getByName(k):getController():popTask() -- stop orbiting JTAC + ctld.backToRoute(k) -- return to the initial route of the JTAC group end end end return t + 3 --reschedule in 3" - end ------------------------------------------------------------------------------------ -- Make orbit the group "_grpName", on target "_unitTargetName". _alti in meters, speed in km/h -function ctld.StartOrbitGroup(_grpName, _unitTargetName, _alti, _speed) +function ctld.StartOrbitGroup(_grpName, _unitTargetName, _alti, _speed) if (Unit.getByName(_unitTargetName) ~= nil) and (Group.getByName(_grpName) ~= nil) then -- si target unit and JTAC group exist local orbit = { - id = 'Orbit', - params = { - pattern = 'Circle', - point = mist.utils.makeVec2(mist.getAvgPos(mist.makeUnitTable({_unitTargetName}))), - speed = _speed, - altitude = _alti, - } - } + id = 'Orbit', + params = {pattern = 'Circle', + point = mist.utils.makeVec2(mist.getAvgPos(mist.makeUnitTable({_unitTargetName}))), + speed = _speed, + altitude = _alti + } + } Group.getByName(_grpName):getController():pushTask(orbit) ctld.OrbitInUse[_grpName] = true end @@ -7792,24 +7793,10 @@ function ctld.NearWP(_unitTargetName, _grpName) end ---------------------------------------------------------------------------- -- Modify the route deleting all the WP before "firstWP" param, for aligne the orbit on the nearest WP of the target -function ctld.AjustRoute(_grpName, firstWP) +function ctld.backToRoute(_grpName) local JTACRoute = mist.getGroupRoute (_grpName, true) -- get the initial editor route of the current group - for i=0, #JTACRoute-1 do - if firstWP+i <= #JTACRoute then - JTACRoute[i+1] = JTACRoute[firstWP+i] -- replace keeped WP at start of new route - else - JTACRoute[i+1] = nil -- delete useless WP - end - end - local Mission = {} - Mission = { - id = 'Mission', - params = { - route = {points = JTACRoute - } - } - } + Mission = { id = 'Mission', params = {route = {points = JTACRoute}}} -- unactive orbit mode if it's on if ctld.InOrbitList(_grpName) == true then -- if JTAC orbiting => stop it Group.getByName(_grpName):getController():resetTask() -- stop JTAC orbiting @@ -8279,7 +8266,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end - if ctld.enableAutoOrbitingFlyinfJtacOnTarget then + if ctld.enableAutoOrbitingFlyingJtacOnTarget then timer.scheduleFunction(ctld.TreatOrbitJTAC, {}, timer.getTime()+3) end end, nil, timer.getTime() + 1) From 1e471df26455cdb609e3588debafe9494c98ca03 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 13 Apr 2025 22:07:53 +0200 Subject: [PATCH 128/166] debug #159 jtac autoorbiting --- CTLD.lua | 91 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 3b748d7..3bb200e 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7709,6 +7709,7 @@ end -- 2> Create a TRIGGER (once) at Time sup à 6, and a ACTION.EXECUTE SCRIPT : -- ctld.JTACAutoLase("gdrone1", 1688,false) -- défine group "gdrone1" as a JTAC ------------------------------------------------------------------------------------ +ctld.JTACInRoute = {} -- for each JTAC in route, indicates the time of the run ctld.OrbitInUse = {} -- for each Orbit group in use, indicates the time of the run ctld.enableAutoOrbitingFlyingJtacOnTarget = true -- if true activate the AutoOrbitingFlyinfJtacOnTarget function for all flying JTACS ------------------------------------------------------------------------------------ @@ -7718,25 +7719,29 @@ function ctld.TreatOrbitJTAC(params, t) for k,v in pairs(ctld.jtacUnits) do -- vérify state of each active JTAC if ctld.isFlyingJtac(k) then - if ctld.jtacCurrentTargets[k] ~= nil then -- if detected target by JTAC + 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 + end + + if ctld.jtacCurrentTargets[k] ~= nil then -- if target lased by JTAC local droneAlti = Unit.getByName(k):getPoint().y - if ctld.InOrbitList(k) == false then -- JTAC lase a target but isn't in orbit => start orbiting - ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, droneAlti, 100) -- do orbit JTAC - ctld.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run - else -- JTAC already is orbiting => update coord for following the target mouvements + + if ctld.OrbitInUse[k] == nil then -- if JTAC is not in orbit => start orbiting and update start time + 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.JTACInRoute[k] = nil -- JTAC is in orbit => reset the route time + else -- JTAC already orbiting => update coord for following the target mouvements each 60" 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.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run - end + ctld.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run + end + end + else -- if JTAC have no target + if ctld.InOrbitList(k) == true then -- JTAC orbiting, without target => stop orbit + Unit.getByName(k):getController():popTask() -- stop orbiting JTAC Task => return to route + ctld.OrbitInUse[k] = nil -- Reset orbit + ctld.JTACInRoute[k] = timer.getTime() -- update time of the last start inroute end - else -- if JTAC have no target - -- if ctld.InOrbitList(k) == true then -- JTAC orbiting, without target => stop orbit - -- Group.getByName(k):getController():resetTask() -- stop orbiting JTAC - -- --Group.getByName(k):getController():popTask() -- stop orbiting JTAC - -- ctld.OrbitInUse[k] = nil -- Reset orbit - -- end - Group.getByName(k):getController():popTask() -- stop orbiting JTAC - ctld.backToRoute(k) -- return to the initial route of the JTAC group end end end @@ -7744,8 +7749,8 @@ function ctld.TreatOrbitJTAC(params, t) end ------------------------------------------------------------------------------------ -- Make orbit the group "_grpName", on target "_unitTargetName". _alti in meters, speed in km/h -function ctld.StartOrbitGroup(_grpName, _unitTargetName, _alti, _speed) - if (Unit.getByName(_unitTargetName) ~= nil) and (Group.getByName(_grpName) ~= nil) then -- si target unit and JTAC group exist +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 local orbit = { id = 'Orbit', params = {pattern = 'Circle', @@ -7754,8 +7759,9 @@ function ctld.StartOrbitGroup(_grpName, _unitTargetName, _alti, _speed) altitude = _alti } } - Group.getByName(_grpName):getController():pushTask(orbit) - ctld.OrbitInUse[_grpName] = true + local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() + Unit.getByName(_jtacUnitName):getController():popTask() -- stop current Task + Group.getByName(jtacGroupName):getController():pushTask(orbit) end end ------------------------------------------------------------------------------------------- @@ -7770,41 +7776,42 @@ function ctld.InOrbitList(_grpName) end ------------------------------------------------------------------------------------------- -- return the WayPoint number (on the JTAC route) the most near from the target -function ctld.NearWP(_unitTargetName, _grpName) +function ctld.NearWP(_unitTargetName, _jtacUnitName) local WP = 0 local memoDist = nil -- Lower distance checked - local JTACRoute = mist.getGroupRoute (_grpName, true) -- get the initial editor route of the current group - - if Group.getByName(_grpName):getUnit(1) ~= nil and Unit.getByName(_unitTargetName) ~= nil then --JTAC et unit must exist - for i=1, #JTACRoute do - local ptJTAC = {x = JTACRoute[i].x, y = JTACRoute[i].y} - local ptTarget = mist.utils.makeVec2(Unit.getByName(_unitTargetName):getPoint()) - local dist = mist.utils.get2DDist(ptJTAC, ptTarget) -- distance between 2 points - if memoDist == nil then - memoDist = dist - WP = i - elseif dist < memoDist then - memoDist = dist - WP = i - end + local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() + local JTACRoute = mist.getGroupRoute (jtacGroupName, true) -- get the initial editor route of the current group + if Unit.getByName(_jtacUnitName) ~= nil and Unit.getByName(_unitTargetName) ~= nil then --JTAC et unit must exist + for i=1, #JTACRoute do + local ptJTAC = {x = JTACRoute[i].x, y = JTACRoute[i].y} + local ptTarget = mist.utils.makeVec2(Unit.getByName(_unitTargetName):getPoint()) + local dist = mist.utils.get2DDist(ptJTAC, ptTarget) -- distance between 2 points + if memoDist == nil then + memoDist = dist + WP = i + elseif dist < memoDist then + memoDist = dist + WP = i end end + end return WP end ---------------------------------------------------------------------------- -- Modify the route deleting all the WP before "firstWP" param, for aligne the orbit on the nearest WP of the target -function ctld.backToRoute(_grpName) - local JTACRoute = mist.getGroupRoute (_grpName, true) -- get the initial editor route of the current group +function ctld.backToRoute(_jtacUnitName) + local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() + local JTACRoute = mist.getGroupRoute (jtacGroupName, true) -- get the initial editor route of the current group local Mission = {} Mission = { id = 'Mission', params = {route = {points = JTACRoute}}} + -- unactive orbit mode if it's on - if ctld.InOrbitList(_grpName) == true then -- if JTAC orbiting => stop it - Group.getByName(_grpName):getController():resetTask() -- stop JTAC orbiting - ctld.OrbitInUse[_grpName] = nil + if ctld.InOrbitList(_jtacUnitName) == true then -- if JTAC orbiting => stop it + ctld.OrbitInUse[_jtacUnitName] = nil end - - Group.getByName(_grpName):getController():setTask(Mission) -- submit the new route - return Mission + Unit.getByName(_jtacUnitName):getController():popTask() -- stop current Task + Unit.getByName(_jtacUnitName):getController():setTask(Mission) -- submit the new route + return Mission end ---------------------------------------------------------------------------- function ctld.isFlyingJtac(_jtacUnitName) From 0ccb2ba7bbd7ffca25169372e7441585c299bc8e Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 13 Apr 2025 23:14:21 +0200 Subject: [PATCH 129/166] debug --- CTLD.lua | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 3bb200e..184590e 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7776,16 +7776,16 @@ function ctld.InOrbitList(_grpName) end ------------------------------------------------------------------------------------------- -- return the WayPoint number (on the JTAC route) the most near from the target -function ctld.NearWP(_unitTargetName, _jtacUnitName) +function ctld.getNearestWP(_referenceUnitName) local WP = 0 local memoDist = nil -- Lower distance checked - local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() - local JTACRoute = mist.getGroupRoute (jtacGroupName, true) -- get the initial editor route of the current group - if Unit.getByName(_jtacUnitName) ~= nil and Unit.getByName(_unitTargetName) ~= nil then --JTAC et unit must exist + local refGroupName = Unit.getByName(_referenceUnitName):getGroup():getName() + local JTACRoute = mist.getGroupRoute (refGroupName, true) -- get the initial editor route of the current group + if Unit.getByName(_referenceUnitName) ~= nil then --JTAC et unit must exist for i=1, #JTACRoute do - local ptJTAC = {x = JTACRoute[i].x, y = JTACRoute[i].y} - local ptTarget = mist.utils.makeVec2(Unit.getByName(_unitTargetName):getPoint()) - local dist = mist.utils.get2DDist(ptJTAC, ptTarget) -- distance between 2 points + local ptWP = {x = JTACRoute[i].x, y = JTACRoute[i].y} + local ptRef = mist.utils.makeVec2(Unit.getByName(_referenceUnitName):getPoint()) + local dist = mist.utils.get2DDist(ptRef, ptWP) -- distance between 2 points if memoDist == nil then memoDist = dist WP = i @@ -7814,6 +7814,38 @@ function ctld.backToRoute(_jtacUnitName) return Mission end ---------------------------------------------------------------------------- +function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a route based on inital one, starting at _firstWpOfNewRoute WP + -- if the last WP switch to the first this cycle is recreated + local adjustedRoute = {} + local lastRouteTasks = _initialRouteTable{#_initialRouteTable].task.params.tasks + for i=1, #lastRouteTasks do -- look at each task of last WP + if lastRouteTasks[i].params.action.id == "SwitchWaypoint" then + local fromWaypointIndex = lastRouteTasks[i].params.action.params.fromWaypointIndex + local goToWaypointIndex = lastRouteTasks[i].params.action.params.goToWaypointIndex + end + end + + local idx = 1 + for i=1, #_initialRouteTable do -- look at each task of last WP + if i >= _firstWpOfNewRoute then + adjustedRoute[idx] = _initialRouteTable[i] + idx = idx + 1 + if i == #_initialRouteTable then -- if on last initial WP => delete initial switch action + + end + end + end + if _firstWpOfNewRoute > 1 then -- add firsts WPs at the end of adjustedRoute + for i=1, _firstWpOfNewRoute-1 do -- look at each WP skeeped in pass 1 + adjustedRoute[idx] = _initialRouteTable[i] + idx = idx + 1 + if i == _firstWpOfNewRoute-1 then -- if on final adjustedRoute WP => add new switch action on _firstWpOfNewRoute-1 WP + + end + end + end +end +---------------------------------------------------------------------------- function ctld.isFlyingJtac(_jtacUnitName) if Unit.getByName(_jtacUnitName) then if Unit.getByName(_jtacUnitName):getCategoryEx() == 0 then -- it's an airplane JTAC From 815684dd7757d6bd2abc318dd1c56afdaff413af Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 17 Apr 2025 00:04:22 +0200 Subject: [PATCH 130/166] commit --- CTLD.lua | 66 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 184590e..e17a9d9 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7738,7 +7738,8 @@ function ctld.TreatOrbitJTAC(params, t) end else -- if JTAC have no target if ctld.InOrbitList(k) == true then -- JTAC orbiting, without target => stop orbit - Unit.getByName(k):getController():popTask() -- stop orbiting JTAC Task => return to route + --Unit.getByName(k):getController():popTask() -- stop orbiting JTAC Task => return to route + ctld.backToRoute(k) -- return to route from the nearest WP ctld.OrbitInUse[k] = nil -- Reset orbit ctld.JTACInRoute[k] = timer.getTime() -- update time of the last start inroute end @@ -7802,48 +7803,59 @@ end function ctld.backToRoute(_jtacUnitName) local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() local JTACRoute = mist.getGroupRoute (jtacGroupName, true) -- get the initial editor route of the current group - local Mission = {} - Mission = { id = 'Mission', params = {route = {points = JTACRoute}}} + local newJTACRoute = ctld.adjustRoute(JTACRoute, ctld.getNearestWP(_jtacUnitName)) + local Mission = {} + Mission = { id = 'Mission', params = {route = {points = newJTACRoute}}} -- unactive orbit mode if it's on if ctld.InOrbitList(_jtacUnitName) == true then -- if JTAC orbiting => stop it ctld.OrbitInUse[_jtacUnitName] = nil end - Unit.getByName(_jtacUnitName):getController():popTask() -- stop current Task + --Unit.getByName(_jtacUnitName):getController():popTask() -- stop current Task Unit.getByName(_jtacUnitName):getController():setTask(Mission) -- submit the new route return Mission end ---------------------------------------------------------------------------- function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a route based on inital one, starting at _firstWpOfNewRoute WP - -- if the last WP switch to the first this cycle is recreated - local adjustedRoute = {} - local lastRouteTasks = _initialRouteTable{#_initialRouteTable].task.params.tasks - for i=1, #lastRouteTasks do -- look at each task of last WP - if lastRouteTasks[i].params.action.id == "SwitchWaypoint" then - local fromWaypointIndex = lastRouteTasks[i].params.action.params.fromWaypointIndex - local goToWaypointIndex = lastRouteTasks[i].params.action.params.goToWaypointIndex - end - end - - local idx = 1 - for i=1, #_initialRouteTable do -- look at each task of last WP - if i >= _firstWpOfNewRoute then + if _firstWpOfNewRoute >=1 then + -- if the last WP switch to the first this cycle is recreated + local adjustedRoute = {} + local mappingWP = {} + local idx = 1 + for i =_firstWpOfNewRoute, #_initialRouteTable do -- load each WP route starting from _firstWpOfNewRoute to end adjustedRoute[idx] = _initialRouteTable[i] + mappingWP[i] = idx idx = idx + 1 - if i == #_initialRouteTable then -- if on last initial WP => delete initial switch action + end + for i=1, _firstWpOfNewRoute - 1 do -- load each WP route starting from 1 to _firstWpOfNewRoute-1 + adjustedRoute[idx] = _initialRouteTable[i] + mappingWP[i] = idx + idx = idx + 1 + end + -- apply offset (_firstWpOfNewRoute) to SwitchWaypoint tasks + for idx = 1, #adjustedRoute do + for j=1, #adjustedRoute[idx].task.params.tasks do + if adjustedRoute[idx].task.params.tasks[j].id ~= "ControlledTask" then + if adjustedRoute[idx].task.params.tasks[j].params.action.id == "SwitchWaypoint" then + local fromWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.action.params.fromWaypointIndex + local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex + adjustedRoute[idx].task.params.tasks[j].params.action.params.fromWaypointIndex = idx + adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] + end + else -- for "ControlledTask" + if adjustedRoute[idx].task.params.tasks[j].params.task.params.action.id == "SwitchWaypoint" then + local fromWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.fromWaypointIndex + local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex + adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.fromWaypointIndex = idx + adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] + end end - end - end - if _firstWpOfNewRoute > 1 then -- add firsts WPs at the end of adjustedRoute - for i=1, _firstWpOfNewRoute-1 do -- look at each WP skeeped in pass 1 - adjustedRoute[idx] = _initialRouteTable[i] - idx = idx + 1 - if i == _firstWpOfNewRoute-1 then -- if on final adjustedRoute WP => add new switch action on _firstWpOfNewRoute-1 WP - end - end + end + return adjustedRoute end + return nil end ---------------------------------------------------------------------------- function ctld.isFlyingJtac(_jtacUnitName) From a706890cb6fe4769bbcd9b7cff6cd372f962ad17 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 17 Apr 2025 23:48:37 +0200 Subject: [PATCH 131/166] debug --- CTLD.lua | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index e17a9d9..0142687 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6445,7 +6445,7 @@ function ctld.JTACStart(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _rad end function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio) - ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), ctld.p(_laserCode))) + --ctld.logDebug(string.format("ctld.JTACAutoLase(_jtacGroupName=%s, _laserCode=%s", ctld.p(_jtacGroupName), ctld.p(_laserCode))) local _radio = _radio if not _radio then _radio = {} @@ -6455,9 +6455,9 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ local _laserB = math.floor((_laserCode - 1000) / 100) local _laserCD = _laserCode - 1000 - _laserB * 100 local _frequency = tostring(30 + _laserB + _laserCD * 0.05) - ctld.logTrace(string.format("_laserB=%s", ctld.p(_laserB))) - ctld.logTrace(string.format("_laserCD=%s", ctld.p(_laserCD))) - ctld.logTrace(string.format("_frequency=%s", ctld.p(_frequency))) + --ctld.logTrace(string.format("_laserB=%s", ctld.p(_laserB))) + --ctld.logTrace(string.format("_laserCD=%s", ctld.p(_laserCD))) + --ctld.logTrace(string.format("_frequency=%s", ctld.p(_frequency))) _radio.freq = _frequency _radio.mod = "fm" end @@ -6699,7 +6699,7 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ local targetSpeedVec = _enemyUnit:getVelocity() local targetSpeed = math.sqrt(targetSpeedVec.x ^ 2 + targetSpeedVec.y ^ 2 + targetSpeedVec.z ^ 2) local maxUpdateDist = 5 --maximum distance the unit will be allowed to travel before the lase spot is updated again - ctld.logTrace(string.format("targetSpeed=%s", ctld.p(targetSpeed))) + --ctld.logTrace(string.format("targetSpeed=%s", ctld.p(targetSpeed))) ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) @@ -6707,9 +6707,7 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ if targetSpeed >= maxUpdateDist / refreshDelay then local updateTimeStep = maxUpdateDist / targetSpeed --calculate the time step so that the target is never more than maxUpdateDist from it's last lased position - ctld.logTrace(string.format( - "JTAC - LASE - [%s] - target is moving at %s m/s, schedulting lasing steps every %ss", ctld.p(_jtacGroupName), - ctld.p(targetSpeed), ctld.p(updateTimeStep))) + --ctld.logTrace(string.format("JTAC - LASE - [%s] - target is moving at %s m/s, schedulting lasing steps every %ss", ctld.p(_jtacGroupName), ctld.p(targetSpeed), ctld.p(updateTimeStep))) local i = 1 while i * updateTimeStep <= refreshDelay - updateTimeStep do --while the scheduled time for the laseUnit call isn't greater than the time between two JTACAutoLase() calls minus one time step (because at the next time step JTACAutoLase() should have been called and this in term also calls laseUnit()) @@ -6717,12 +6715,10 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ timer.getTime() + i * updateTimeStep) i = i + 1 end - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduled %s moving target lasing steps", - ctld.p(_jtacGroupName), ctld.p(i))) + --ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduled %s moving target lasing steps", ctld.p(_jtacGroupName), ctld.p(i))) end - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), - ctld.p(refreshDelay), ctld.p(timer.getTime() + refreshDelay))) + --ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(refreshDelay), ctld.p(timer.getTime() + refreshDelay))) timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + refreshDelay) @@ -6735,14 +6731,13 @@ function ctld.JTACAutoLase(_jtacGroupName, _laserCode, _smoke, _lock, _colour, _ end end else - ctld.logDebug(string.format("JTAC - MODE - [%s] - No Enemies Nearby / Standby mode", ctld.p(_jtacGroupName))) + --ctld.logDebug(string.format("JTAC - MODE - [%s] - No Enemies Nearby / Standby mode", ctld.p(_jtacGroupName))) -- stop lazing the old spot - ctld.logDebug(string.format("JTAC - LASE - [%s] - canceling lasing of the old spot", ctld.p(_jtacGroupName))) + --ctld.logDebug(string.format("JTAC - LASE - [%s] - canceling lasing of the old spot", ctld.p(_jtacGroupName))) ctld.cancelLase(_jtacGroupName) - ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), - ctld.p(5), ctld.p(timer.getTime() + 5))) + --ctld.logTrace(string.format("JTAC - LASE - [%s] - scheduling JTACAutoLase in %ss at %s", ctld.p(_jtacGroupName), ctld.p(5), ctld.p(timer.getTime() + 5))) timer.scheduleFunction(ctld.timerJTACAutoLase, { _jtacGroupName, _laserCode, _smoke, _lock, _colour, _radio }, timer.getTime() + 5) end @@ -6879,7 +6874,7 @@ end function ctld.laseUnit(_enemyUnit, _jtacUnit, _jtacGroupName, _laserCode) --cancelLase(jtacGroupName) - ctld.logTrace("ctld.laseUnit()") + --ctld.logTrace("ctld.laseUnit()") local _spots = {} @@ -7175,20 +7170,18 @@ function ctld.getGroup(groupName) local _x = 1 if _group ~= nil then - ctld.logTrace(string.format("ctld.getGroup - %s - group ~= nil", ctld.p(groupName))) + --ctld.logTrace(string.format("ctld.getGroup - %s - group ~= nil", ctld.p(groupName))) if _group:isExist() then - ctld.logTrace(string.format("ctld.getGroup - %s - group:isExist()", ctld.p(groupName))) + --ctld.logTrace(string.format("ctld.getGroup - %s - group:isExist()", ctld.p(groupName))) local _groupUnits = _group:getUnits() if _groupUnits ~= nil and #_groupUnits > 0 then - ctld.logTrace(string.format("ctld.getGroup - %s - group has %s units", ctld.p(groupName), - ctld.p(#_groupUnits))) + --ctld.logTrace(string.format("ctld.getGroup - %s - group has %s units", ctld.p(groupName), ctld.p(#_groupUnits))) for _x = 1, #_groupUnits do if _groupUnits[_x]:getLife() > 0 then -- removed and _groupUnits[_x]:isExist() as isExist doesnt work on single units! table.insert(_filteredUnits, _groupUnits[_x]) else - ctld.logTrace(string.format("ctld.getGroup - %s - dead unit %s", ctld.p(groupName), - ctld.p(_groupUnits[_x]:getName()))) + --ctld.logTrace(string.format("ctld.getGroup - %s - dead unit %s", ctld.p(groupName), ctld.p(_groupUnits[_x]:getName()))) end end end @@ -7725,7 +7718,6 @@ function ctld.TreatOrbitJTAC(params, t) if ctld.jtacCurrentTargets[k] ~= nil then -- if target lased by JTAC 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 ctld.StartOrbitGroup(k, ctld.jtacCurrentTargets[k].name, droneAlti, 100) -- do orbit JTAC ctld.OrbitInUse[k] = timer.getTime() -- update time of the last orbit run @@ -7825,11 +7817,13 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r for i =_firstWpOfNewRoute, #_initialRouteTable do -- load each WP route starting from _firstWpOfNewRoute to end adjustedRoute[idx] = _initialRouteTable[i] mappingWP[i] = idx + ctld.logDebug("ctld.adjustRoute - mappingWP[%s]=[%s]", ctld.p(i), ctld.p(idx)) idx = idx + 1 end for i=1, _firstWpOfNewRoute - 1 do -- load each WP route starting from 1 to _firstWpOfNewRoute-1 adjustedRoute[idx] = _initialRouteTable[i] mappingWP[i] = idx + ctld.logDebug("ctld.adjustRoute - mappingWP[%s]=[%s]", ctld.p(i), ctld.p(idx)) idx = idx + 1 end @@ -7853,6 +7847,12 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r end end end + -- add a SwitchWaypoint to the last offseted task to ensure continuity of WPs + local newTaskIdx = #adjustedRoute[#_initialRouteTable].task.params.tasks + 1 + adjustedRoute[#_initialRouteTable].task.params.tasks[newTaskIdx].params.task.params.action.id = "SwitchWaypoint" + adjustedRoute[#_initialRouteTable].task.params.tasks[newTaskIdx].params.task.params.action.params.fromWaypointIndex = #_initialRouteTable + adjustedRoute[#_initialRouteTable].task.params.tasks[newTaskIdx].params.task.params.action.params.goToWaypointIndex = 1 + ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute)) return adjustedRoute end return nil From 9a0cc8eeeec2bdb0a8253ea00763811a1f37da5d Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Thu, 17 Apr 2025 23:48:58 +0200 Subject: [PATCH 132/166] debug --- CTLD.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 0142687..9bb813b 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7848,11 +7848,11 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r end end -- add a SwitchWaypoint to the last offseted task to ensure continuity of WPs - local newTaskIdx = #adjustedRoute[#_initialRouteTable].task.params.tasks + 1 - adjustedRoute[#_initialRouteTable].task.params.tasks[newTaskIdx].params.task.params.action.id = "SwitchWaypoint" - adjustedRoute[#_initialRouteTable].task.params.tasks[newTaskIdx].params.task.params.action.params.fromWaypointIndex = #_initialRouteTable - adjustedRoute[#_initialRouteTable].task.params.tasks[newTaskIdx].params.task.params.action.params.goToWaypointIndex = 1 - ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute)) + local newTaskIdx = #adjustedRoute[#adjustedRoute].task.params.tasks + 1 + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx] = {params = {task = {params = {action = {id = "SwitchWaypoint"}}}}} + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.task[1].params.action.params.fromWaypointIndex = #_initialRouteTable + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.task[1].params.action.params.goToWaypointIndex = 1 + ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute))ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute)) return adjustedRoute end return nil From f8659c5de622d4fe28f090c70dfa969b8f3ae7e9 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 19 Apr 2025 01:49:10 +0200 Subject: [PATCH 133/166] debug --- CTLD.lua | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 9bb813b..109ed40 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7847,12 +7847,33 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r end end end - -- add a SwitchWaypoint to the last offseted task to ensure continuity of WPs + local newTaskIdx = #adjustedRoute[#adjustedRoute].task.params.tasks + 1 - adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx] = {params = {task = {params = {action = {id = "SwitchWaypoint"}}}}} - adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.task[1].params.action.params.fromWaypointIndex = #_initialRouteTable - adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.task[1].params.action.params.goToWaypointIndex = 1 - ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute))ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute)) + +--[[ + [14]["task"]["params"]["tasks"][1] = table: 0000012EF5AF0CA8 { + [14]["task"]["params"]["tasks"][1]["number"] = 1, + [14]["task"]["params"]["tasks"][1]["auto"] = false, + [14]["task"]["params"]["tasks"][1]["id"] = "WrappedAction", + [14]["task"]["params"]["tasks"][1]["enabled"] = true, + [14]["task"]["params"]["tasks"][1]["params"] = table: 0000012EF5AF0CE8 { + [14]["task"]["params"]["tasks"][1]["params"]["action"] = table: 0000012EF5AF0D28 { + [14]["task"]["params"]["tasks"][1]["params"]["action"]["id"] = "SwitchWaypoint", + [14]["task"]["params"]["tasks"][1]["params"]["action"]["params"] = table: 0000012EF5AF0D68 { + [14]["task"]["params"]["tasks"][1]["params"]["action"]["params"]["goToWaypointIndex"] = 11, + [14]["task"]["params"]["tasks"][1]["params"]["action"]["params"]["fromWaypointIndex"] = 14, + + ]]-- + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx] = {number = newTaskIdx, + auto = false, + enabled = true, + id = "WrappedAction", + params = {action = {}} + } + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.id = "SwitchWaypoint" + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.params = {fromWaypointIndex = #_initialRouteTable, + goToWaypointIndex = 1 } + ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute,100)) return adjustedRoute end return nil From ca5bf76c78e66c8a1e1d30ab49ae69ec8fd8984e Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sun, 20 Apr 2025 00:55:26 +0200 Subject: [PATCH 134/166] debug --- CTLD.lua | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 109ed40..975d067 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7720,7 +7720,7 @@ function ctld.TreatOrbitJTAC(params, t) 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 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 ctld.JTACInRoute[k] = nil -- JTAC is in orbit => reset the route time else -- JTAC already orbiting => update coord for following the target mouvements each 60" if timer.getTime() > (ctld.OrbitInUse[k] + 60) then -- each 60" update orbit coord @@ -7744,14 +7744,13 @@ end -- Make orbit the group "_grpName", on target "_unitTargetName". _alti in meters, speed in km/h 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 - local orbit = { - id = 'Orbit', + local orbit = { id = 'Orbit', params = {pattern = 'Circle', point = mist.utils.makeVec2(mist.getAvgPos(mist.makeUnitTable({_unitTargetName}))), speed = _speed, altitude = _alti } - } + } local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() Unit.getByName(_jtacUnitName):getController():popTask() -- stop current Task Group.getByName(jtacGroupName):getController():pushTask(orbit) @@ -7794,16 +7793,15 @@ end -- Modify the route deleting all the WP before "firstWP" param, for aligne the orbit on the nearest WP of the target function ctld.backToRoute(_jtacUnitName) local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() - local JTACRoute = mist.getGroupRoute (jtacGroupName, true) -- get the initial editor route of the current group - local newJTACRoute = ctld.adjustRoute(JTACRoute, ctld.getNearestWP(_jtacUnitName)) - local Mission = {} + local JTACRoute = mist.getGroupRoute(jtacGroupName, true) -- get the initial editor route of the current group + local newJTACRoute = ctld.adjustRoute(JTACRoute, ctld.getNearestWP(_jtacUnitName)) + local Mission = {} Mission = { id = 'Mission', params = {route = {points = newJTACRoute}}} -- unactive orbit mode if it's on if ctld.InOrbitList(_jtacUnitName) == true then -- if JTAC orbiting => stop it ctld.OrbitInUse[_jtacUnitName] = nil end - --Unit.getByName(_jtacUnitName):getController():popTask() -- stop current Task Unit.getByName(_jtacUnitName):getController():setTask(Mission) -- submit the new route return Mission end @@ -7832,14 +7830,12 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r for j=1, #adjustedRoute[idx].task.params.tasks do if adjustedRoute[idx].task.params.tasks[j].id ~= "ControlledTask" then if adjustedRoute[idx].task.params.tasks[j].params.action.id == "SwitchWaypoint" then - local fromWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.action.params.fromWaypointIndex local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex adjustedRoute[idx].task.params.tasks[j].params.action.params.fromWaypointIndex = idx adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] end else -- for "ControlledTask" if adjustedRoute[idx].task.params.tasks[j].params.task.params.action.id == "SwitchWaypoint" then - local fromWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.fromWaypointIndex local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.fromWaypointIndex = idx adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] @@ -7847,10 +7843,7 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r end end end - - local newTaskIdx = #adjustedRoute[#adjustedRoute].task.params.tasks + 1 - ---[[ + --[[ [14]["task"]["params"]["tasks"][1] = table: 0000012EF5AF0CA8 { [14]["task"]["params"]["tasks"][1]["number"] = 1, [14]["task"]["params"]["tasks"][1]["auto"] = false, @@ -7863,7 +7856,8 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r [14]["task"]["params"]["tasks"][1]["params"]["action"]["params"]["goToWaypointIndex"] = 11, [14]["task"]["params"]["tasks"][1]["params"]["action"]["params"]["fromWaypointIndex"] = 14, - ]]-- + ]]-- + local newTaskIdx = #adjustedRoute[#adjustedRoute].task.params.tasks + 1 adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx] = {number = newTaskIdx, auto = false, enabled = true, @@ -7873,7 +7867,7 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.id = "SwitchWaypoint" adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.params = {fromWaypointIndex = #_initialRouteTable, goToWaypointIndex = 1 } - ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute,100)) + ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute)) return adjustedRoute end return nil From e4a0fff93182f29fc3f7017990d476bdc0eac6f4 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 20 Apr 2025 12:38:18 +0200 Subject: [PATCH 135/166] debug --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 975d067..8ba1a4f 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7772,7 +7772,7 @@ function ctld.getNearestWP(_referenceUnitName) local WP = 0 local memoDist = nil -- Lower distance checked local refGroupName = Unit.getByName(_referenceUnitName):getGroup():getName() - local JTACRoute = mist.getGroupRoute (refGroupName, true) -- get the initial editor route of the current group + local JTACRoute = mist.getGroupRoute(refGroupName, true) -- get the initial editor route of the current group if Unit.getByName(_referenceUnitName) ~= nil then --JTAC et unit must exist for i=1, #JTACRoute do local ptWP = {x = JTACRoute[i].x, y = JTACRoute[i].y} From 87e8a31f315b6f6115afab6d91db23e95e5e68f4 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 20 Apr 2025 14:06:58 +0200 Subject: [PATCH 136/166] debug --- CTLD.lua | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 8ba1a4f..24e6086 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7793,8 +7793,10 @@ end -- Modify the route deleting all the WP before "firstWP" param, for aligne the orbit on the nearest WP of the target function ctld.backToRoute(_jtacUnitName) local jtacGroupName = Unit.getByName(_jtacUnitName):getGroup():getName() - local JTACRoute = mist.getGroupRoute(jtacGroupName, true) -- get the initial editor route of the current group - local newJTACRoute = ctld.adjustRoute(JTACRoute, ctld.getNearestWP(_jtacUnitName)) + --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 newJTACRoute = ctld.adjustRoute(JTACRoute, ctld.getNearestWP(_jtacUnitName)) + local Mission = {} Mission = { id = 'Mission', params = {route = {points = newJTACRoute}}} @@ -7826,6 +7828,7 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r end -- apply offset (_firstWpOfNewRoute) to SwitchWaypoint tasks + local lastWpAsAlreadySwitchWaypoint = false for idx = 1, #adjustedRoute do for j=1, #adjustedRoute[idx].task.params.tasks do if adjustedRoute[idx].task.params.tasks[j].id ~= "ControlledTask" then @@ -7833,40 +7836,34 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex adjustedRoute[idx].task.params.tasks[j].params.action.params.fromWaypointIndex = idx adjustedRoute[idx].task.params.tasks[j].params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] + if idx == #adjustedRoute then + lastWpAsAlreadySwitchWaypoint = true + end end else -- for "ControlledTask" if adjustedRoute[idx].task.params.tasks[j].params.task.params.action.id == "SwitchWaypoint" then local goToWaypointIndex = adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.fromWaypointIndex = idx adjustedRoute[idx].task.params.tasks[j].params.task.params.action.params.goToWaypointIndex = mappingWP[goToWaypointIndex] + if idx == #adjustedRoute then + lastWpAsAlreadySwitchWaypoint = true + end end end end end - --[[ - [14]["task"]["params"]["tasks"][1] = table: 0000012EF5AF0CA8 { - [14]["task"]["params"]["tasks"][1]["number"] = 1, - [14]["task"]["params"]["tasks"][1]["auto"] = false, - [14]["task"]["params"]["tasks"][1]["id"] = "WrappedAction", - [14]["task"]["params"]["tasks"][1]["enabled"] = true, - [14]["task"]["params"]["tasks"][1]["params"] = table: 0000012EF5AF0CE8 { - [14]["task"]["params"]["tasks"][1]["params"]["action"] = table: 0000012EF5AF0D28 { - [14]["task"]["params"]["tasks"][1]["params"]["action"]["id"] = "SwitchWaypoint", - [14]["task"]["params"]["tasks"][1]["params"]["action"]["params"] = table: 0000012EF5AF0D68 { - [14]["task"]["params"]["tasks"][1]["params"]["action"]["params"]["goToWaypointIndex"] = 11, - [14]["task"]["params"]["tasks"][1]["params"]["action"]["params"]["fromWaypointIndex"] = 14, - - ]]-- - local newTaskIdx = #adjustedRoute[#adjustedRoute].task.params.tasks + 1 - adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx] = {number = newTaskIdx, - auto = false, - enabled = true, - id = "WrappedAction", - params = {action = {}} - } - adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.id = "SwitchWaypoint" - adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.params = {fromWaypointIndex = #_initialRouteTable, - goToWaypointIndex = 1 } + if lastWpAsAlreadySwitchWaypoint == false then + local newTaskIdx = #adjustedRoute[#adjustedRoute].task.params.tasks + 1 + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx] = {number = newTaskIdx, + auto = false, + enabled = true, + id = "WrappedAction", + params = {action = {}} + } + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.id = "SwitchWaypoint" + adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.params = {fromWaypointIndex = #_initialRouteTable, + goToWaypointIndex = 1 } + end ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute)) return adjustedRoute end From 33d8810ebb4526bf854893eb1eda4ea07e294277 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 20 Apr 2025 14:13:32 +0200 Subject: [PATCH 137/166] commit #149 drone follow target --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 24e6086..c2b60cc 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7864,7 +7864,7 @@ function ctld.adjustRoute(_initialRouteTable, _firstWpOfNewRoute) -- create a r adjustedRoute[#adjustedRoute].task.params.tasks[newTaskIdx].params.action.params = {fromWaypointIndex = #_initialRouteTable, goToWaypointIndex = 1 } end - ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute)) + --ctld.logDebug("ctld.adjustRoute - adjustedRoute = [%s]", ctld.p(adjustedRoute)) return adjustedRoute end return nil From 807e81e38981816d53044dfc47855c48f4a20ada Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 20 Apr 2025 14:51:25 +0200 Subject: [PATCH 138/166] debug #137 --- CTLD.lua | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index c2b60cc..2fc34b4 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6058,9 +6058,19 @@ function ctld.buildPaginatedMenu(_menuEntries) end end +--****************************************************************************************************** +-- return true if _typeUnitDesc already exist in _repackableVehiclesTable +-- ex: ctld.isUnitInrepackableVehicles(repackableTable, "Humvee - TOW") +function ctld.isUnitInrepackableVehicles(_repackableVehiclesTable, _typeUnitDesc) + for i=1, #_repackableVehiclesTable do + if _repackableVehiclesTable[i].desc == _typeUnitDesc then + return true + end + end + return false +end --****************************************************************************************************** function ctld.updateRepackMenu(_playerUnitName) - --ctld.logTrace("FG_ _playerUnitName = %s", ctld.p(_playerUnitName)) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then local _unitTypename = playerUnit:getTypeName() @@ -6075,16 +6085,18 @@ function ctld.updateRepackMenu(_playerUnitName) missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} - --ctld.logTrace("FG_ ctld.updateRepackMenu.repackableVehicles = %s", ctld.p(repackableVehicles)) - --for _, _vehicle in pairs(repackableVehicles) do for i, _vehicle in ipairs(repackableVehicles) do - _vehicle.playerUnitName = _playerUnitName - table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, - groupId = _groupId, - subMenuPath = RepackmenuPath, - menuFunction = ctld.repackVehicleRequest, - menuArgsTable = mist.utils.deepCopy(_vehicle) - }) + if ctld.isUnitInrepackableVehicles(menuEntries, _vehicle.desc) == false then + _vehicle.playerUnitName = _playerUnitName + ctld.logTrace("FG_ ctld.updateRepackMenu PASS") + table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, + groupId = _groupId, + subMenuPath = RepackmenuPath, + menuFunction = ctld.repackVehicleRequest, + menuArgsTable = mist.utils.deepCopy(_vehicle), + desc = _vehicle.desc + }) + end end --ctld.logTrace("FG_ menuEntries = %s", ctld.p(menuEntries)) ctld.buildPaginatedMenu(menuEntries) From dd7145119488e4acdfd692336b88099104c5b856 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:55:49 +0200 Subject: [PATCH 139/166] debug #137 Repack --- CTLD.lua | 59 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 2fc34b4..8bab379 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2042,9 +2042,9 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if t == nil then t = timer.getTime() end - -- if #ctld.repackRequestsStack ~= 0 then - -- ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) - -- end + if #ctld.repackRequestsStack ~= 0 then + ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) + end for ii, v in ipairs(ctld.repackRequestsStack) do --ctld.logTrace("FG_ ctld.repackVehicle.v[%s] = %s", ii, ctld.p(v)) local repackableUnitName = v.repackableUnitName @@ -2082,7 +2082,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' end ctld.repackRequestsStack[ii] = nil -- remove the request from the stacking table end - ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units + --ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units end if ctld.enableRepackingVehicles == true then return t + 3 -- reschedule the function in 3 seconds @@ -5874,7 +5874,7 @@ function ctld.addTransportF10MenuOptions(_unitName) end if ctld.enableRepackingVehicles then - ctld.updateRepackMenu(_unitName) + --ctld.updateRepackMenu(_unitName) end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then missionCommands.addCommandForGroup(_groupId, @@ -6033,37 +6033,32 @@ end --****************************************************************************************************** function ctld.buildPaginatedMenu(_menuEntries) - --ctld.logTrace("FG_ _menuEntries = [%s]", ctld.p(_menuEntries)) - local nextSubMenuPath = "" + local nextSubMenuPath = {} local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do - --ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) - if nextSubMenuPath ~= "" and menu.subMenuPath ~= nextSubMenuPath then - --ctld.logTrace("FG_ boucle[%s].nextSubMenuPath = %s", i, ctld.p(nextSubMenuPath)) + if #nextSubMenuPath ~= 0 then menu.subMenuPath = mist.utils.deepCopy(nextSubMenuPath) + --menu.subMenuPath = nextSubMenuPath end -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 if itemNbSubmenu == 10 and i < #_menuEntries then -- page limit reached - --ctld.logTrace("FG_ boucle[%s].menu.subMenuPath avant = %s", i, ctld.p(menu.subMenuPath)) - nextSubMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) - menu.subMenuPath = mist.utils.deepCopy(nextSubMenuPath) - --ctld.logTrace("FG_ boucle[%s].menu.subMenuPath apres = %s", i, ctld.p(menu.subMenuPath)) + nextSubMenuPath = missionCommands.addSubMenuForGroup(menu.groupId, ctld.i18n_translate("Next page"), menu.subMenuPath) itemNbSubmenu = 1 end menu.menuArgsTable.subMenuPath = mist.utils.deepCopy(menu.subMenuPath) -- copy the table to avoid overwriting the same table in the next loop menu.menuArgsTable.subMenuLineIndex = itemNbSubmenu - --ctld.logTrace("FG_ boucle[%s] ctld.buildPaginatedMenu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, mist.utils.deepCopy(menu.menuArgsTable)) + ctld.logTrace("FG_ boucle[%s].menu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) end end --****************************************************************************************************** --- return true if _typeUnitDesc already exist in _repackableVehiclesTable +-- return true if _typeUnitDesc already exist in _MenuEntriesTable -- ex: ctld.isUnitInrepackableVehicles(repackableTable, "Humvee - TOW") -function ctld.isUnitInrepackableVehicles(_repackableVehiclesTable, _typeUnitDesc) - for i=1, #_repackableVehiclesTable do - if _repackableVehiclesTable[i].desc == _typeUnitDesc then +function ctld.isUnitInMenuEntriesTable(_MenuEntriesTable, _typeUnitDesc) + for i=1, #_MenuEntriesTable do + if _MenuEntriesTable[i].menuArgsTable.desc == _typeUnitDesc then return true end end @@ -6086,16 +6081,20 @@ function ctld.updateRepackMenu(_playerUnitName) local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} for i, _vehicle in ipairs(repackableVehicles) do - if ctld.isUnitInrepackableVehicles(menuEntries, _vehicle.desc) == false then + if ctld.isUnitInMenuEntriesTable(menuEntries, _vehicle.desc) == false then _vehicle.playerUnitName = _playerUnitName - ctld.logTrace("FG_ ctld.updateRepackMenu PASS") - table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, - groupId = _groupId, - subMenuPath = RepackmenuPath, - menuFunction = ctld.repackVehicleRequest, - menuArgsTable = mist.utils.deepCopy(_vehicle), - desc = _vehicle.desc - }) + -- table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, + -- groupId = _groupId, + -- subMenuPath = RepackmenuPath, + -- menuFunction = ctld.repackVehicleRequest, + -- menuArgsTable = mist.utils.deepCopy(_vehicle) + -- }) + menuEntries[i] = { text = ctld.i18n_translate("repack ") .. _vehicle.unit, + groupId = _groupId, + subMenuPath = RepackmenuPath, + menuFunction = ctld.repackVehicleRequest, + menuArgsTable = mist.utils.deepCopy(_vehicle) + } end end --ctld.logTrace("FG_ menuEntries = %s", ctld.p(menuEntries)) @@ -6120,7 +6119,7 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr local _unitTypename = _unit:getTypeName() local _groupId = ctld.getGroupId(_unit) if _groupId then - if ctld.addedTo[tostring(_groupId)] ~= nil then + if ctld.addedTo[tostring(_groupId)] ~= nil then -- if groupMenu on loaded => add RepackMenus --ctld.logTrace("FG_ ctld.autoUpdateRepackMenu call ctld.updateRepackMenu for = %s", ctld.p(_unitName)) ctld.updateRepackMenu(_unitName) end @@ -6133,7 +6132,7 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr end end end - return t + 3 -- reschedule every 3 seconds + return t + 6 -- reschedule every 6 seconds end --****************************************************************************************************** From fcef6a66d131e8a6a09d1f6fc660df2162b2cc0a Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 21 Apr 2025 01:44:55 +0200 Subject: [PATCH 140/166] debug #137 --- CTLD.lua | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 8bab379..fb57c10 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5873,9 +5873,9 @@ function ctld.addTransportF10MenuOptions(_unitName) ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) end - if ctld.enableRepackingVehicles then - --ctld.updateRepackMenu(_unitName) - end + -- if ctld.enableRepackingVehicles then + -- ctld.updateRepackMenu(_unitName) + -- end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath, @@ -5954,13 +5954,10 @@ function ctld.addTransportF10MenuOptions(_unitName) if ctld.vehicleCommandsPath[_unitName] == nil then ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_rootPath) end - if ctld.enableRepackingVehicles then - ctld.updateRepackMenu(_unitName) - end - + -- if ctld.enableRepackingVehicles then + -- ctld.updateRepackMenu(_unitName) + -- end end - - end end @@ -6049,7 +6046,8 @@ function ctld.buildPaginatedMenu(_menuEntries) menu.menuArgsTable.subMenuPath = mist.utils.deepCopy(menu.subMenuPath) -- copy the table to avoid overwriting the same table in the next loop menu.menuArgsTable.subMenuLineIndex = itemNbSubmenu missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, mist.utils.deepCopy(menu.menuArgsTable)) - ctld.logTrace("FG_ boucle[%s].menu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) + --ctld.logTrace("FG_ boucle[%s].menu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) + ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) end end @@ -6083,12 +6081,6 @@ function ctld.updateRepackMenu(_playerUnitName) for i, _vehicle in ipairs(repackableVehicles) do if ctld.isUnitInMenuEntriesTable(menuEntries, _vehicle.desc) == false then _vehicle.playerUnitName = _playerUnitName - -- table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, - -- groupId = _groupId, - -- subMenuPath = RepackmenuPath, - -- menuFunction = ctld.repackVehicleRequest, - -- menuArgsTable = mist.utils.deepCopy(_vehicle) - -- }) menuEntries[i] = { text = ctld.i18n_translate("repack ") .. _vehicle.unit, groupId = _groupId, subMenuPath = RepackmenuPath, From d2789f554af79d29ea17610cce5aaef98bb9f93c Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 21 Apr 2025 02:51:14 +0200 Subject: [PATCH 141/166] debug #137 --- CTLD.lua | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index fb57c10..2983ca0 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2046,7 +2046,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' ctld.logTrace("FG_ ctld.repackVehicle.ctld.repackRequestsStack = %s", ctld.p(ctld.repackRequestsStack)) end for ii, v in ipairs(ctld.repackRequestsStack) do - --ctld.logTrace("FG_ ctld.repackVehicle.v[%s] = %s", ii, ctld.p(v)) + ctld.logTrace("FG_ ctld.repackVehicle.v[%s] = %s", ii, ctld.p(v)) local repackableUnitName = v.repackableUnitName local repackableUnit = Unit.getByName(repackableUnitName) local crateWeight = v.weight @@ -2065,7 +2065,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) end for i = 1, v.cratesRequired or 1 do - -- see to spawn the crate at random position heading the transport unnit + -- see to spawn the crate at random position heading the transport unit local _unitId = ctld.getNextUnitId() local _name = string.format("%s_%i", v.desc, _unitId) local secureDistance = ctld.getSecureDistanceFromUnit(playerUnitName) or 10 @@ -2077,12 +2077,11 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, "dynamic") end end - - repackableUnit:destroy() -- destroy repacked unit + repackableUnit:destroy() -- destroy repacked unit end - ctld.repackRequestsStack[ii] = nil -- remove the request from the stacking table end --ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units + ctld.repackRequestsStack[ii] = nil -- remove the processed request from the stacking table end if ctld.enableRepackingVehicles == true then return t + 3 -- reschedule the function in 3 seconds @@ -6076,7 +6075,7 @@ function ctld.updateRepackMenu(_playerUnitName) RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") --ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId,ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) local menuEntries = {} for i, _vehicle in ipairs(repackableVehicles) do if ctld.isUnitInMenuEntriesTable(menuEntries, _vehicle.desc) == false then From c608d00d15075d56470af525e0d3d6cc25c1ca74 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 21 Apr 2025 13:29:31 +0200 Subject: [PATCH 142/166] commit #137 --- CTLD.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 2983ca0..ad492e4 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2038,7 +2038,6 @@ end -- *************************************************************** function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' to process each request --ctld.logTrace("FG_ XXXXXXXXXXXXXXXXXXXXXXXXXXX ctld.repackVehicle.ctld.repackRequestsStack XXXXXXXXXXXXXXXXXXXXXXXXXXX") - if t == nil then t = timer.getTime() end From 35f52d543a58c61c00b477ae1726eb291be81a4b Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Mon, 21 Apr 2025 14:09:18 +0200 Subject: [PATCH 143/166] commit --- CTLD.lua | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index ad492e4..ab48fd6 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6096,7 +6096,6 @@ end --****************************************************************************************************** function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each transport unit - --ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.ctld.transportPilotNames = %s", ctld.p(mist.utils.tableShow(ctld.transportPilotNames))) if t == nil then t = timer.getTime() end if ctld.enableRepackingVehicles then for _, _unitName in pairs(ctld.transportPilotNames) do @@ -6110,7 +6109,6 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr local _groupId = ctld.getGroupId(_unit) if _groupId then if ctld.addedTo[tostring(_groupId)] ~= nil then -- if groupMenu on loaded => add RepackMenus - --ctld.logTrace("FG_ ctld.autoUpdateRepackMenu call ctld.updateRepackMenu for = %s", ctld.p(_unitName)) ctld.updateRepackMenu(_unitName) end end @@ -6133,11 +6131,8 @@ function ctld.addOtherF10MenuOptions() local status, error = pcall(function() -- now do any player controlled aircraft that ARENT transport units if ctld.enabledRadioBeaconDrop then - -- get all BLUE players - ctld.addRadioListCommand(2) - - -- get all RED players - ctld.addRadioListCommand(1) + ctld.addRadioListCommand(2) -- get all BLUE players + ctld.addRadioListCommand(1) -- get all RED players end if ctld.JTAC_jtacStatusF10 then From c576268d879c06829b84ae76f23dbf33c4b6ca9a Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Mon, 21 Apr 2025 14:10:41 +0200 Subject: [PATCH 144/166] commit --- CTLD.lua | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index ab48fd6..a2b9302 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6961,7 +6961,6 @@ function ctld.getCurrentUnit(_jtacUnit, _jtacGroupName) local _tempPoint = nil local _tempDist = nil local _tempPosition = nil - local _jtacPosition = _jtacUnit:getPosition() local _jtacPoint = _jtacUnit:getPoint() @@ -6989,15 +6988,11 @@ end -- Find nearest enemy to JTAC that isn't blocked by terrain function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType, _distance) --local startTime = os.clock() - local _maxDistance = _distance or ctld.JTAC_maxDistance - local _nearestDistance = _maxDistance - local _jtacGroupName = _jtacUnit:getGroup():getName() local _jtacPoint = _jtacUnit:getPoint() local _coa = _jtacUnit:getCoalition() - local _offsetJTACPos = { x = _jtacPoint.x, y = _jtacPoint.y + 2.0, z = _jtacPoint.z } local _volume = { @@ -7010,7 +7005,6 @@ function ctld.findNearestVisibleEnemy(_jtacUnit, _targetType, _distance) local _unitList = {} - local _search = function(_unit, _coa) pcall(function() if _unit ~= nil From 3ac2b3b22c1d20dadd07a6d3bc9f7b2a22b5224c Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Mon, 21 Apr 2025 16:49:55 +0200 Subject: [PATCH 145/166] commit --- CTLD.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index a2b9302..8b66afd 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6045,7 +6045,6 @@ function ctld.buildPaginatedMenu(_menuEntries) menu.menuArgsTable.subMenuLineIndex = itemNbSubmenu missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, mist.utils.deepCopy(menu.menuArgsTable)) --ctld.logTrace("FG_ boucle[%s].menu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) - ctld.logTrace("FG_ boucle[%s].menu = %s", i, ctld.p(menu)) end end From 1c66408127e5ef981712b02b11a42807e062483d Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 21 Apr 2025 21:04:17 +0200 Subject: [PATCH 146/166] Readme update --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a61c2ce..02f22d5 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ This script is a rewrite of some of the functionality of the original Complete C * [Spawn Sling loadable crate at a Zone](#spawn-sling-loadable-crate-at-a-zone) * [Spawn Sling loadable crate at a Point](#spawn-sling-loadable-crate-at-a-point) * [JTAC Automatic Targeting and Laser](#jtac-automatic-targeting-and-laser) + * [JTAC Automatic Orbiting Over Lased Target](#jtac-automatic-orbiting-over-lased-target) * [In Game](#in-game) * [Troop Loading and Unloading](#troop-loading-and-unloading) * [Cargo Spawning and Sling Loading](#cargo-spawning-and-sling-loading) @@ -862,6 +863,13 @@ 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-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. +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 + # In Game ## Troop Loading and Unloading @@ -1010,7 +1018,6 @@ You can also repair a partially destroyed HAWK / BUK or KUB system by dropping a ## 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. -If repacking is possible, a logistics zone is automatically created around your aircraft to allow you to load and handle the produced crates. ## 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. From 5c48a9d756c1626516d0f57eff3de761b080a4e9 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Mon, 21 Apr 2025 21:27:07 +0200 Subject: [PATCH 147/166] commit --- CTLD.lua | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 8b66afd..7cb6fea 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5871,9 +5871,6 @@ function ctld.addTransportF10MenuOptions(_unitName) ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) end - -- if ctld.enableRepackingVehicles then - -- ctld.updateRepackMenu(_unitName) - -- end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Unload FOB Crate"), _vehicleCommandsPath, @@ -5950,11 +5947,8 @@ function ctld.addTransportF10MenuOptions(_unitName) end if ctld.unitDynamicCargoCapable(_unit) then if ctld.vehicleCommandsPath[_unitName] == nil then - ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_rootPath) + ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_cratesMenuPath) end - -- if ctld.enableRepackingVehicles then - -- ctld.updateRepackMenu(_unitName) - -- end end end end From 42e8549649d422c144442ef027ecdcd255f6b819 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Mon, 21 Apr 2025 22:48:32 +0200 Subject: [PATCH 148/166] commit --- CTLD.lua | 16 ++++++++-------- README.md | 5 +++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 7cb6fea..2ba32ca 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1927,7 +1927,7 @@ end -- *************************************************************** function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between the center of unitName, to be sure not touch the unitName - local rotorDiameter = 19 -- meters -- õk for UH & CH47 + local rotorDiameter = 0 --19 -- meters -- õk for UH & CH47 if Unit.getByName(_unitName) then local unitUserBox = Unit.getByName(_unitName):getDesc().box local SecureDistanceFromUnit = 0 @@ -2619,7 +2619,7 @@ function ctld.getPointInFrontSector(_unit, _offset) local playerHeading = mist.getHeading(_unit) local randomHeading = ctld.RandomReal(playerHeading - math.pi/4, playerHeading + math.pi/4) if _offset == nil then - _offset = 30 + _offset = 20 end return ctld.getPointAtDirection(_unit, _offset, randomHeading) end @@ -6072,12 +6072,12 @@ function ctld.updateRepackMenu(_playerUnitName) for i, _vehicle in ipairs(repackableVehicles) do if ctld.isUnitInMenuEntriesTable(menuEntries, _vehicle.desc) == false then _vehicle.playerUnitName = _playerUnitName - menuEntries[i] = { text = ctld.i18n_translate("repack ") .. _vehicle.unit, - groupId = _groupId, - subMenuPath = RepackmenuPath, - menuFunction = ctld.repackVehicleRequest, - menuArgsTable = mist.utils.deepCopy(_vehicle) - } + table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, + groupId = _groupId, + subMenuPath = RepackmenuPath, + menuFunction = ctld.repackVehicleRequest, + menuArgsTable = mist.utils.deepCopy(_vehicle) + }) end end --ctld.logTrace("FG_ menuEntries = %s", ctld.p(menuEntries)) diff --git a/README.md b/README.md index 02f22d5..d413e5a 100644 --- a/README.md +++ b/README.md @@ -1018,6 +1018,11 @@ You can also repair a partially destroyed HAWK / BUK or KUB system by dropping a ## 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 + +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 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. From 08635622b00996947515f8ad8d5a744aaf0f303d Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Mon, 21 Apr 2025 22:49:33 +0200 Subject: [PATCH 149/166] load temporary test .miz --- test_witchcraft_#137 et #149.miz | Bin 0 -> 96230 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test_witchcraft_#137 et #149.miz diff --git a/test_witchcraft_#137 et #149.miz b/test_witchcraft_#137 et #149.miz new file mode 100644 index 0000000000000000000000000000000000000000..139dacf325992f4ad868869a6781661f81502b3c GIT binary patch literal 96230 zcmZ6wV|Zpk(=Hm@w(VqM+qP}n6WdNEwr$&**tV@FJoA3vd-gfkUO(zbSFOA2?ykP7 zx>`{N6buar1_}!3|NbEV4cJ+^xL7&ZU;n;##+69AzO5=Vy!#tRO4feaWyA3jE>5u7 zWE20Jer9?*ff5S|S_&!{2oucA2LDm;mFp&Y+XV-{QE5A;yp$X_X>(_d8M>aQi<_2K zKt8uS+rZ$j##FMufY;q)@Xzz-)3Uc)Q@3ZMw*5-g$hpAV$4@BY-Nf?K`}^4P`$6Zo z-LuW7U+2y=X7u{|^*Z3|%Kfx2;QitS?c1!gdy|#|=B5BW!AjrnhW0<# zKjwU0mY*cZrH|q{e}85jw!8htCVL-0w)ONpO>RGHue+Mr3ReQ8aZgrvXECefY9{=% zdUEq_mk!BI|LhY?7*JOJb@ zTX*e3>s0jnsH$_iUR}QR{dn1geOSnP{#@QEDLciznip+D_vg;Z)vKPZ{k!^DHM2ei zFn$TwXqwqA)!eu>U#-e|23*BE{J6ZlAnxWJSbUDHA6_V1e19w|@B`XL2@i2Gt1dux z{eQmBll{M*>NHO-75emN1iozD{9L$n>}v(SjC%nOCnIHfd|vNo6HRseJRhIY3V^=& zlS*BA3=Wm=bILb=#GBt0T~*nB4-REI$@z@4{&Dd! z>vHD8`BtO*}|MqeD!B}69z~F6&k}_+x>G+%DqRT-bOjZxY{(kA zXR-ZGVMCk(M|~cLM$NJ9H$Sq3rW$!&C3k&g8;70U{PpM&Mo;$l)B30H<7v%+^3DE= zw(c$+yAO}Y^TV-mZ#H?Ea^qZ^g@H@nE`9`0%LOl(gM;F>eba_4rU!eok?HQ#Oq+)P zT=VwrG5_)iVY7dio;|}Cy8h^l_X}6`tU+mdQ{R==#rlfvUafzHLZQXtC1GV<_5=L_ z!;A0vbT0o@`}g87o0s4Fp)E|T&TY>p;pvpCQfXErRn!W!*Kf8>o)4;LUwTj6XES%q zuR`w5odGis%nd!wK3uD3`HiFGYZyDbE9ZA(W*(mo?-md7NB5@lXX`(7N9OKcY&r@6 z-#q+8p2xV#fK=4 zI}X48kvg06DD8Qj^ipS`WQ)zITtA(!wp#eBb67mPP1^QNnd$qlf6>{vuTY}inr3;) zYs;XqsxddSzJ7AOTn$jvi9cPRc?a*#ItJ`m)Rjf6J;=&&m2rA>!JWPQZ5@U3;IX-h zexFRQiUv#pt`4x{qFw3J7vDX~{PP%{5_ow7SK0M)b${+&n)xx*C$v1?&HecNnGK4o zI)5NK(<(Zbg5zYi^gfLJdj8y~hxycAa?kYHYgKH5m-BY5zE!NOEmAIKJ)S?$Jg*Gi z*?NHsSp5z9{M+m2jwi7xuudSQ;p6Yae+cruy0WcmcIT%oTGjJ%JoJ9h0ve21lZR!K zN?+uHi0l9DU&+YJGK;Px7lRuFiH+&7I2@~Wh??{O)J94j8AmbwZ z+JEz+?{f9xEqHm8(Dzw=`XyY^F8MvY(fi`vxZyo>_d7r8f>h_HxHYTlyCSKex~`Un z*!Q=XTBOp=_pF_q)Rzi*MBKANDp@IprdnZ0ERc{QFa5)!uBq=SMoN@w;^ULh84i&V=!$ zMg6AR`fryi+AbUW)RMFf^~QE>44sKWkJYV7<)5S0cV&UR&03k?0vZzsmt}4`S{Gei z*PT~O?zB@kS*dire*A5E>Duxd6DS`3|4iJ%mpe9<`EqkNz%#pMO4LkTsF&CD%v@YL zMo$*ZOk65XPNHUd)L38BZ0zZevvg!SPF5DgTx_Vfx3nuQ?df$`+tTcA=&rMPrrvvU zmbWy0KBN(!%-b}mvZAeUqbssXCs{R`RAZM}TN^SdSk+b7(p?o8++bY@Z_8WBy-+($ z-i*)0wE52Des%{uS-+E_y+2qtOs#CcXLdR0v9xgXs3jEw)*-xGU$dWYR~MrJ7w5K% zzei?jUifts(pNRAo==(qT*BLSaZXA)e^$`%>a(;St+EisbNYOMJ^Ji@NwrdrL`K2f zGKJX)+K{syN`8EMI(gS2LMY=`o6aZFt4@Ni7+f*cvcK=|J$8$C+qLVE%I!{?RCh$F z^sf#;aOSjcTb0@CI{e$Z=FkZ2u2nTKUFw!Kq`|@|KSu$a`6hxBJ+3r0;2$_rhbb zK-pl6EcS=LD?IwyAKV8PP`gsnq zljF{y&$qZ&7UP45;MW67g~-a)=QG;hzV=dwCA+9h3Bz3ucMUGv#|uwwK3TuM=RcSA zUH3j+U%d+ceb3jI2~A#KcA5t|9BW|^8*Set_H{?v{)#UV7p;8`k)6+D&+J&6KCSP; zDveo6Atww04+VZ31ijtsFIo7vQW1(C%f# zUly8+s%8ELm+!gXi}kIsPHrFuf7>MFneWZ^owm@(omGf${OwwN8;oZ>y_)1)b1*6; zU)BZ==}%p4I{Q*ddGz|UD~{wf7TcnipAG`ny9U!c+lZsdnl{*diWX1_+`73N&Q!8O zJsuy-%Gi9|nyTXoC&IR+d;LXc?k3YVO{+g~X3HXoPwLXJOKtss+1H3abd75NSVi(a z90-?08)^HeSzp$OA|hN`KeRnN{SfJdBiJZD{;}7u>ah%TMA>@yWDs2!IC4B|)Yn2c z*hHSzs~&g|Yx{XfqJk{`v3V07B6^{pc+Hu&Na-xj3$y_hOsCiP0NJAbsCyhQJbil2CMq1&{ zDfkj?B675iqwLw3olv^iM%G>^-C8O{v=^zs%V11g+eSm;mQ`k=+$-5yDedj*TCG7q zw4)aFZwzo+DSB@+oUv6H#K=qgq!`S?!TNf4p%iJl?i&9e*Yj z!`^B^n6@zkp79;IPIZ){{X+aDh=%Ukr{JQxzTX8Niz3@PQ%~zJn$b=rL2J9VCRU8& zfmg8{V}VzF;M)^SvqTLGWzXIG`%9uCW62-*j>8d5=0^TU|6lR-0-VmUqtBoQ_Lc%Y1-CTKh`q)B!^7 z3A90CAQtPo&YvV&&oBLn0PiLpIFYZ#H(1&JwFLDMfUn&r^aXT?b5%fL4H~Y zeTPfN{ulUv(V<8fzG_JrR&kx&@*KFvbS~I7nfE#s`i~*Ob@NPESMG;(^d?<2-L#`z ziucp{VA}?<>|^&=uWD zSZ--{9)91e?*ga0^p^NF;eytFfaw}xmEJvH&1@D$ws$U{);~0>o=S?;I$}DfnvAoB z>6(l`f3I7jmx6g8i_B}UhN)3UM}}EZUxgbSH2&A%f2`cVh|_;-5Nn)~GhA`)InTB& zIlP#RyX7TR;h{PIhIQe~%OynAFe(GVSI2@t-sMB*S2+ zY$Vh;BMEEj61LH}K7y?goxVz%F|mPKfIErpTf`MC zlUn8zod1els*{IXnv+U8C(bF8%3g9^ zY*PGHqaj08c{LseYq&KpPxm)Q4)_kgKcYFBRv#b5ej#mCOkeZevR<4*=13U-BJ%y! zm-|{PY*3xv6y$-t+qQU_9KAmAZ0(fkw|{QS%(LtI-WyKZds$_buH~6mU`X2DuEp4} ztk8Sh<4hVolxURJ)YmrPpPTepUu!GEY=xu zVePikMCQ%uxxhl~^suHxm^6NOZ}nr*^J^8KTYK)^oxomy_6^UgJzSS>n(gQR(^fm; zDeW6m7V<|kw%a}C>k$stmsn75BxUV~3}7rlQ*bELzGEweeyf%3jcw0Pd;Yk+ z`1PBwr!!NJcc)f+3=_GvC;xOU3gn3G48`~7x|!e3zmNDdulMVstIQ675HaVagp+Z8 zV7}7FTXU%3=1YDUaQnVv4^FUk=`75&d#zZU{?@*Q^0D7RZcsItnatSt{eJ5&B6W%) zVNE-*>8Na5m%LzSpv?K}@pjIgd(HnH+3~1wb(ZH_{2i%*RbNu$A}v|ijuI*m?eB*5l{_!N zW&Sf9_dTKXPWesxqTrk7zhI*4Vr%mO*EV*V=gR+I7vCtkP_HF^jzs?E1ysmTqef93 zD=ZCbV|H8ZgNy%|J}Jaa3OE({JEwe0y+wQ_&t(amv)6L9w)_t3BAw3rKTQ9@9OnHm z%&^qH4653Eo`4cfPy(9BvVUJ7Bp|AS->ub)zlcb$#$OR0S4 zwRN199pUablnSNZ|I4pdYPNi7o(<5ihpJP^US8A~IyYuV68WLZDimKkah8eKWP>8U zdPJp{{Dxd6Z~Xt!L;Sz!xq$!2FUD&i9lb)(>G!$YJWWtWPORPUg|obvf9nIFJU+|A zEAcOkieD3w0u&=;uHs#8@Q+N9(%t+Yex6iZBJJXc$0Dza2gBUDQ3UjeIX`W8LoAM-trI%U9!p(pjOsbA6vMzz@wtTe`c(y5D@ zbl@tnlbZ0?Q1wMw#&v5Q*GbDEnSglQdMzaHr>7pG2P4OEni+dy$3joe^n`_jdEM!0 zw2od)n)Uh+zuJm>74KmNth!Vl1*gy;2)t}Xg{Gb>qu ztM`0R=AL#cR z#){sd;x3LR{BUPH+za=+csy?$w-M_PTL;U#VgsV{$7VwsgH2xD3DLmoO~fLp2LHTW zUIp{PHVXv$Zb2UMw{Jrti>Uf4Hn5J0THN#@Hn&+DS9@UmI(%mjiYmwmz{p{ee!R2= zYidKjYDf0FZP3w)6wuxMIFEmv0bKSe?CQ6$-DAG()v5ng?~4BR@?!p|;7~uz)vaUL zG$6P*(fyj5cm`aqn`O>A9DVd<_j%{|WtyzLqI~~+DDeX9 z*++!t^R76cPwp?gd)1PSQD91%L3dLl#e#Bm=?$t&FQb29?O*r6j*UBW-NI&1>&h4n-2EOd%(XD5 zqrbl#DLwpwMZJMf7e@}fzpbeS+N?M4?P9XIK|QKI%Z{f2woZxNv)sjsgP_||PYg0F zd>byB9;P!!=T{FXZG3%ITEz*`JDGBBP_y43=B>Q`IvywMy+N~l7hG}s9*IadASdE@))WtBsx;X-|GKOm zzR!LoPTuFH#be&DnX2;=CEI1pT-FU5r^me^$eccIK~08V*6t5%V6zNXeZkglugQ3R zae)efD0j9S$6hadgDzh_%=`hc<%(g0JbYVkM5yPK1mPIpQ@`^^$Ix!K-VA4NecJdJ zr>2(Iqn8yR=@g$^KrzPAhUgXJH;&H4|L&B~ipaI|1BTpj{U54u3TGj?KRI80GGu*< zZ}+{geQO|p!zRCXSK;O>?5O)E_&ycV*zlLD8oT4>s&dC8I_%N@-PzIVzSdu(r)HWn zD&y-?6NOCQgm^lSwXb{5*I(eUfOpXf=b0%f<$Y#!AT%_H>&kh54E{n>?SKWwX)p3P z`s0UocIlHNKZ-Xww4MKgyKL=>(TiMMVY#np}jkd^P2OZpRu z7qMjm4ewNl7n!M@&KMq!_x6L;^{@$IiTbA=>QC6)BL(%ND?@N&0FqN#$!Z;=VnjaG(vmAtw# zgkxt|3L?Vpc7m(=c&Z3oD?Z=CGJXSvfpAW0#g?%_$q(>eA!v^7W~l~_#DDy5n~wW` z$!9b0!KFG*Ta69P9q_1oGw$3@AxHUgIbNv%w=jmh7J6-UDw{l2ARj@Ak%Z5rlRgkV zR{49#b%#ujz(9sY~aK?>`;~d=LBtJ{IHw{t9{!e6ZLJ+_p{mSexoH1NN@@N%OLR6nCnHy?lq7EeB&F~! zliHuC_K|I|0sFBbkdjLqt4KYdV+{KN#SS`#6IDYy>!8-%InWhRNmVv*8<-%A?_U$; z5sLh*P&X(>%5730`6u7VAyX6F|m z6Nzu)8ndXNc%4~kM3fOYbTdZI?C_-Rn@lvfczgw=II5GLxOm=<$k{H|m^7hdBr74e zabdB)1$-rGYIsS`v{HeEjyD_=l{Kv$;NZ1gv><*E$kY9EP_*#Qc67}G9N0J zhPg@F6DS8B$Pr;jQ0J58qPePqrdggfv5?xAEi4{@hq|fP0m;FqpAeD&qcw+_H-NWN zHV<&_Ck#40?y(&OVF8;hXWh>{5>CpWJc3lipvJ~Cl$M20M8zg$g2i)9l4DG+Q4aIv zn$r%h1tn9iNr0i=D~BkzMjlyXP+G{SDHc5TI>KmXge88GII^Hb?oHB=X2D~gfit6p zblyZY_RG|ziv=X+NMM032c*FOtErM#3hBZ#o{Ps=@tQr5@*JhY zH%@l{L0V_t#Hk57M#7IL;j@lTAY2~+A9O{%H2IN=2Cv{HI_^Hp!lnGyJg4#9s(>RSsydMQmn%CNW-zzkuj;tC%8DQs0 zh8SE-qSgp!UvNfVh8)}g&}UIy)&j?zqM48ha|p;pg7G#+Gh1L?4tVZ&x|ANPm)AzL zb=cDu#3rSpeA!7zgH&P+d~zrZ7MNr7So6(MQr%=|(Uu=Vfol zV<|=sCLn_3R!6Eo_)O53X}0AEVVOwlL7#z70A(c5LIQdyc?B^uM^0x~P+epVnoI|U z7rBF$@gxc|Lj*V1oU5lHr^H9$+5+MNx*7z8E{Xu*0X+dN!BkdnL{jQmuRY=yj0YM4 zKX(d4w}j`P53@|*4?jyTDjNmhnFpO|^IS?xAruc90qm&NQ`EeV5ST9Ga3Ts7QfN%a zqnUZa#8e7%OWl75aE(YsQ6P}PW^>mQh|+dZZNv}fUI4$KDWZau8p)45&sFn~*3m({ zb2pTE2k^x{ISJpfOFN_Gy1k*is74vMX5voG=3-FI%4-sfEYVzH4k2?eRZ1>Md1q3j zJZ=-cam=YKIU)0=uqck-1>EaTQkLl_v;qWKz$lprq40VP73R4OItW|QMkJ>z2!N`J zG$KA6$0UvSkZ7W@j ziGhV!C-0y>_VA;;gmW@f^iHM%uONlV$X05K38=seU{ng<=z7zKvoAOW5&r&B!O&3# zDMn**zuT}y+Gfrvm5T;;9)=dqIIC_ZaO9yVmm(4%?j)pOE~6m4g%ZG5xTsplAcOSv zj-QlNlDsrRamh4@A*n_*A{V#Cf_ATf0yDzUnjR`Hq59Pyg`5|BIRu-i4=b;Os0jjO zXUxsRY+@b&fT|Wf;IHHjxP45_xiD6=LU}ZTu+?5m8IzYi$&wJKH73}!!VXiIF z|D|e2H=wlW{J?Q;skr|P=fa=jxeF)25~MyF8Ec6*gxI38&D3p@WDZ+JH`2*-lRIEq zI*ax*76zlTxs3q1){~+^b3xXaxiWpSWzU2{iOBs=`UW|bQW|MB*2XRW22m=Jv+?*> zA!K&?vWbBwdD`Ng9kh~P!RJ~nE!a3krgi$#gR|gQ4`Y<$;G3b*NJoV1MwV~=OZHqq z_*Yo?0}&k$Ks1~C(j5PeHB?OpBwbZeOJ_RUMI0zz=HlIW( z6E!D3!FH2{Fie|i0@MfIu*`78@Gr8J(R_DCBVF>f@&+)vX)#^~7^{9Y(XSu-c6$^B+Jl>u2+#m2f$P<-8SsjqOW(rYpUoI;zo<65d z7B(nC_;n%r3TSqx0lK1eWEQK_)=3BC))02Ei2WG2%(#$Q5@5Qlp08z0ZY@j|ewFXFII_arE zGN5?eJuk9bE^4??U{8@dtHPMLFw6n1;i-V?2>&3c9fZitG;2YJ9Agbxq2h|8;UWWc zt6yn)xwl}!8`@C&t;t^_-f$Ue!hA@y`= zD<%F|{tJ?kgHQ!anh^x&8;YX>BW}}!@QiY+=`mcCuoyQ95Xpy}DnGUM*#H5_Tk_JA zW<>zfbdVk<(I=F&^8xg!ka!`T8JU^wZZEIHVUJnJaS~l4*Vyb3WL|ak<+=9B_na7i*?Sop zILq4L!8B2XXO8p1=rY9c&$MaLf`94nUw}w%2B`%GL?}a1(j{#5PXNUin1;82JGp>* zU((f3=*Na6(MPlkJEbdy|Emh?0mphW0l=u)o-*nWs2$6J^e@s+kqhs&lXZJqY4&<* zooq>DzTwEbPLQ3S3yvgx>ZZ^DQDrE-X<-hkPtm_Zq&xW5xzZDe2LZkW^9Wa*$nPEK zFJM3lKuMg;4rmKSjJxQ96kfvoG@c`0nyEqbvtd98Trwy+8TM@X9@up0vcqfNVZ>7x z_zQlPnC#Lwi+0z0pXCsBEf;TNa$q_j&xWZ46@5g(ohnsnh%tg5bi%)1P%-$Wd9d22 zgF;)W4pkS~CM;l@QbhvDpf>P^neZZf#8x1ftCdR4mLG}?MCPJSFw$1QmicY*OqErM zIgCrk9R2VKcz?FCg)HOE?n3p`K-`Pb2Gk~7xziuBka!K{BB=JNprv}Eg0ddvgVV0N z?FRk~1+19unXlv7nIu$KnfPF%YuH0=Z*slnVUklJjhN*8pL2>UV~_1vsc&VjnY9|qFU&W`My<3?Q~_NDI|0(vRt7~^e$zCN)1 zo?%A=Ef8UCV|nnnH^i6+BF1$fm#mgPR)hwiOxFXdt$uk(L-lpt$Nwa#K5WMnOlBaY z@%(UYFY}F8@QtQ%*!$Kwa4tcz6ay{H(vWKfM^Y=SG?L-@ zV=8DQC9^?zf1mra_+$Kr7@@MxLqQTM8x=JViX@Y3+D>H-t1GUFIMU2; zfou+RnTAay7I->b=ouIY*X?&@pkn&7aGSd-_3ML+_?+W|5}X?9cJu~9eEj5*y8~D` zA1iteR8T}pWB|Hu9Ix!*JQxB=FNthEseDCJI}id3jD9?SaxLs*zCaPCG(JE+>Z_h# zQm7Gy)g-}$Di5Iqj|d!DnO*wMl!zBjN0<|F34v&9e5@8Yezv?B=%u315uU?nH(R-jXk%%G>GMsA@| zVPJm|PjX)-RsR^mIjhXwd8ibScvw#FP@++c<3LoV6hBE36Te_e<|jdieqJd}HWDGI z6*1-!xby5O2T!-FnW64vB4_ZRFPL59$r%Kz`3F#(fOXXHQ@4^O7h!Q_rX%o!LWpLN zpgMbyTw?CFrBtPSUX>3abUT^^79(Zd(73Jyx z4jeZ}=$Q!4x?(jxB#st@PT(}SuRzqa^*J4_$?4E$b|L!*ThQh&pf}zc@;Oi?V@C*k zf*X`~hhWV3@8)=gV~>LXa+Hriq~0~cp@FajP1tF-(P+E~uIxmGXsrnQRQz~nFLSlW{Fk3Ohi60sF3ShA^XNVBvUw=jLJQRe z9>G|lSnR|L$-HDh?|~X1{t;_vDegW;92it_)&VLDnP}_g2SDpg;=usm-{4ODM-3JsA@7bX^^3L)%5Hzt#7RhTj=$U!9xaRq!BN0@;$w8wolS~vk z%md|9!|4CY9UEL(M7b#?lH24PRC>HZ$Ywt~Wvmw56dG>mdM4EzE* zuu*XXm~;j$!3AeR7xEKrrH;RqFBpZ<5cVV31gZ2M9YR#3MNT{B^GcgaXix{~ooZwn z$<@0tO^ri=epW(|e%vkEp1pfLRNLtO8V@Vtc){YM%azq2;B(<>42PpDk#Vz1mP%Iff z{eaf!!OsonwZLOp<3f;ZcW!JqqAW^Asdcp{u0-JjtbpzN8Y{j>NASQyCP^kUj{C=I z@T^J~^9OLV4t0S=JijOX}VOybfE#@yf+ z?BmW8LB)(?M~qU24fyyu5l78?;0Xyp>lfoI9`{#v^pR5rC<_gsQGvtVDZwXT$ygI) zBxCFhddZxIa<*oaNHDN~s-UZFL#L4qDkr&vo-$~4TRp*gA~_@SMh4X?U_U-ZL5A+T&U9 z7nyV*T^!`E0LOrh!`vdQC9#GdLn2LdS@$yIjF^SlpcS3eNLAJ|q?IUpk^O>4v;RsR{RT^Da1IsjBI#CRyi-0I2 z;=-pXZ8!`pletmh?+9+0dNrVWWwc^Ck*)q7H zOdu8=XQUD&Elu~8E^Et@tFjUk>W3au(VVzcb7wpsBdLH!K|8_Z0C`;$7l#edqdIEn zfaneB5Lr=P6BEmhmyJ@sae;TbG8YgUWiQDjwgx=iP~)OxzLw_JH2*Uug|Km9tHj?C zI(WcTR@A1BHr^L!pSGL`f-%}eIHsCnH10@gVpZe zd;LJgoItlS&bYJ7=#!7GR9e0ztA=548x5du$I>Jq$emF1uu;@rWL!q}qQ1Tt$!ox- z%k|s2CI>OY%<)KgVdw<~s8cc2W>Vh~FsAcRzeNr-hSvux59owX6o%VcDOBP5@g?N< zgrPJdg54#R%c}sQ_Ty9`z@YY=nqkew(uF1=S%_xhB=q1pU|i0U~%zigASy=Lsz&cUXFXXS~21j#$IQwGeYZq_g4ifu6#}+hcM2 zPc$jaVe_iE0+zfUhK8hu@m}pQmkLKXe0&*fj+QVui%(;Oe9f?V$ z7CcD|6bxCgsSV|Pe`L|yNtnpgUW3rd=PPp0d>L~?@S%7DgI0yD=e5{n#e0PqyGSZ9 z2_aJa*J-1ReW)eaiz`GeG9w?Vdz#;EAXNR0A!h=} zokUj+IV6A=g;#v;P_0YE)_UcwBINju*kgGm03bw^ca_F+bDr>3LCdfQnIS$Z9;8L_ za)H(q(en!#5aR4mn?u0PWGmjEu2dD#mhK^&Cb!gw<72P?1`kmZ1$U{Iv58=(xk zzda%`EN?A2<6zUcls*=MVz9433uzPnlvg=Gr)((QL^s(Fy~7udE1Y~70C)W1f+)OY`SjsAYi7xi{afi#q4s*pl7CY{w-j^f9)&0a$mjskmHw`3L^(>_ndANIh7 za0E&R=i&WL{D<|*!4(Mf#Z+cJ8KELv^{CBazL>anFQ2TS7D5J80H&Umv~=j-UAO%t zi?uzPDNEh9tHPF+G7h3cyd-|vim{boe zNx8Wm%V^hDWiA5(NHBvJlG6@s1>l0ii;4yWobMxghuuH{b`dzO2Vq(kj3djhjA%h) zjCX|5K!xH9(r>s&Ij@2>g0A#h+7;R~KtjSz+l(#??KBX#^+?d((#?3DMsf+dGtmrD;gMoMc=uGOR2j4pr9=pe zZmW*c5#gpyAUI2`gOsl(h!`w~oUPhL{K2Inc`q1|ywddG5>uZp#-^|cX>~}n9Qj+m z(5)dm^#tqD0Dh2lWm)k7j$|cGZV1Z3@h-AJ{lWTS{SO=d8B8)^aYKKfR%z!KC4~3D82%9+ywRi*DR7rAV z6tK$5KxUj~VY4y{j%N^b5iS@3NvtBb;vyo}YPBRD_;7ww8JkFAfD0>0N-|`^177|iYc?cuXMVvFExsH9)#PFaP1KAV z<%iscR6Jy9 zhrCe3C`HxHlH(8&O0_f?>hkXIV-M>8AY-GG5iO{Bj>9MajEXMe?!(|$!EnCj{L z+Jlg7G}ZtwgvtL6Cbfq*aL_Nq&4eSl?N6k@MvFv74R$L7+DG5jk=S@I1dlV3Y{1r_ zRz)O@?06N++mdk&*MAtHmDOw=Brwrd*Z*y-B!k>_iv?G(kGQ-6YPGr~GpP{egAgbZ z%a}s4_$!Ln)}Ky#eXfklDF@NTs9fw*$FM+bRswuR1AV@ zA!I@(31Jkszj}(;@y=H?&FE>2gkgxQW(0rhq8dxET871ppifwWX~SsJTDgush!>r4 zM&vYnusC#Je$uhsATBCntLaoIjg-$00;n-i4?Jcp4dk-1B7zg>CBb5#G|t!0D;}&N z;!Wy~>I_1b8pZxC?u@6*hyABnq!FS-PZRNyyq@vEz|^Kvx_7N2?FIfQ_%}7AqNXGa z74(&kY^7~=s+Bg%P@+P!Y7(A=lYD(ZJ$NdP6cl!llsRc^|Koj7;8$O6kAQRrtA1m9 zgwv@u0a?Nc*%c6##Y9IiCk6S1??~iIOr^G|fYb_?Spru9U|5_(oFTmk-6gv?sziBup_qaI|)33CfZ`4j1ke}NSBFvFB@ z(l-q9DQF%Wv??+ANDw4NBCGMhGPF_5Ft4usZbivrED3Eu+6hkDlC_OG&=Y5@IRDT$ zOj&Lc3?(j*l3vmYPG>M-#D#@~!Hh}`kdnA{Wg z%N{?PSwAS=FpqT5^h;m%rL`Xg;Dfbq!(1_nM$0&$?R1BMS5`_#mmPS2yYvW=5JDR9E{RV;8KQV*X7e7S#Sc6X?IZO^t z-_~`e=v8EAh48xhGPQ+WNkmbBklbkyU=#U$ei`fD# zbQY51w1AXk%7)U1b8*ewS^{A4+2Vt6uIUsG*93QyT=+pvAJ-(O4Lx==6x0`cQV3Kh zTcc){ptV1V6W}K~o1AJ4dF|vUqCpW*-&beMLdjZ0j@&h496f`=O_7jJ=Mb z8&OxC}N;)FrOxkPp)d z4L#Y#C)dFmFPjE-eAabje>eN)4KF&-J3*O5TStx{8|9jk|NHCZqBr=Shjy`CmR#qv5lod8UjzLu~c|oZURg7PY zCdvKX3BEP%P%4Nx5)#{UW|C^FJt6^TR3jVyr;)$H*OR_EB{pzHS_LwrH;5+Dkr`*5 zP*P#v*V~8FP*iU~ixw=&KCY5XuiZGS)pBt`g;StKq8~wG*15d;2=OjNlv?V&lR*-PR07vbR~9z-mcWQnS*aH-3nRJElx25KGkbHl7e z(7^pbCn=7^k6OwLLVfZ$Fz7xBrT(JI8iFOB;SdCi`xAdi&rmTH3kxTnseiuM%%6A} z6`i55vHyp!cMQ@bXuE{lwykN-v~AnAZQHhOyZdh2nAWsy+qS-bcE9JxZp6NEqW)Ay zRGp}Z$~fn`GEbr-8C*C=Bp78J)Wl*inDH{gAOBe?>tc_UZ;Zlq7pcxo9jlwGIq`lI z(`Sh%9+U^Bo8`;2ltkv1!hX+gfId*Omv7IgTM#oZ1d{!w<1Ui%JFBS){daGDVpbX> z81ZBTXxzL?y7qMDsRMwak^jwe#6kR1jzl_L;zR^0zv$Q;kMG82&^as99*s z7Iz7Fl(;0`B#-r}SQmH-7{C(ZD&d2LN~B?9Zu<$dAvw#%dQ<>X==eLJ`J!^N{R2fY zB0j8Ibqgi39XZa;&A1r6tKpAXK_^eDISJ zv#3vbz&2ztf*@Y%EY)ZJ3=*gFQNS!9#uVnwfh}{x3=`vqokmqrT5TdJsd76o7pRJ1 z(&ZurqVg;ezf3@BeKURo(3K9dSQaYCFTw^8Qb~au`is5}b4~ym%wH^~ed;XbB!+TA zeI3j-LJ0s|tRc|i7C=8SCQ5iB4B9yd@njqr|L7M|{a2s<4O%ccB6OHQ# zg_B8RM(&YkvuXYUL*A^2^n6-Y!nVYtSAkIxaT4Z)5*W)ldKl*9aGRzSi6I+^OU{9m z9fCF@pX95mOeQL!g#6Jolw!R}x!X-6X;k?-K+139uYuz*Yyyc50V!AT{|r0m+@jeDKc<3iKR&miq<*hN7K3^Btm~Qkt$?B;3Iy-t%L(4DbEESZ+TKDvRsQ20(6e)Hl^z>{+{{L^6q{H2yEb! z0iB_;?=!dycPEc}5Q1YngA?o(2KF9MWiUpvOb6x6jzp!37g0!hx<2-N7EpZXtp&$9 z1M*p4qRYj|>)tM)(OhnG{|E*^zd74Qv$PO(P-ylgk=jADe=9!qNV^^w0Ijm$<9~)q zKq#B*QwW^;2c*ATC^y+k5&gA0QusU?F2vM_R8pxK5U!3$O*tK(Bcq9u%bEZ1H-e}qJN<2L2uNF%WhkmhWJo#DlndAg zE2EDFTq)g)B%YESYRaB?#gzz1b=)gv`JnCXj<2VsLTq9thNdec(m{9iq$)F*?@r1) zxlb0ZuPsRC#1$6~>BBpI1RhKs)_%Q|$PJDkwf10@m3ji3{b?Zd;YQ+AX@=VHDo1MoKLfE*ucX+arxR?wqWo;qq^n8HH?%8|b$P zs`zx_=;jtOac7(YnE;ekNTQT20XMOzx-+=chDVC?XOK+?Bzl4TSXt_UNF*VDfl100 zrL>k?H(tH1L|+623I)_sQKYbCNxk>|!6mFUe67%`Nxhj1QM%B>Y6dmJ@zVY%%9rFQ z{N678jetn;kP6CEVjWRaI^|FvIXaf2DtA|rCLRPHh8AaK*bBT1rEC5hrRuIzf$A0I zP6_k_IrW$RNFluo0&eBO3zPGURz~KZ6w*9&&@b`D@h}6}z_?H)%(X}*i28yya+9dM zKfNsJIu?{chBlH^I)W{X)(w&}RpyVhp!n*8n2sik}B3?<` z!25q3A^SjQH0f4RcJRmWV+TcRG@H8oflp+e=NzQL*C5COE%RT1UHRu@qxa>QTvQ}UoC#`wqFtA(SHm{`QqX6nihHi<#;jW;!O7!n~bR-V}utTmVQ`zIwB(V$UqR7xv9lAxd zg>j1pNG0Oaw?!8V|1;N#9Z`=90{1Y1?7>SbhyX@SC+EcdzCa z@K^V%W;M5_8WkE_w1LpcP$2nO{M>mM7O&sB6|B^2V2}K+M%G?RUNUS`$VcYr%$bUr zD{pxixj!tE93@7Vze)qaMOeH10Kzeaas9-b7dsYR-;3&l6gcEST}Gao91PjEQi&Hk z2l49`?hxu)&bR~yFjtH8VNxM%l zuBTwURm%Nj#m0s`g&6BH?rXrJe0*Lcd9llYglm^#KmK~=}%a@utdW2F&J z(glZV9YukoonS17Z1&1aTv5Yd^c?d(~rtDA}+(A$@H$3Yt`jreXTTR?x58%JGJ?041 z8LOikxmkA_H8xE|Y!oQ%)_>}%U^t>L3a^EiGjF)89yQF}`vNWUP@OF@kQaiCOjJ_d zk!Gx&x<=VHA4UvX5uq_I+*&8|;_r%B1 z-IGaMD~FrM$JebnnU-}v|5;|vD`b`PyghL0Twn3UDPY9Ix7pR5sgjq?+o?f!Jg!Eu zeWzPCw*2*Q;qVC9GdS-sb4z>K-@opW_`W-|^x@c?P#bTYytuPu*j(6_>2(vMtHiEM zVkO&6uyt(#-=n7w94-~I{-ZFBQFTvfin-XzcAlki7js^v@o8}GFhzB$+Gm$au?;B2 ztjNwfeMBq^Td?Tq{y0rqJd9do`Vy&>Qk#E#xa)8!IFNk&Y}tN)&3ng`STJ=P@^z^; zxmJg>=)dZHwri%1NP)1z*ET*lJ(U8*jwgE93PX6(3ZVWqGuNLsKjhMj;`lMY$@e9s zIQA)*Nau?69ZR?C*?9OcRA|?^RAt|&@ko96weM5!+4A=OaH*n|)3U9#rQP!rk9A!c z8~47HFJ=FG$N__$ zO~pf|OR_z+YO3qEfAw&!uqeDGdG2~gp4Xl6lfy-4+uTGolJxEV^g8C&n6rANnR8%x zT$Ocb`q|Q<-htV+O3?JJ-;r)N9RcY*wdl!Id;RsF)Tg}0RqJSYjADvm&eUaAp4)DEgRHF%UMSW9b7F}uIsLBEKe)bogp0q zzb2>crVmb6ht4kEym@-pvb(eCY+JFTY5XdC?l&(o;~v^NMlvulT22c$wj7w=f2wJQ zLs4J5@ADMjZ>M^-_2hZJ-I;!Gt7jiaC~_{Jr4@7pBH2EVsg1YkE^T4dfxxO zeVEk_e_GiYzJ70G&xltZ32)cexwU@8oP_dj7E_lEd?y{Gdbd@po>s+Ym@YjX#p%s# zEuRC>>K4#_!h^Pt&9N!nI?Ji1aGvYOi?-ZZJsUgnJvtHRtFM+G_1)RyTP)2`_;Euj z9!__J(*%4R?dr9ujbdjHIu_SCv^jPdn5bL(4BjcVrOON9Z?@Zcyxa<;FaU&*_g+Vr zYqhV*JMX32-!ALP%o9L>f!9gT7_tG^^*3r$t|{P72Tr|>>a3;wyT)lV%x&#W8r@F; zLwA;nxt|^DX8Xfw_f|N?kl%#zQjkz=3ZN)XvOqvNz92yI|IeH{%%7Y(YX*8-I$;q} z0TpRwI%5kX=bz9z14j?3F=^VdiD@b7O65A0oyw!VXjMmG7>_d5;j&^33QQ9W|Kd+Y zDXB;lr$`}`sK$MnWWxZcLSGHO0Bj65ZJa zUGZKK?QR~;@n^x}|004{1{Njs^BU>@kqFGssGEVKiMgGNlZn%1j*e4S8_r1Wy4>!V z%c(&4a+FiR_8zHg*06$%S>8+5JSUBc_qAuejf- zN74tD@vcwDc1Y)}pBPAxaogXMpXKA$mR^u36Q%v;>Teh2T=)mJJyy)Oe2 z4tw5&!-v3y=Z7BKdNIs4Xyj}~53UPt$ga1o>-4a9lT2fUp+ zza{*Sj9u&ZR76f?obeCk4lSB|B|1M{am5UVb1&DQ&-Txm!^g>K_;@{;y!Eetp zB;q$q@Gn#wCwgoTP1|8zE%3JI?U+>k%9BiYif zbGckpoZum?~Z@3*J6M zy^jB_dJ@EeCJ8KBj{A+eOb9vnE`1yK!UC|oR7)DqNb!MSiv7y40Ls%L&Jj*MMbY%_x!+#{ z5&)Zsrk&->72PdMkQT;9$Xa$ZGQH9sFq}QSK0MD-xYGw*mnj7heb<;qJq_m4jR)Z)~8;i#~?XKE_Iq`Eg2w|03R2$B})G!oNI2tkQC5HtE+uHHPoF zTI6Lhf9X()BYVL#)`>-M5X~rlXA6jBb=)IqCcq+?P#CK=kX6uqgj0fmniL!kIgyis z2!(p`k~NRe>;TadX%Q-zG(-ASu;6Iglg4BcEShN}C>xRK}UH1U$`1f=m9)#^*1 zGd-)kx*|{=i#EKM?Cuc?tL$vas96t&XqB%9o zhz<{E-6FIF9N}#F3Jy9hXJj3o@KRR2L`}R4LytFv_S%4pfi|(|@`Pv&4%LC;hflqg-e?c^z#*4hJNEk9QeKZAF`iv$P>UFUqbtf>*0B&)T5LuaP!Ll8q7 zGHW<4cLKkSUz29fh`uB)QNPYpY(GRq*uwLJXoPi-v+kP!IiLLTRfIF z1**PGY{a10DYUV1Rwr--Geh3fCFtP^@OA5BAB^Om?|l(%LDyB`km7)D|djLjX#s zq!4Veqy(k@>D9(#5>l4m0QB^x(5@sA|v@vr%AyK$sgMmPRW9bO?_C z(0IaIzJt1gKQjJo=x<9mI3p2;ljT54X+wL((tzk9d>A%DN|k>vq{fIM2|);h=YYdP zg~hL0x=1UX30zTT(^NnQ9N(b%? z7Tfn7zBz*bX2Chy&hG&n;UTd88qM|A|`A za&wpcg?m1zrmi6GQQD4F{SAf?^8{{>+_HVcVg104SSNpJn7j%X--SHW>INn((;%J* z2n>-c5!2mW$$Rz7Y7jm4WMC&dUQTvCeSat$n-_bCc)1y12@9hbC0w2|Xurv6k_0PN zbOlJ`Fj9t=<535#pB}gT;K6^nnPpmjB@=CCiTm6Z+ul3CpoqIL$Oc;;q~$q`zn&u) zp@l=ZN`Ih#rV)Ck#c)dFPgoG~)BXUdy{5&H5}X5%KS&Rb?AAH5&~Fs|GzWy`Xa(_T zco=$KiI!o-qAfAFb$Z~7d8I}dCmUEa^bG&?y-5t8C3FPq(T(xKZoNOD8p}XyHO*{M z(F6W1L`kD}J(xylxxKIryF+a{E>VU~Qu&4JJprDM(S=-~p_UXQbD7*|E>X$+)niHH z7=R4X`s5a5pj*ctQvl+lLbU*x`neGJWiE!7-p}L-)UpGZDc-KB;}zqg{!AbE@CO%5 zZpj+hfgZSEd@MQ>M%z`#Y-3e;L(gOg<+MWFV>2IafQ%;AH)%ur3yN`GwSVbzgBz($ z3@#qe>W(jThbH0xKzMmqDC;p7&qRof=KGuLUOInFiRUW7Kl|7JaUPvCOwLX3Ss{07 zq>7GER{ld5i(j}3oXQJ{(%TPr)Fdmb4c*`2aHvAY_XrgcHL~ zWO3cIbAdJz$pTs@C0FKa)p!aGqc%Hhz?zl&aH+Rnqz=Rn>?GM!2c)lZRmLiyAPZYJ zW+Jm3t%VT_KmiJ+sY-F^(|7D+hrL6*TdAT7rA|b+i{d8JrT~i*tYOvu(AkQI5^AUH zdrN_dfy}pyq8j6xm!AP~?*So{%e`b-ew0D0&)z-Ol6`JC-eE;ZtUEgf2ni(XnDyJ} z;?ocmteEl0q@VQq_zm%TYgu;_9E7nv?_L{pWn2%wh?iS^IX6Xp!Bxrqi#zDV8s6Q@ zgVXFDw7V!Kik%-3$*jF68rSj%d9(C0&w0zZv)1$5C!V+tB8_1~oF#nKeQ}{$;vGs? z&8Fo3_(VE-HHY9@rD3Yq>e#pe-pL%Bn0F34Ii>=cqD$lFjT9kIj;!=w zrAVF>Uh-c(7$xyn_5T;2;gh6$|B;M8qEYg%0gA(ah3Fn*g!_Mm=&fXgyw3lAjvPq{ zt7HVye`P5WIkM1yUl4ZT$p+|^VM<-3yTbY3reHFr7go;>q4$IDKUQhq@2J`m4hU## z{eQAb|4AaZv;U8$a{}P5I_?8hc!X`Q%fbrJvv=x{|N12?~&s5+4fBUi)HQ+^Aiy}s4sMibTaHd;CF^s zvLFX6SG`mWl51!K;-}d^WOg7j1B%ejGtAEV!9gspZzQH7*VR=_Lw6+PQ-=4y}UsxrWWE*n&P^mIwoVh^V%|I z;4}86PIA@J=J*5(DW%m9*ha4eXihZ>JV;hx3)Koi2+1i)vC`#`TvK=&6-pI_5g>QO zW@i5;#wqCs)0>15UCLWLL@Wed2lX)+)CL!vVMehCW|3Lo|XDKR=my=llcmz9aBn}yJw3EP8!`h@e6ghXI9Q6 z++3Tq7ANoQS2{h5M@vkA?5*La@R~qwK)) z3=~%FhjS6T*s|jp<@^!I@C<-|`rrb_;HeM-e^EB*k@|0k!UKuG4-IO3LLP8^iDmyi zRmcgZHC^VuKDb+g%|e4L%bF)yg!152@DYT(!@AH1_M6wKosq(OoY0?<|7~u$)A!L~MSfKo(ONt!iH(`9KM0rl%@XS6NhY(5NPc0Zh$E%oy z^Gu5lisyje2yB z*~etr7QUwRUkNi#Z)hXtkGQo$H{!>R!gADDfXZ_c7Lq5~qf8plXtLgyQW_N1e;Jj7 zZLKbWLFr))(@%AI@Bro*KWH9L)M?*%+07jX~pM^Au4&;D$YI z#pPEjj6#P+6eYK__@v-W`*!2{Wj0C8xxL(;>?$}LzjzngcKdpQFT+8}N&m?OwlZt2 ziAv9{+0h%Cg|)DjL@77k!-k?3vUoWqo{nbD7VLTiRneq|Gv5tvR>j?bLFAxY zY%)?1{M~$atb0_Y$WheAIw;AvI=J?V4yysQ5~$mCMrJ|8myB! zOF_kxvk$YEb!nX{MzgYn%vzPnjyQRltR?KoG$y4yN=smxhLJ~~uVLVnD$(KTSlMRn zWwJPs>KQ?~-xQ_*Tx3^8&mYj=UsN8$Mjs=ThRipR3!ZF(TW&EpRyRr}0$&Dy9cGzX zUa#?hA^!L z8d=hz@d8d`LMR>LMEIj|jv)nT#)uqg78pd&j~JhS2AmL@j}TD-66`=d;fZUh;546D zpevl(>x>*r57B;3Sc#e__hduKMrW3EImuq1&%j;87*^>F(9C_#9!2(|B%N(}TU5h# zHsYu!HMFe~7p^O-%n*aG0mJOgZ_3oVQ3R!zpkWL=NU)L$Fefs-#mF>Fr=#xUaNlFm z>e%8mMhr$+(lE(+bzz2Z(`3^SBd`EhJ)nA-E`E*FBmX6WA+8HXj@rdmVUbvpE2h40 zX4D!<1OV)Opxja>eal=~I8@{1qhmHPW_QC$VKBMo{W?S0Cmo?88`n65zo+EL3fD42 zi0P;jCcnGT|IjaY4OwujISjDc`}b()N%5BfKE^S$q}iUF16gFt(B6$pB(7@Oyj{6% z9@DKgs*j28piKQ>ouEEWhp^Ysh)vB2RBsy@aUu1wEdFJgltV;UgG(Zx1%l3_-rnAq zbKQ*Ok*9(ms{xbNao1Q~zJcxU`&bfRC@v| zC06b33K`2#NZ_s16jT6zQguBghQtAWt)X2`X&4nz)7`o^4E zpz{cS*Rk~(?H@5NsxCp9D?zRhZy2X_RZ5l?%MPTJZNq#Tv~F&i>OvB8I0h5a>z6}u zq4JQH6JD$uGAQ0|*TPXdX>E`oL*z-?UqEot^~+%lrsa`Q<$=Se55qO}$=a z67dmJ$efD!YNTC^3dBF_D@Vh;TiY|}*vK_)j^RV|SfyrW9eWtc;CsVLCS5IQ%w(&w zk4#InoEm+R>|&<#1s;YL0G?nqz4_BwmqjrbIaC(#R=?TQIxeZ<3*M|hdurO6d)9fs zOmSj2>oV#E<`1*ACUT$23I^g#JPD$GKVcbuKS#9?<|RR0J?kzCodJ0S)#1bI(ZCH~IV!%Z@r2>UG_5lPeg|wrY|5ngzV7-NHRBq1LoOJwc*}3+$M*lujj7tQSJf>9S z7wgw_8q;5l#t#~VeG)4<{)=XlW4Ca_^yj|?HrQi zAsIsD)l%(#76QGHOaW2GujkWHkmq&&`G@a&>7gFYIg?Df$iawv%qpQ+e)XoA$?s6< zt2DqNfI~i^-JyEW=l;(|`f^rzD=P8Vub^1SOT zfRy8%({fV>Yj`o(S}8!!oct`=ixYjPv)1m|(|Z(-IAY9TtpmQLG}ETczSi*ohF5l|=3 z(T-)jusjicXvLm*Mn7){^KuyTyr2e$t!lh?pJ#a9=E!;8lKbbzJ+>V&2sg(S89qV! z{)m2bZ(H-(a}YHe`L4x`_+2*P#zl2@_J6K1ht4=15`vhM1@7&xmk&_&UQY31%ywVP>!FFAOi%1et!d<} zc=Re*Dq%@No>zt@#tk33F=X2+kREEAJv(&4F)BW3($(dj>ME<4$f)D(ukHia@RmLG zq}PUH8tUDUdF5}m^=Z(tjla(S?Be-cn~@ooR=r_q*XSD>_Oy67b=sWuM6-i~F_jVu zU9j!qJ)#-7K;~F~)sB6(ns!bQ` zn`!GBZwGvkfZ&F?H^hBX@05D*_=3inZ1nzk)^Ni?&6mi9SA=+@{Z4!3Sw;iAHK&`S zr1rIQUO<*u_8B@vRB-bhgFz3dBp#_EyF2JTGsUSRr(OMjg25)F?{{w7Z4JE z*N2!Lg*#;6OPM-w*W$Bg?$Hu~EepZcy&YFus{9wr-*RFycopKfJBwF!D_+=?*jHjN zyWCFU+h#+NJd?KY<8OywxfyqkU8;={bVL8thG1~POCW+0s}R2{K7JDx`@Ycq$M!EP zgzpX@0s&=n{7<$Y{m1tIAC29g)5x4@tz8T*d|ustu{qvM-+6rzdLC|y&6To~j4(aP za?3hTG;=K{F}rrXp><=d7Os)3!eu23b0qbCR)LBE$Jn;SI&_U)NO2p2Qlj|_7RZ+y z8SUTL8|{zNZFQ~jg>MymWnO zm7|w<3PoOKES74MSclt>dlPdH^klA0|8K=VwpK0RL)Sq`Ac$~X6M4N8ZNXErW8l%r4 zr6*v~Utbe3;~b}*6F`|vbg$dM8o4pcjrp0GZ?Hy^$l&a_r^xku4f?0|Ke7cLrwy^* zjnfZoPMCSI6|Nm}$x-mDGe$6Pj??s_7dMs?EED^`QsE7-Kal!Dzpwku38T&4k5{d?_`LF2OR<43SwLQ7i@MXIQ3^M;_&w?K z$i{*kvOzbI7yOKV7i)y--5UCaqO!{*27JC6LV#tiZx;={QfK+T4$UGf+;e;4d_Ex; z*jNJzNaHwdpeGe*%GJyh&%HvA;#+)MJOIA3We<+U&hE!Z@Zoo!JzJkYhp*`Dp7naU zR<>6zYN~AY$!G&ufIgoBwu~k{MOWzqMKXY0r1gP{su58k2p{u;l9pe71%98(t!(-- zj!^w`Aky=#Pn|Gbz3^exk=`A&;Qp873g$CsW{f@B2{AjCxJL3qZ2dIu%pCc~4|`9V zsi?(oA1Dl^48wn$28+sDnlxX1gACLo8W>;)Z@p;FG#uRZA?}30g=4=znL(m9RtgQ= z-~ZUC0g+{ucwic%JjRVJ_gr>+NQ&*9UhG6GCQd;$p$qo8{}lx}YBrsyfnxar;xETy z+?x*T)&MTO1b1dFh^_=nVGC=}%k_$mfUwR*^QF%bFklYsj$DOMU_b;#>J21B{TtLC z+M60WMjxzmACy%J!T$o=NlE-B5uo6iFw@k&pX7p|4>J9Ps=pO9bD}4$xBGm}y6Yzk z97sd8R$|Z{Xmk&wz-uk+z!`-j2Gxt-Kg~phbL#}96O~8t>zD$s_P*)bn3K@Zl7k!iRHv zb1I9b@$b#}%v=1@Y-}&#{;KX4hWF2RT{ZMJKU9|h-gXuk zcn`C9()k?g4r@kUn$SF=XUuPq#XfU7RAaDN_N;<<w zma)Il7`PJxY=PJMlz5bPdE&L50}h!C9I1l#*Fixqt^aU5IxY~iODc&@CE6ep{KOep z)uRK6S%ta?PQfJ+|3{F9H}uHN)13{s>_6A=N@1sTxGv2nIyb8mRch!k7|tSh2^89d z*lcjp0<>ozuD8lcGDYcwPq|x*xR37Cuv3M4lUvxm+o}Phg|P((w6n!0IBPR#89`-p ze!MTw1(?B=6@Sn#=-gcyu!YJqi}-+*Fu`6F!~qv%xHz1%7|tXt4Ow(`b%Z^)1te0I zHm5#j>(H!|fBWvfOBq8!q3A`w6kS#QAoZ5>ir@Fy~N=hY%TOhF~ONfgn&E^GXv)TphW z*doz69U>T+_cP0WoYv#rf=Lr~MUKN^8#yq-zssf`zIKV)YIV_^dD_gQa1Sw`BgiS$ zd)7pREB4aFpQNTs*U%ImcQ-*+$&8{gWRnA)TE=+pK$@_e>X^wEU7x-y1>cA~3uH{D zKev$g;A|%@Ut|#nI`MxZ&zQ^#Bw6d#;pw)d%cJFMxmtsd0BT)TX3#dLJHM!S6IKk7 zTFod<=gMRHhG&CRt&Z3f;^Ybf{iOhUd8X)qz{Ut7^SdZ-ZQ27@k!s2r> z{uG;xVUOh0%@liMG%+vHR}SUh?n61g3VyrEhG*o4=A$Dd$ZtvNC;6(rax~MIDxp&F zRbXYOzIS+)2QiIUK}&o=%1u9k+euvAu@}h^Q?^WEtuRjC=#D?57^jvFupNovbg`Uj zHSREO*JJq_<@Yh%*j@nK$GYm-LB)p~DHlXGvb0EaC_QfkXk@gQI92L^g2zW42>)lE zt_1@lWkv!~zbjEO1@1zzAjxe9A z$yb0JLatmlu549flq78ocYdEbjQpmaZ2jl>dsnHl;O&^MW8~h#fH}>2ImF;G-kV6P zVTaD1)gZWKak6cpN5-UR&=h@!orBK|1$Ihs$Li2%!ZLc73xHhVv7U-ImIc_ds)!jx zYGpA?kgbp($@4rGB~YF(_^wE329J7}VhoO$2e`b}6&O9ShQeDyZQJ2ClV)kyg8gHV zdVsXYoz6sB!MF^H&(7L9Nim0|n4%KRLm4?WS4iFo@0aMv{zHdMFNEUyKBZT*Xo<>> z>f72xUpN?zcI)p6R|K5cu{x%}urdv$C1aZfQjG~Sdu&?Nei60~+oHfdypLQNaW{)l z5RzRY4rQo*&=US~mjlY#hmBn(2&y|83A}A?v@Ba<6%{xAe${n@(_pMcgZX1U;TJeQjpNeI)is}0k^)EWqb5dVXk1Soc} zxLn1#XYGnwkvmB33aUvHh$^zgx3-~OiEJuq9%p_|CjE?QlBt$#LiRNVatHP03@Ez~ zE$iU`iT{xrFcQ&Ti>9YQ1Uhp(s{C#q9{0Jzask0zLbDY8w@@X~=m=3v^I{&YKrn>i z7RKQM361oAf0aN!91*d1gn ztHK8hWyEMmU<{?Li<8hfh1R@5O`x~~kXywroTd%iWXCd)T#eEhdK-gB%0Vj~M)A@q z&Kb)X`#smQZ&%T4)QIlzS@K6IL2i}0c}jx7O+-zg0u{0=$u_RiN>DeWO?M}POq-Kj zB1cRGbQgMF4Xy-St?5g=MvR zIoE`E8U0;r2*Wly=d5?a+Z6!S;qHX*8nLCmE?!W|dGLD^5`=p2=JJamY(()!i3)Fv z?IHfbMUF?jkxalKcX7_=Az0MVmg41bt1quae}m0|Ysc<+Be|Yufr!}`gsX?rM0M^%?WEZ>GP@Z%08DwnGGjr}e9#a#Tv z+O!#Oszr~<7mu!cV^?sx9r#4H3h?T;=2t-e+mD*~n6Ca`>SmbvN#UKaD>lX^QcbxO zG6nN%-ZTxg3~a@vDy{i!(}iD#te`^gvxz(jeXUf1YCTT!72&rVY?`v#O5JJ|MY&LLeamnd4j49QsBn;?)iz}N|;5Un( z$P6o+14x1T@--LHj))Y$(FXcdp$g@VXstHt$JbM~a<9+hLCvO}9`tWtu>7hGjdq6b z3A9i8YV!G_`)kHIrXn!6!G0nIw!Kq^61pV8LS2uBT?Mx#0-th-vP0v`Yck08G6^s(@61Ex}!!T5zgOGKsjpCdl;Thlj5*=MoY+r?KA>;Wdj&hA;4aAf0J8_*my z&rg9dYXp9w9ECpIf<1`Azf-GP=u?E2@CR3;OHrdou_ndV8rR<}<~NyBJc+!mD|0JIelk8KJ)4`M z&ZQyRk+hJ~vDD-&*?E}0`ar|z0|T4+&0$YOuv!kU4>ZpEHA|%}PhyT6r_|*}+fSGt zh)d7poJMq2q%|NV_$ye@dJiPM`LP31Q+es&+?O)I1s4EZRgsTd68f`(R+Bb#C+rO4nv>P>zmf z9}s@TvY!sn(!HSA3s?(+pEAE{xH?f8`H;^xk zL0Gu$A7IA+_wjzpSSRFNoSB-!tNtm!y4A z%`XQTI2`MKpPJg8Uw*y2c|mrR&Cp!b-7-edrJBMFx*(W3_zt((q^cH5A*!9Fs;&k0 zk#^~C=4w>*;#*(3&)2)8^hxZ=K~CL)r7VV&fcQ-^2d(NkEuHc7;?J4Yt52tnNmL)* zBoGC{DHlm_mtMz#+aqC#dV=(zCIcl(2>v1Pp?j02C`Pg`N3jCs5O;y_wbo~t%w-lq@^VdD(ucI))0obV!GoheVGr{nyte7wGXtW7u$eLv@$X^J zIWd0cEQGTU9z}ENBnPH__}A#(u?8E2Zqh`&4J6zD-tl{7LGxqu_`g}We zTQsTJB$q$h8u=)%7Lc`&5}pSoJTX3b0YQjmfUXnnijKVz(alk{U$f zv<@0zsc3BHmBF$2=Xv!jdlIs;b#)t~93QY{7)0{ci|75hl8gk-kl*g4Z`d7*a6Bf&o9{`-vuuZ zxk27bUda!KYr&npfi}b`4B)Ff5bm&4;9ZN<>}3O5R5ER4r!=1iXuuKeI`pZ3WybjS z-uZdT1~`^!Lt-c1Uo(w8@!dSrirr+7hw3Shruu%D=a#Sf?#H9yzY5g1R^!`m*Kxo+ zAl>0bmdcbUOL{?}-qE)TvEp&Xe$6Vk_kix_*{OGP+*}(-`cLgkZ_h5QOwRWx)Eg(c z{|{9_s=v`i4O|-+{W-do>~KwRT#J_*eN%TEE}>*2(^HUQ&>xKBo&`3BTW16waG)CL zDIkMv+_NCwK(OaIcNU+Xat(-$4VO>Q>YG{zpvj9cj4*_=-r`FAK}hll874^eF$xk~7wpE* z^rV8W zbP_LPG4P+&h;Bat=Xm635O0XtKbEquh3YRei-r5+>7T2cEGYpvz zlnbusfOT||U8y;m9iJ=)l74Yv9#$<(Pf*(6_&G$5=h`nfFpB)~p1_Mj$wPPWd3n{@ialdm{EJep^z!ou%y&Fp1-XA2ok8BpWLVQn1cP@xWOM{(cglTYMqr z&t(k$y|WCI#30kFNr3}6Nv;RSM5@)8KbM%FT4Dp>OzGTtd?LlO`KNbn=uRUu5UU`M{%Th>(lqh>x?9vim89l%K@MRA7fXMWkov zV3VT|038#clgH!ZxBwXxkaKz&5PE@ zO2^|c6WucWGd=Id?n-UTm9m@RMFM1oF)Gw5i{ExT0IN{M9-nOVy1irO9^swa3+&up zHHAHs6qXd{9;}Hljm6BFeE1mH(#JnPcXu^@D6lnfl*z^!KA3W4nAmf8fxFDE1xJQ= zJl!QE65j9#tC3L3D4TG6yAh=#+8}8OQCiALoWRqFOLWEn}u6I8kD?#z(^0==* z+athjM;DrxJTdto`E$)H#E_F)m9AnJy8JPwKw>#?bZh23|8@gg){7fYiDP(4SRp}=r0xH z%F@bVy$AJA&O(wp|L_BgqSbBifD|p@XIXu`eY-Ln{JJ;+jX;QIArRj*8|Z_Jf0a`B z3@9=O5klW<=k(ziKdfjYhO8P@k6Ft-V8Aial)8izFtg-@rHZop;9#Uy`1%qAi^(!P zK{Gv_uwch#wgP0BdviIA^NRYGcl0(-6&{vx(6@><-v-)z8*HA?-TzlBojeB@A> z{QqFZqcSrYjaoJm7Idl(o4CRsDw7S$Ot3LiXn0Jfea8#Ow(lgFUnXGl$^ol4JC7hH z9pq~R&<-4v#Rj6bB*#!HfpJP3v=D+>#zbpN1E@_6a!y+2wt~opc$WnRlB%pDF=wTK z@U*g;&bEgj!8osra0Px~hg&*GZCWc5?zody;`3Dou%&=0^LXsNe11cK16GzACWfn+ zb#$Vl=W4#2q4{p3Z+2U?05^4vfB|dp4Nj4Rq8@v2&59K{wkn`!X^!%BJwby+jycqF zRJvql3dXG4I&iISSi_207~vYjY-`i48dg|!TWfA=rF<}SL;bUc{kbtbmu*TTOj>UP z3T7GdHt+xmM+4oJZRPZHhnPwfrn}HqNA<;Zez&t5A$u95!!f3e9L~T*$?@|J+CCrp zzK1#%Ux>WLxGKQD8XOiK8gB&B_?*xh+}!76WeOYvvQ=osUb9v6=Vj0`GAf zK!@2WB^Sj2U~mLXl1t$`(CKspI^h7`)jgJn$ZyGM(~J&t88#c-1)}xmZlfI)SEE%I ztGDQpRE2>X9nlNM!M;NNNA$(nf%iaAD@1x9c*XB2`oemv;4u|+Qn&-Th5Q6oZq2~t7e=+Q5q595!N7tW{ziaR$P4B5v3e938vVU#()9Xu9q`;=4xv=I zymQJ6I=e&lTP1bf0E(fOM^01a(HIm84r>5&5*mb}D%}#GAE(0eX$0h4_qKtSkm&UXYiyi3#;0Dxj!`mL?1@tq^% z5aCVxy@i>r*4JwWp77@2kl=ugCvJSv*C$LHR#sS*d55sRK$QD>87MdP3>6d@h6&2j z(`8la9oz~kn{w6q>CB;CkE>QnCDr6iz+$TbS@(8#;dj##Yug(RGL| z%(Idmk6>D#V)YDd;azrtduw*%v`hL#Bsfl75;vItf)n)wFKJHOBN0!Y8o42m!h~fH z8!w%jJQr7xO8B^BNbTsiDmpkbj)5S_}>k9 zFkC?;61YW8P|qH{+RZz%Jv2bk8W}TxhNp~?c$*P}IwYaTFl()A;;fy;o*<-ThSC@~ zsKA3QMukk`f!b8q?Gx5Y!C#uC@(_%`CWqFFU>i1~uNwHTsiBY;?bTTHUq+KDIGIdK zD1t;d$=B@nLzr-tM;tLL01amy z3dO$K| znF*MFG)i;DLGD+$OB7`Ud+I^)7}8eL<*FGv+~IkL5i@NDS)}X@XrsN_CF6EbNh;yEx{j~@BeR=Hay9?JU&KoJMI{Cf5@0JTrqELrbO>6}IwA>Q2a1Z%@&!)m{3r7KoOepA=- z^lrJe;9A7_J`BOxF}1{5;Ggen+^{n(@_uoMs7ebPP1=rEl|fz}6EYv=A%mXSdxyFZ zl^fhnSCjPWR1+s&FePaE7mwJU8oCa<%UYJP#8@|Y>4F7G^&}a7)1hKL&mJ>F&a^dB zJ4SD|cElSJXl02}<W;DSdw7C3kQWVQxdz$gP4dzQ$lbWRZg z(@x-V>c*QbvJzX(S<}LbsVZQaR-xS~Vd^@PVH2zeQS+AJ5#FXt`U@uunk$a!X6i~z zjRivo+Bu;7Gg_mnuKp*#DhYwQ!2(@VW|UgnRIHGmz!a~$Il>w?% z9M|Hc^(0!y0W2-$ycRJJ2d^V4;JQK~I>7WmN)E@GAd62k4p-NS_ED{9xO|$!diV5t zxAl4|v>|raVm1+%kCII3&`JrNW?pQzdTMBhLd8{fs8NN;HKF`XWgE4Bo`HH4q8m1y z4qojxTdhEow>3Jkhts=B0_tc@P-dxu0q|{oIaoszjDyS~6GnW8YiR6cQ<)~Gtg)&S zXl^tAI2RjBN#Ow?*bml}m8BL@^+#pXY(ME3^|Ya#&w+RC(6LZGTMk5j##Vp}&R_tb zoW*L8c2*5MyWMwGsa>QHKwg3HBc$HjdJ$m5rD*;QcVM*xfvv;F|Cb1@isuoE{i8;MCHeZ-03zy_O7;oa0>nA zt;nK+>-jW9HsZt$5|#P8q*6%+?_FVGp=_}AY{c$NC_?IhZ(Sl4EgaUG@wT-rFmTIxqhn=-T>A;4>U{zDpzk&y{L4H2^jDc-3%y_ zHV4&qXoIfDjL92xV{4k{Sj(Bj5JT6vVL;zznyQSUey&hc?qq%}h(vCU_N8LIy$7(pcsyEArv=rr60WGxX*;6a&3aY_z3x#rV2%f;_N?0=W zX(?;42W9Qj+@GFaBCwlXW(BvP|b6l!X; zB~IY$HmcjnYfSNOv#PDU2#4;+HQNVU*TOr1wZoZ0uiL8yQ1x2q-L$G?P^<9|Js_+F zpL$D1CUe!6q^T`C+*Fom>tD92g~-SoPR3g{`!+J|ig= zM?EcpWW%l%?}|y(UVA0ht*ctU1fPZV&@+CGrC8tI+IzQN!a!HGhUR-)yRY7@8;v)- zo1=Di&Rgxx{%f%D#N_Vr$q8^n<08#@i^*dL6~J3BUboQ-75qMx5wA!vS}B?}D@r$l zUL9qEB)fQ&iHRRu)oj+>V~{||cnULwX>*P`x;WQ#!@V>(noZ}{;Njbq_k}`P$CaIe zs_j6lQY4F2YuqM^jcVK@ijDjmL`jX%-Jxr)4P}j%!A8rnMzwAU#b)I)y;&S1)Q2Ic zJ?yNdFU^Okpw=@)plwiWm>7wnq(OzO5Foc2QbK8~(la{LqMk%i(Xv980QOm_R7d9d zn8kkQH3Z}MfS&@o4D%`$o#|Ar)D`Wt3Wh5y#o91moHX%M8%%#e-`;4fu6I6kDi)_h zH8s?PzsqYE(kgS-p}bMi!lY;osnbk7C)096#+hssOqS8i0?Z|ncm`2s$Rdm7b>*Qz zCl#12&0;0C!2|A;%&~Q<$6HBub(M<+h8-65R!o7HXPSa50Mg3Xr5wm=p)kM#WwLb7 z6}r5YqMSm)-%1(?X6UU!XHNKseaFzUl(VvqmW0$YJH^oanM~7O%Jfx}Yk zp-!j4Pa&g4%Qrz8kIoxU1e9o<53Thx{0u2LH{a0%g7C{`BJf^?)Byi?00ReSSO7al5!pCs0s5vnB<{mA4nuXJks+-LFD`f!a$26=6n8ISr zgFPYU7eR34L?K4=Oo3teCPA;q?3d8tGX4T__6L~D2{MPy(NmrRk4V_I!iO!eA6A#{|=9?wMeEpp6Np^3hY6 zIMH$D82DN34LTtCTpPNtBn8au35i?sqC?OotlBEt--*3C6z_wM*{apTgq@_}f;t+} zBWJ$4DxMTCtY$3W7>$}n!lgrttG7p(V;6>hNS>aisXFLUJjoC^XPm}sG-whb8wAQ7 zbIyi~wyTiC7MsP?bR)r_(@MNpug~NZzz#rwwMPSp-amqVA-_gs6o0H)eI$f<5(V8K-Ei82mFCg>(C zMj^DEcilM{NNH|0cO6+K94KRBwKV1Y)U2J`j6xyarc^Ze?S zZmaZmAGAB*ACq&4EBHd$FB>7lvXUHk{i@h0!HFT*HTR=DCY_gCU(0%Q18xzZGyts} zQ|KDRzPWi_u3`D@qHx$%V_hFUiY&%Jz*8$C@hsDXMMsHMcHB+wSkLF1OFMqM?*;*$e(X2dD&I*}Ik<&~?&8fph9AOr^P}mejH+^OZ*wYDVp$hErKc#Fa0iOw;~a6+o?P@1uXxhRq9`!8R^AXCfe0=% zwCU@J>&j=^x(JEeCNYh+PY>1dxPoZ>0Qk4#08r;NGmt{j7D)l4u6p<{);cF)1o zfSF+GJPZPqsCQpfI+d96*StTMDM?EdDo@J_HdKHUE&cIdTD7-k^f|m9BqtF#dWe)2 zjKYVNsoO-?IqDamku!kj@`~6O88ndLuQ5**;+`dJ0@mHNq_pmW!Y#+r^$i^MA3ng#vuFf{R4_HoM;Pl zjs?AOoGxGNz5DQX_xQuxR|n<&HZBs#M*$~*YdkJ2BmBuw-WYW-*m|}1{`J<|XBFMU zv+m^6bpG<$>sLGP-XA>MtLPr%=7LK&u*Xk=!~V-x?<;!GA&J!u`gPxQE0 z-bY=XmI9!o;t~G&P_XQGU-^u}+Go1j7FEC~$F&H-Y7V@pT8_*tU|ZW%@@Mp%lYRSyQC`_ABzn~iw{D(h+$W20yv+7c$!zyAwt;zY$@THc%iHfsO%E`8q7tsux--rU;5j z(y-37YJGI1uq4ixKtD?_j2%Nzby-hQW6*(UD2EJV)ley37Fu=?&B8nApcE zXhup~$Qe6oShF%SB{~gCb=s8G8==NVS3yy$<|QKdn~HJ^pTjQpuJqCq zS2?%dVbL`4$Q80y)YZndYSg;Nhd1i+&^O1t5e|n%A9`Woe7p8F3{1Tde|yIfdwl4Q zHc0gFN7Be_)fg6{n+a_Drl!}cikvAM1nBIT42>l?^g7}==N z{`f^UIUnL`tlXZ)vL_^_h8yPE)pz@vosymk3rfl>ZR6>E0@oChfqilLELUZ*uUJlT@mE`_#cHo!LE8NC}vDw&nG(LnApf|T{#AVnn4IKyjQt%*yD~AykF$P*r z5z&u@rZrz7ZNgY5zLS{_$jFG6z}ke+HHyP{13L1jV*#&%DH8GQ?(BmTu90@7x~D#! zKE#8$X5GgQEm(@?gP!Jq%;7agqK*tc>3?>~&KeMz>Q4G2v4kW$w+pDYL>ztipZNt& z6qklYp)9-vJvux@b)ry%YAjvi4sY*$89OsTdVUNp8>LeW#vx5*H2BejcteUb^l12f zb^O+BUnMRd=*;B>$)w9(BA6v5jeY=0Mdvyn96)%iV&XINNKkPg>9F+NDzI0fSs*yLYR9z6<_}G`od_w+ZDGT=@ju{t zg^`v~R%~LAol^`6F^&7@{PQB7ID@Ns-Ku?4M`$1(w+m#@LFmS^m1FtGzN%c{fb z5;lcMbhPOZvF1(i;`3bz@%echf3PYUlxJh)VrhX9BK4$RP8z31a20co#vLs1)_0Ac z&(P`(-aqjxzLP;@r+#{x;!By%jD_WFhMQdVK|nE~*H$bs`FRxDn!$72_@sI=ekW3e zr3!=>g4w4c*hR2{8eoJ$|1ojVKQ9Rvr_|7Mu_%ObxK@$UOK5pDm50J-1L$Xt(;NDy zkuXduXwn^>alG(j?d+`Y%pU6o;b_;Svk+%+4Iz2=+14(K2CM4r>}~D8w2NmU>k}*_ zqhe(D((m@hDdl4FWe!xQJV*3l1bMv3{|KBD*?-UaUXi?H({WMv5Dh&Za0V_X`xj&i z!8140;k3O@DaAc50KNq9vwAuS<_7DyBQUAEXejAm?M6At0Oh(V$uL&C$Vg^IlN;tE zOSL;hYT|RHgJ3c;47wZ2MaD?m#%c)akHU7a3y==(KQGzne>5kVmB_V&g-CwmW)!_~ z(xzZmhsvZrB1+I5Q<&)++2NtHR=|HQl9%)hc&M+6e##&JQl~OiQ)7rr4C;>qID$}H zLk`UsX^iAKKZB@!NM&VU+uA+Wc>$=2ksm|q=t#^{Iw;FV@yE>IHu|GvZ>vH4e>m#D zNw&9o6mpwPaHIRk0{6S~p4BPi?wm93e=KR7vnaR98MiuV+^Vc`b<)OF=Z*6c$2lSA z7bQZhhDrBFB^sZa%yzwP`v$q5;0 zSP!~`zvL@;v$!c+DmIMOW||!gHrMIt5+24|!90cFi7-1|M(`XcUF1|okGJI0QX`{> z>y2e%t|$<#kh*m^%_A@I2P=r1)SuTz+6`gLkpDHlK}|{N#v@@YHxwDx=Lhx^THDhCJ>ENdp2wzjl6A-PM z>u+A|AE?0kkOc?T<2hD+jUlS&q^E5VXxYG)yW0(~Emap@x27rnNpCAdydKWxI3o&S zjCYOu49OPDDLz&kt6kzJRP(BusunAR=!qle5)=;r458gzSJl>A+RRJa2q{%}dW~?k zyR6D|jAbe`qUsIpYrFt@+6<2^ZgMlbzQiLfcv_{koCk(hh#sxj^9l(M2}Eg?y;mrU zHQCSw2#a+2@hR@Blr9Ro_{r$BW6;FLIzl8O0PjP*+$ft70|NhI$XEC0hsumd z5dk#(HJkm!b@Zon;(wwWY;d$38&=_^U(1_XAZ&x7cRTR<))j`A&INHJ!Wfb?72b|T zW3uk%CIQQ%DMo`#KnxzJ;$O0b(qW+PkS5fLf!HZ*MLT7TUttiYR@o>vJDXj67_$qb z)=v9Lz)QpHMa)#si_I2<2S89~`-Hx6ik9EhJApBUcj|-xgs`X!0o+v2Fcnc7*QCLL zvAlx=*RgOESDW%WPi^}S^RcE|!_Pu*tD{vS%w;L;mNi1Njqu!Bz8*gf|Xrp7XzVWw99BUrS@1XEAf{!(Dtb`QZ4Uc(@sio@q`Y(V)#yS^^yg&b#A_d zPTTs6D4NwY!I5r*HCYF+ZsYQrN4t~Bkkcyr$!-i7V7s=LK)KCg;Z`D z&1|3}H4n24-UhOMu8XGeEFP=SZ@Ip1so$u`f{mtSJF=5jNmsBV)h(NWP?`dFjNniL zJ{f$6YfehUF`Ko#M7=r@f|HJ?4Yga=#Rq<=tcy1LflXf`Ql~4=NW6D~_wd|!0+886 zn=CD4daMJHU)j!BErXzY|?4eGRgY?)^^TrIVej+wE)d4JBr+a)wj zarUK`k+x4;*EP($tnw1-n+Q=QVUs*M8KFAFA>g799d4x;mf^n;jDF2)V{7;kbt*da zJ=+n4a}2vD@S599RuP?A-lM8dWcnih#i3g?EH}>IYP8+3SBS(eF~V0E{PVD(nReqy zp;%Vx4NKwK6l6f!VBG+JRe&pmG7=cmTd~1dethbYNwKZYIpjANxropK4AKHO)gO~V zHf}zAyxD36X`H6jO|{{}76EXA4P2)&RfST6R6vqhV}eLg;uF{ZSC$;Rgk)+5;Mv&| zEuG!092smWwgJ1i+`2Q!2QfIP@uIK0BrjV8j|}U`&30VCw;qrZwp7S3@|FKZ`;i8Z z_RvXwB~S6C{6V@=6HHN8cUiGtysR$9ze`S@u*9)uRr|4)ZInH$KI2mI5>|Wks2!Qs zlKb*UF`I&iyK(4T8E>#4=TsdQkE~Y6>I2F^eprA%=f5M%Z%4nNCcPb@&A}5Dl~K)@ z`D@vhQ!B+!z3YQgr;6{PuL+YfxpSWq)8>&gW%gYXgoFM4XC0mT0zph+kD~1g3Y#_! z28wND@1WrR%$W)%9C62l`ZxahZIe)&Vp!=AIYeU0j=muJ!aT6!lOy?(c7difm%=aU zDoOEReTYM%Z*wxNmU_9%lFcy;zt`$!cge9s06=Tj(0{*!XKkBRryx)ALuf&wbUv5J zlwKX@BvG%eSmVGXo>L8kFC?TFp zqXc>{-!V}Dy+t)NR<^LtqQ&#@f(a zMY$#|tt6&s7n_!b>J+Abjc5p%HAMJH(K--j(`gEHS=KZdB-3dO5i-jfd(kbOX8GB) ztl9R5SFd+py?s&E^kk$*Dn)0-j=g)1vWyAc2<81l!w+3`Y?;33thi6QeFkonF3L7s z4`1i_t1D`U>pN5?nmr9vmZV+MNjVgg<}}P<<fiRup>>5NOlmSJE|M7@@~)&Z!!4@Q~yr^fJL_m*ou zYocj|Zo7t}T19)bLCF=Cdp}Fz#xim4bxuL`HJGbjr0gnR1fokypcsj+LvD4)^tLQ7 zDt;lwP|&AGtFYn%N|4a0+;id#H4Y>oF2ZPe?y+G&n*l1<2Rg~>D(6ZlN-l$QqvX0C zY;V17B{FSOwo4l&Q*werEwdX-8Q-Eb=He>V)LJ!i0dK;AL1ywk7x(>F2OEDd$YHR#8}Ude6_((WqAUToahV9%vINLSE-Cx2F7Y-o+r5!SGTLyjgp@nm;~Ml@pFWmTke zk`tv%#>TyqQaC&v^0~kO)R}L*4|O_d`I0souY^S4VjUg5%Kgmle0hlwU4Wt=r>jh} zAxFY;kGfnjSp>1fGTK9)|P1Ymu5lP+$+i^bN## z42c;>|G7xPcbF9cizzq`aXm=pv-Jw(z9a{;(Y=H1C>=xkX7HX#*obva-(+rg%mwGB zSYYR(viz_sf(g6V4$-1-RBrZ`BOfMV+EkivoDH_tgg?RT9IQ$yz=z1iSVEkogc$`M zH;Pq54hrpPS|Xe~Ev2EvlrunKXc)xG{_K$MJr94Szq#zIvchUN9FRsdA3M z5LsmM4p#oNY;0YvD0Gy-$$E474|=rOb6`)*x;U zl)M&(AT>ZtqyAX{sEWqGLDn+Vd0HQXGP~MhxUisoZNbgqv>kCIuz4Fti}5Mcj#hGk ze*DL~1zEr2N?}||R34u4>xu3IxJd(`rL^u~B9L4`*txmD2_Dfws^y?b+`i?5q=;iO zgC!6rkJ|jgIkTtX$Xvogf2Aoju}hr;{v$So0hY9z`je z6BDB-E+}FxIATKRFy)coRvFPV^wzpeIi;Ap>A0rZaw@#o&7z5xMTTTC$O+Z4GDN2@ zXt*KponVws9KVobMTI@jUNXe&Z8P?3b)x#NK2)eC9u(9t%=0*!8>M^md^X+r_r3T| zF@_~H1_v<=k%_@^|72H0yAEo?PY0auiT_n|p$DR3+o?ou`YX{pi672zz!5X!h`}<< z2)Xi|iZ5!K+0x0!T>+BY!s1f0HC&fSiqFqE@r0L#zeC(M)!PKmG~OA7%}}xzoFS?G zfTG%Y0w!aoQ!S&3@E)p7E)6imTHw7&E{(S{0)P&oOD70NVKX>)>=3!DDTnFV28y3x zl>KqW9*l5c5}wle;-uwf_e0r~-!-qcOe|FR1ES{b@`D1R@R1~RcWbcb+<1d@^6>-Bl#_LtS zI1;pHYJQsC&HrdV+Q4x^

E`1r4tImKpLuX>$Ul`0}Y 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

OQc(lTxjdtq zG{dqks}75-4JC!gdRdrdkjU$i$J3?;;vJD|Yb$6B zIhq_!+YoXc1cM=ZQe*-hlK zRNM*=^XdF(G{7KV>F5c=D-j- zmc#+i>^c7c>3lq$kV_7XwV=hWql{OVMF9Xr_!Kz1>|QgI)_L)2HLBH_oVihj2Q#Q{ zfPwC911z9zr_Slc7WYTFH1JUQ7tkpo`q>$p@9N$UW;3|_z(EMX8LiMd0ys7Rblu|t z4hxWFTLaic*sXy++(U6ZE}J?7#~vbWk(nrOZT!80*nqnaOajp7IcX6Isp%N`Yw zR69u!`tAh^3MFGi-qLI?{S*pWnT#AH%(74+pLrj7$};~I%I%H_Ap}=?GbkH`-3SZI z;&hb3w9ncr;8!707e~vxNR#kC8NX&=9ia8JoYvIeR7V!Y4ts2*! zwjC}28O{rK;%Yl^0X%h$V^0_I>((`ZB^b9jW`Ed#oQB*?8Zs6;r|!DGm!NU3%t}b^ zp34Iz``M13UUEJ^WxsT@^1W6E7blz%oQ!A)Zim@XPCC}Cz#VMq@fjs|1qpt7G8+T- zb8tlmf(@ZuD2j{4V{Ogjb4raHX5IPXl?DVCOc5*&Qq#hFT(9`Azgpd}s7nOHW%?JV zD+-_+j{7)SdVD;GNcY-rz^-nuUd0j@>5nrsEDnxeQlU0)+%gd3TSvshEXoo@LzEd9 z9)vPJULjKbvuo&VdtY_Vh(SixPny#~_RrH11oh|8{%|A|In6$Z%iv zB=E}QBnKyBXdajH(NtE@46X|fM+X_F$CT=r-{Ncztc!;qAvhD7$MFg*iimJ9@_HzF zsGs0~wq+CqM5jDhcof-@n!7_Nq4A8o2%k|GXCQi)#MJ9D0*9WDjs9|_Jq&1JX68d+ z!;lrD%nVk-ax=))ImQwTJ$R>+zcJUF;3_r80)}JvFZtMe}wIlmVB0rxGRH#D}>TRR-B>C zgGUfv;sK9(NEhw`)p5bT{vpt98TOMT6pFUOY7cppqRYezq7uIAn_FmYf_lF*+T|AX z_$G>MDmlrY53Md>+g6b-TAfB-j;5J9F)meHk-c~kD{Q99RR~`33L--rxhYjlFA;f2 z7FF_)$Gg}1Ia*_@dKWY;1)AU>JYz#83EHq1uKTi9L!3>)ru(WIb2lgyal`%oRxXu1 z^sZPhZ4R9cVRyyBN^P zgVYK%-CF9tycyI=7NA3xe1MP_@aG zEN?oZ5G8%#MI3_|RH@<+VJboEqRZSiRNTry@$J4;>$qJxd!8%obqG+M#vHL`Q7TL*&G^CJ3s)14f^9(wMN?xNX3hm+ zmC?wr&0cz~ufHx@9V=Ljr_ZJ;6w?c$FYk?>%{mRS$_c zbu+kvo}JR(8|Fvz95a_g5VX?;{(w+!Q;?v0E6-4JJ{uL4<$IrCOFw>Gzo99fTz+so z;megSh*qG}YvC0OUACYF9In3Ml*YZA?PLESmRvptjWp^52Kysq7<%=j0S8y!!_QAe zG9Ev!AsAgj6#jmp9U<-b2-G8>ADUKN<53JjShBU1H~GO$jK`AArZA{osfO2*0H>D&Gbj`hw zH`J5iI4512*}->Syp$nTxye;hazz(-v<+RLr%y$94Avpt;X#1EUFin=?K;f)#}`8M zsR>cGZwwH++^OPpe~m7la4))@)Wcp3a7!Of$FEsjJDbj5Q?`P9v2b2NLUei`gU<2H zqJa!-%<*adJEdc%V^}ihDh02(@xhB}37AS3A^TEt$5W4+0qZEE3phSLQv|td_0K05dGmj~1B$i9CW7YfopaaO`-J8$<5mUC=bU^}*MP8lX?Z2APqOcH~j9Nha62 znt)PmGxRYzSMF5#TOOOr{t-LYjj*+$m6Hfa(zAoaHT|t%ACGcmV>ZRX7VE>v?OJ4b&)cxkP)d$ zk__sIZr&iRTp-4LEKc!Lsmnelt*q9ZrSxsrrqKH&q&KCZyB5+LDD*sKpH%^Vp~ats z7IQ-?r_(*KEeI1j`MH~hP@?d%H_;oLze&Bap^AKZLLYlGn25!L@#yH7j4|7|y*a>@*Z>;Gc+ z1efUj;;4}=^c)WsSA-tD0xzayg(%6ZHl58g)z8FR9~HQ~&2 zQpUa0VT(``V>+V^E?oy(Pq$2f3mvM#74Uac>+6kvuYZVvuT*B#DHDRW)arC1!FB{2 z9yN`N8OH=B=JPUmP@O6)XlgVPGFJ&4y*)T7c4ziAY` zD$AhKhMIrxgHaw-{d>Rj_x0Ybs<8>%E#as~W_fI&Mpf(B`=Hn9*7@TytwW8f*456^ zNO&7Afs0pW8I)YbPA3tx45`-hsX4TLwQH#$P)Ah|o~8%DX$1u<()y@UgZ+Y`RrUuM zAn@NgYD?oCXZ@TP#(3Z#y^IE;mkavKRp80f2J_;JgWdcL9KR3c$M*00Y8}WTPp0k1mPW=zYJT0pGwE7P_0w+9rmxW-WBhp1?`-8guKTQGe{i z!6jPun=VuN`{bK@>zn8{A(D*szcB!3z4HJ-KFQ}t=ZU^x)Peh*pvLh2a1q@b_tQH= z>ZICe-)2KHV)#;}X9o(w2M4SPKT_4mGx)>Z`+2~zej zLo+C~><-6ggh5%ED zFDLsdwlVIxqsAZncB)}Uy?WMr>+82zlLBsu1Jw8(O(mW3fTJy(fXkHCkwAc5f>MyX z$AS1y&ru(MVT60pb)!}e40Pq(HsNJ$ndo86Ed*zAXxD(pGCH4>mM_<)f8A#2Sg$3m z2e+L=wTihjehnA^m~Al&2ZV#v#EtCw-$V|WKw zC2vrPEHCZ|Of?cRC8$e_rl4alzhd0;^%?!tIO4;M5L7vw&yP#y@@fuqL0Qe4j zh&F3Q{?Ajo=rOBCf?h#WXk6LLbmt6J&jbk1_#r#t@;c_wzr8F(=8?Idp};2N(Q6x- z$_6$m7UyH!d@^lk$0OLEva3ua6r-xnp6B!90eq0LSV-IyGTq}n$XnJi0JufPOq3$& z{A$aG*luR`-{5Nag)UU)9KDRs52USu}ceCCtiba?BFoxi77=R9g+TmbL z7B9hL8NK>%W5h(iEi{>pG`21v5w_o2>B~~hVKjzF*FeVZZDjbT)VUwv%@6qLhs(4# z$xneFU$#wv+w0?}{&H=a-@(k|iZqk2X7{42haaEp=-`8gIt384;W|$qxeNy35+nm! zjy*V*;DiUGIfQp>bz69=jxPrD`Sc8b;5`%m`_I$C5dS>{*$#z-)`bW1F#uxh*C|%^ zWSR+ViM|d(%m@MO^yzUpD1c72x@CP+lqkWnW!v^?+qP}nwr$(CZQHhO8>ela?&&?3pwX#;Ns*IhP6+f9(k$ZEoy*qTOA3wcwJ!TsusIKmGh}e;P;`Iaywq2|oT2I<> zE9;NZ6IyN4$6_cWHX9F9Yd&fdhzR*8;L^93H4C*aWy*}5F(3PdMluItH#h_1-5()t zyDYs@15F`y9eCTX(k4IFL*?Y_WtB^SiXG#R7NbrTks{MYA=LT=OzsDP(DLuwlkq+K zZ;8i_cRI+J2etFzJPGjw_oum!m<>E4TSus1oUFLdlBi7#J6mKsMR?M?ICys`z*wq? zlS;@($}Hd{@XE`hb$jE%CziDDp#ZG$BV9+d3*88g1sIs7lj?o(# znB}}!Q3~k!!zyqMfSoQsr6m9v>$_#>`Fw~ZBm1dCMZt%fTW&+5H6i3+Mu%9k3};BO z^>3!Lc;i<`46_dZnezQ`I33C-m{fjSI+8w9HB3)@k~n*wFC>7&$430NL0#4D#nOd#ie zUv{~)pG4?pY*qLi0=qT!iEgol6mmWST*4P^llV}@E9qhtZ9`Y!`jgLvO0)H?gr44| zp?;&t1k=MU|GTB##Q|TYa2JeyEic?@At#I=&oNyeU_ zh4jJO)s2(OE>Awc_sQ|N02u%kL3Nok(mt5b^0b+O1lMOAbhG}+f>&O|{F645G?B0` zhT%J2kvR@dO&a)aWKO5QAfsY*ZX>iTH+3!R;h*TwDh(#e+`2tuoB#8RfejIwlN%dWy!lx>*EXl;yz zM2B=dIDWnHOk{wL$pi1gbYhD6EO!JY9dw~*>%Som*?l{?gZm&&*aSS57K)_-{@5a^ zh!z6!>DGM*5T*9o!z0nFI!Y);O(uSWSFj75s*Q!>MJFl|B~lx-lAmZKO*n5!n~h-K zrBoaCP}ZNGP=PiF0&+0T|3uJ=86_2exx_^E_Y&&5nk6Ld+;+KJ&hp}7J7}bq$)A&) zh1?%LX;SspSTm}TU?iPuKur(2>-f!HQGR*sgmA=kHI-0!JDfKtHD#6RP`@9JoCP;~ zUFjjs(rXLSj>>kYdOrJh{5pGHjW0+1Zf$nD-0}k52NXsUPVUFH9@5*3f}!wD3h;CZ zlBnlA`Gr8C{*byHR%zrFpV}JwOAqyMAwecoriru|bJ6B|{jw<^u*OFBI->m$_tNyl z@99**A!PJgMNc^);$*Bb?%Y(uAiYiU>IlmvWKaKFN_46u(~j8;#v_8c339VM=GnH$ zj@g8((~UG*i-e~TN2l$g>($W_(;`(0ml7xdY5!8tE ziUtN@>uyie0f5YJ{pXO5{6Qe`ws;ZD zRCQo5q=|MLCPP@Ph8l!|_;eC3K%x|Jfy@Ubn?E;JDfo4mx+gR`M zZTrMn%8?`^%IOpZtd*k;M%;42QPg$k9?oJNBu1j#xXEpvE$-E(7DD6vUciJ_Mk24= zh7;&d%a~cjo}-Kvdr9`wM%DGp5w(t_k^Ps*bNJ}NHmAg#sM;_fvpr)E{`dBeW57V3 zyC+%G>y%_awsg_176WN(pBTo?_o5sI5{Z-cuJN#GSj3RL`nuG49J}i^hu0` zTeS=xI{F==r=>+S+AAs3PSL^Yy643%=G@m_n<7;JkN_e_`ftCJ;{sA540DRLg`1I4~QkjH4dQg9?b6`4vu{nG&m*83Z|8x_J`oksG~fFl4Mtrse?Gt)--S2uLz z+NF)wR!=a*H$y~!rcrIEm%0RnDYxC)?JzF1o7!bSyVFaDo^EJyxH!a!sQ`uy@bK#9 zFM4vC|4YsD^LlFwC31N~D`M~1im0JlGi{U$0}rA1g*ij$pE(Gk!uQ&r8s%yEUN+U} z=uih#9s^8>2MWg1h@Ns?Pv| zBcu%d9ex{Jas=@u40R|E__%7&IAMl?W6twTA5C#2o&X;RF=R|o2918BoJiOK5bK1! z{a`ThkeCDo1^*oiO-{!{9w3pomBM45tDDlkjF_<`1F@n2dNX07lpgM>++A?k302p# z@0XWo4M2~Aynvu8=(0i_1)K;-c8oNfow z9rl$I=GXCFb3!c1%#YdQ@>e7F^JUdfHIjG?`!FD(-#1y~w?o5%i&Pw(5R$Z1DljGa z<2^Y`s4<~3$-Ika3&Shb{n`ZkEGT0mx@{eeNx`di9Gb{_QyLL}T2$Sb)Wx{I5OQ$K3M^@uxHY*jZ$ZBinJ#0SltCh#3>lAO*sjut={}r#} zZ?4x8Ep4%E1LljuCsd$G87m(J+`|psshRyV(u2D~QfN6p6S`cg=;HvXh*FIehuabI z`bfI%0etyi;<0hI1oStVs7C?Wq&G2iP`N6vE&s%Ux8G$3BwY>d1m67=V8p(pXLy-z>?HQ~9 z&5Nc~tgX^$*~H7>aR#3nn=6KjgmNYBAWU_$vvd$`jh+aaNfgf57sD*6lcdo{rp{Nt zLSzNoaT1=i=9&QVt(O&U zqxP=p&X%I)@fDhGd+ldG(ug+&@S#AI?zy_M=ju8a&-`v7B~wC#Yq>I$X0u>GI# zrY_6H8T}4r)HMz%qCcf7K(`;KHmT<&kHu>)Q0OmYGPu3VNK7z(`e2oPqD;tZY>HjR zmZePM7`Bl9XYi8@*TS76FbZcGGk$2szsajZOB+1KTX>w-QQ zg6xHJfH?Llepu+dM{*I2FZ>HobpG_|OvQdplW^=qA{FnuNq(Ji-1YmGxTQ;Y{$8}W zwR+f1L*7fxr}5=(cg?Ax zWpmBD@i&PMBW5k$cI*3w(s!eiaWl)zM4pn3cKVgBFhG@dGEKR4;-`kma3EP6+s#imKecwTq z-jQQUJE(>Ggw}+nWlPiA9_8q-HehY%k43#Vq@d$-Y^r)sa8r#!M`Vr4py}z?0Vd7$ zUlZ{hhAMiU1)@0bC-{sNnKc4CM~W26rsM9YnuT=k_Dl`B_c}#(5gF&O!B}_M#Cx0- zbDIcy`!sI5L7w0`x8A|Ny@zYi&j~fu`{sfMpo3?_2Ozw|O6wksXwv*Son#rv9;_i5 z7{d5=I)vOC02fPz8R?}RFCQNTU`u0XYo}IZt(6Z<7UYw!1!&7OAikZXrWVoJf$u zfeX*46~{E58y-+O>iP*owh|(dNw#gduhcpRBiI?ibJBJ)HUnkc)Rnfe3dmEHmwTnDxrhl7-?q?zV&S+KEO>hjw!i+QKR>g;%x9K-oR29_;i@`@(+e?~AVGn{XYiQ`R<1 zCs}E+wpgtR{}UF@UXo0bhaVwTs<;ZJLsS2RIB7Qr(WXKbCQ*|rVm}v29%6GHL2IjR zQ&X9SX))KngCS83UqOMrwk6jN-f-gH5Vavf*CNZ9aEOVt)c-wQlyVq+S=lw3NKia_+FW>yv=0?bM=)M2<^{OYvE9PBdz6 zN_)p16d3wi&R3nV>jVQCPh`wI*s(yNf{E%b#h>14^j3Or30k!nxu{zZHH@yGy~Pg8Xbu4ay!vE|0XiAl^41`4gn$a+MD$8-=h#m3 z;LK8)6wt)4c%Jxb{KDWxm?Kq(K)$C2`URqaODMdG`QUC^eo>xDqEGF2J{!|E2l zn8xvE`kQHpTB1l&??UjNytf-!YXCgtf9L03yQH8iMGYmQASJw9F8~Ad#iK9HS1$iPlo1%iPAMiH}ptZ z!BLb8B0T&l7IXxiVLD+(M;A=ayk4n2-9K*vbY6Go2X%BeW^=wfv%3Et&Tr^8ru15K z_xvVit>pj0d3)XMpZvSleSy(lY3$qe+o&q2P!hX64&0NUHlXRi_H1TEo2>%r5x5(s zP})x0oGRFU5HP6%U!hrzh1?Fu>(15Z4G&&ICMr1IeIBknfw<3NJ#<1=$7-p^$8{zW z|K}-=qS6Uio+a}ZHfc-ku{q|-4V8(#bWGJ1;3s?Og#)@S4te!BLhMjcUE;W<9-_GW zkR35ZZ!!nLfEJg7QZtc0`V&#Nf_09 z)p$CqoI&!KCv`8RSv(;R3ogR_aEkfLJyul0%eu;^2*lD@Tu3zHta;*%}w4l3Clew@pz`xRgA zTb#(48-t?!WX(%xL~ZiNU|-+!{Kx69+-yyqmnU%F#F2W^n40;+TUJ&Zl@@nIi@Re8 znp|&(&WbLgRh@b(kIKs2F^K-d-}OucWx)S^~X;mcSq4T(voKl#(+ z%ikWvKi-W#mbKf&Ha2biM^T7G`7xuT%z1HPvaky&Ld24aO2j$>tvD1bMsQZKr!}+* z_{8DI&;;TgOA8ezAR^nMxvAUXK|T|JEzB@>57hph8o5 z{0I?`bBrd)#w?}|QPvgx1gAMo91Gv%jlanq5lmg1uqpTt3_tZ9;vAE#f}QbJD@Ks9NB9DlF#3g5`;jmzQ_tUeF5M%V zJZ$(6{!ugmdgUTx&)D@e5Re2T;90Cjae|BfmaEJ+z>CaJh7UcAsF=IC&NyiZ zKLUR@*n<+Y#^9yd*l}3^_gfSrJzdZPKET}BJW8(DdWE;i!8!23vM(Xgc~8V>7K#j45N@Y-wS2p^65NYdBYLL~@7F_lIu)o1gGUE8rReJme<&9e7X|oZ zn;>qBJ{5p?J)728X>3v+{|-{(`R!_bam0!$9*}?ZPsWlIyg!$Dg-{BqsLo_x8 zk)Sw1h`?wJIPlau+tFyK}(70v*rIbAsxUWmyGL=+5>+_ssyPP`B$x}2w800Ii zCpv1TBY-tPuD=4A;)sO*(P-|XhzBGXC6hA}3=2l25@umIuhIO!XCw#Q2<{cKk&i6L zx_mQ!wP3A3K67O}N9bDU%6_J`>7XOirGY8;arYc!BZ2)5AiR|clI&3dtd7~G)S)V?eQ+am^RZ_7Ud?UX)J)NB#oa09X(1|hwk8N5RL6H;{XqP0IL zyZJF_jzd^%xXI7GzdA5_Ptx{zp9<5Xg8=BAqWYLL7)FiVTDw+8+;4a$7oH(hN$?p7^d$;^rwzDZV5uX;wodI-#$;?jI_9Is#7jg70cp_NL<1h2Zs=DMh?!xG z)1UYvT*m<2=?VspT!GIQj3~6cdEeSF__pN2KaC(D<=@I+M#kSGX|3@rl4IzE%4pLg zB;1`2T527*#W_?HP@ z?{hS6N*TdMqYvq`t1Le!d~2#Z#giuZ`0YWs_GJay21x`H$g&g{)AE~2`$i9ILVODe zaNe}&{nYpqIs5h(nFxj}=LlXhxCd6fMEeXdv4tu{5Zr9TOzH*d(+T1v{Il{}mLF0# zrp?I9Bbc;*LA)~HCBo|zik~LXnQ{EQDZ#*x({P&f%AOnwZPSl85RyZ24#fa_#ERI` zL9S?PDdnqdb96Y|B-I0WWw@~pY>D(;olk@kayZTye960C8QA`bvny8Y}-be6m$M(4v1r>9KgIs}E!_fJP7Om}-JA3;I^XEZ0MX z6=MUy8iXsV=I7Z6!DK6wi)vqW5vl;I$;l>zLi-fou zj;Eq-0g7&c8)$d7Zo0y#KPitfDNVH6X1DsC0!igNu?YY%r`W1lNe``$Din`%Ap*gJ z<_6+YW@9}2Rl>0V;3h51s3Zjy^o?&m{KM34w3Qr1vVnQ^f~*} zgp*1hz|zLfGlCszZ^8CL85h*C=>Mb7N>#Ivk=EO2{ug0~@ntLNHc1ENZ3s1fhCvrv zpF)@WUip@9O`AOb_&smzNU*^z(35$|jKCKO`!BqT(6W5ytJRo9buu`v#fYNSPL^{X zTD~NWUkxfq*d_`+L0Cd`NWUeIgdk%H$-j>hix}x{IcGpzWeQ;qxX1(MH_1UH?Jf00`&rd%kU!RzKl zYkTo(-a+4T3YeLH4(%1W2OcU1vXMBI?0hR2_DC|Du55vQZ=B*lQ3pBR6L~~mpw3e_ zpwEU$?RC)^90@b$N|5DSPeF%4>0yw#o-~GbRi)N?(uE}`SQ{2GyB=--#%VpozLZT1 znt{u@ibQ3hR_i0O)&aiO2ht!+65SRyw?E^9=UOcD_B0(n z(b66+gR_Tz5L`=b9U=5u4BCGOiTI(3Dhb|_I34&Ei!G7pj+otb!!w3Hv?t(4wxSYu#$ z9z&-S7)W;;iLj3$&L75R+*e%(Vvjm-=9CuR(cbq8CiI3N^1RH1h8#gJpEUgL7FdUL z5WyJPIB8ZTvC^PH{7W|WWoHZYZFahQBttW;c(ch{3`CPdM_U`_U$)XcZM2C8!kK#2 zRWQLl?esVSykN4QD5x7S;P1rW=@%gY4sJ;-P-(YAo}SWmtB^)HUHtvj&mQzc`G2{3 zs7xf|TAem!x$?>&QHE7YE2e7+VihB*&kh(tl=tAmL_d@dIge=ej<^dG#tqogHw`mt}gEA0xucrS%?G)dk zLwnf6_%4T>bkiDGYR}F@^pPv4J?geUuL79=bY0)uV~Lr6I&kn%CBW$;^lqKv7N+C9 zy&!Nrer%dM4HPjFY#ez%bv>|5tRrZkIbY{Bkt}dX(DQi!Z-es?NLx&ws`BI_L(*`v z*lb73kdJ2$iHId@w^ORg)>U4}!bYWDpwO-Nz{; z3$LQDWq*1%`$4j4jLMd5Db;QhP0Hx_c;xJR-|5X;#|gf01iFU7856xF*nLQefUQpX z)1FAOY8oST*u@})RC!r!igm;HzD{2HrwcB#^K;cn1uLLKE+^JhNMwac&cZrDzt$jG z;&#di)7*hzW&<;^2`n<|`^rchPLDP31rP$q8r%x8MmspztkOQPgm?;1AN|bo=A$pw zhW#e!-K%$I3@LB=t707ey>vWT-JcUU@Yq=~TWlsRU6s`|k>(nb?5(y#++U+>JUJsy zvzqFu7)J+dt*e(LPD9Fdzn^o5-a;x^Dp%jC5A09$#D#4iPB%km_L7+=sIw_k1B&mb zqeK<{1fsKBYq8%j!Py*jNC?udMLROj+jx|C@4t92c&QvBxR+?i6f#^)1q*&+4{tJS z(06x3_%V|#o{IPR@AA=KqWBV@hx50MD1UUD-KK9sfqF~Aw>2>`Njp~QW$l?R$2T4T~morB>jffOC;q^dO^qf-nsaaGu<_{!qd?3&FhhGrdPAkuyLG2l!H z&S!7OkSM$u4UFx{;5ATmDU#(6WiOvTXI1=RCRXE3b_Kswyle!juw98F@U%z`s=iL?V<46)=-7&s`{ z+rTvsg=~0_dh4+;Qx;{Z+eQpXsc*SH4-n7n1ZFw4q3NPB_YJkSSP?3$br>aZiVyWh zRrO;t*4M3pkuE`Mu##KzH64?o>2~ZUV2jNaXz4jri#038$lV2|hrSqVi15>1JMr#= z+0bz8MGD7WG~yRH8HY5uCOrOv2Wp4v7=n3q?y!fn+zYzA0&5$CFtOV=wZ{BE#7G@h z?*+eM`t9U^(S;`YD1rIf#3%v# zC=G}|cVJpb&#I0DO043~()hL(-v={cV-9(ZM>)Ws&VcIul!9JDgfqQrQ4#yL_Vtm_2oNH#UJuzUqlW8nwLV4Ws%TSg5biEz>k}C#nyfRNAv05B-)?Nk2Zz_g*#7zgeNE$ z06JO(iW_ZfcQUoDov$R!nv&UBY<MkYi!4PGgY)i~EkQ?b&OvK-9lAYP9E7&v7%_rSBbu0(b zT*4v1+X^#qobpQTIl$n04wkkxYLNJ_6d>^dsqf;d(DyjgYQX%`yX*mNFhr6)AvqpL zC(|?MMsBag06$$(hU#kFuqYhn!Y#(K^cwa~D#;uXDg`7u#f*BujFRsQM^nUbevUQ=Zz<0Ia={c+ij5R-10^n2){fB-vo_kuJ(L$Y3@yFR*M z|KM(8Bl;0LZJpw6%Q64#f4l716KSs4ss2k}H|#18XC(e<(Jqw6awD}Zv9kXa=f*;vIs}HA^t+#xU6z8ptN&DY?_R{dxlZ_)Fw3Wv z+xG&r+-FILI%|lowD!2XWW6?QnUQoJa<$tWnrQ5`)}56zkg{t#{^NP+x_4`oge*6u zPIHiG?b^oj;PG*Lb$giJezLe}H`rCFF^*i>By!;wFgU`0_mXD5oW-xWZ| z<*2j4v!N(DaK!zhe@qa=RlRbuzyr+%%<;f_iDqFM^@jsC_eBBEO4SMwJtPz>7puSf z07;~L!KU!&UTbZGMLU&~j;K|MO$}yH?dHO$bJBQ^tL|FYe76UbXS`Tz0dNBDdq`NH z!D~o}`PxqJlHL5JVp`U7`;>9sh|TVf%U!R0Lw{&e(uUc&idUGQ7?0_NRyWwrPL}V% zgyc+k*HsSDKVJIiUm=`Cl)QzBh=i&6nOx6Z7&T%bLL=Gzrii2a_YO(S(YO91@gf-j z{0VPG<%OHzq|Tf>A00|;&?enr37Ir zt|}Q+9R|+fdy?}6^24TYRyN(WmAicBK#YThHh&A!0h=2 z;9b;j%21*W`mc7J#Ri-HoRiTU#W|0hP22Ir*KT+u!2KCB?(uSzsSrw79;s;P%1(6j zML{ZZ;t8Qbb3B5>GvJ8WI9-w@Hf_MHh`&m6C`ExI_}=R4v*GYFP+z#{wRH4}Wfx*&W&@{2Hf~Wh0)C7_LosUUyEw zc%(bcVs?}Vv^XT-(|tYa#vFP&p4~%MuiiJ9`J0$o4Qi@Rt%qxPSnk(3_0VDZ8E(~3 z_jpB?fO-{nfo%5>faF29(8{B3Hl>JxF3V#H^u_@57I0yIWheo>Oa*+uNzeIWZyF_s z+iowbG=28+vV``SM&nFAJt8j_n(lWNw(U7LMDDUmiE#F7xv*?{i_uRQ>~@eU^J8$B z*-BwfNqcR3l3n_+Nc7yQky_8ZXYM5voraz2m_*0Dgk^V{{wDO5==XH2ezf*le|}mB zNvE#ZCHA@s;WYpPk<;3T7tLsm75~tmW6RJW#2*f2(NkEAUF;}31sb8l!wr!N0iRXI zv%=`i_V4iGIFoQoGudZ<{bOn0upXE(u6u6p!+fH$&YCOk*T|IUwL!9{)ljAd&hgAthc12Q9CBbmOm*l4L3B>&kEumkXrzD!!W&YgTqBy@LGvbFOuY zyx}N{tzK8E`YBk781CTWla6m6L=>&WQ~MRk<6Jw=4-;FyLNPr6HvOlTmL(tO3;uwE z77`7b)FJ*1hkvg-j!|{WQTE+DZ~x)m%QWLwI0!(VfrVcl*!&L!=la}PdC>_hrp!>@ zJxXlD{;PWjQ1Qy_quQPeYH^BV876vs;FNYSxnLGdVae{vtOzQ;Ha!RcF_iod(*sK( z7-&rMSbN92u$L}ik_!QdZ`dO4oc_7F6tr+SH()GFDD*hf`C+g?t5=PYs4RP+RH^5^ znF%B&P+(%5=s7%)JrDTRn($cs0-d<-g$^qXPt!qqfmH*V@d_lmTQB`&D*1d9F#}%l znZzBAGQfucK;5#-V0;hyue==0HoW8q0H69G5Q<(p4{D^EbH3C#P*oF@1^`dIH+3xn zj&61bf^G+O0nFxB7*RCU$PDVbb}iLs|zV9Av7JjyOd1|-F6Kl4;qo&X=k zc>@l7Q}V7lSgKq-%B-4j*D@rv(i0i#4dtm-2>N4o2I6LbOS5*eR&KZU)4Appd;K|R zM^I0F5DOd*7r!|=j@|OBu|Xp7OZgk8t>=ndN70nv#HqluPv-eQ|2-9GMxG~9yUK&N z0X&XEVn2tk!|*e#p%PRneOv9Le(x|wH?OiXP2=#cfrSfFTZe&}7-JxoH~5*qn3%;& zM&j47Z%)jD0Pin&<0<{?IefK|&9Ip;FR#&i>{xQvcK}u81JS{7we+G40C3n&xkXN{ zk;1S+v$!Wnx)Mr+@URud9O~U(hf1mq6swUcQGhyO`jqxX&FZ;=;l&BWfF#5O`0fX` zd6xl&{!y_xarWGv25($0IAw(3zsuf0Zu>4DvrI`N+N^pU5%6!B!awRWfTq(j{kB)x zLb(hLQvAmN&T9Ueoc%*xLqZ%HIyn?s_-Xw;+j!5itosDf!RbB`kZ_}Oq+H@7l@&U7@i6$vHp|D=;15iA}5Ol-a9M``Kj2aCAaXCTA za4oJTMhGfw_#tGa0g1cD+@H?_Ayv&5<(_)0*F1yCw?YJO=No;8LE6kn1mRp?_VJyw zTD$HV;_%EJAtMeR{Sy$|9DE1Q4Aib5tnRsRxU+|Rx|Qi%!@1WjR?CNzy= zogI3*7O^nTvlJrJ3PGq_hJ_7oZZT3$g!>zdG9y#9S^KA;$)eRypF0j8W1}G;O*(5Z zaF7XxKggb@;>hX5ITUI?P*7Yw93#tr@HftREwu?Q# z5Y`v#_IOfDneBplsX+!SHXW>IdkY^K6wwmF%?WSur?SYSSkkHeW6l7gs`H@XkB*A( zZQNLZ<77%9S`xTjcNAxDg>;p%Y8|QnummX8%`hpg{da!$r3#uI@EyQ*UGpu-we87vrj}hblrb9#~R?sKb9C_t(LIfNv9Z-F2JD{lqCsD!Y>o`(m*$W zHa+;um@bo-%6XA(03~TDDMD4yiR3B zeO8C%rjW9La?SOD zR+hSZGm@`JvpY>mj0d5nO}6+YWhczDQdc6EdWTiG&)yR90C$B08=bZpobe)Q_-y$5 zOhBHAt#Wh$SGHgODHJyqEBCx`(#XEAfr>c8YZVi@ZwS=; z3=8}5&PiENp2S4-xw}MMVC-%~T+Dn9HT0B;NXQ>uj&bD7Msl|OM>Gd+oDNM-^-H75 z1qs*XnSU>p4y~eqFn6}TD$xwq%JcPHe;sPrGulKk&>L%zH5u8CnpAtB8ulDp+G)=- zwWc6^`r3!Ae#Y=eucMLce8%ZQnylPjlH=Ns9Nmt1ovZn@{hk_2Ebe4o>J^0g&jCoo z&b4F?Z%mn({hT&+TMdL3J{17>VE*(6o8SOWB~wj ze1QP|1Mt7i#=^ Date: Sat, 26 Apr 2025 13:44:25 +0200 Subject: [PATCH 150/166] Issue #131-Limit-Number-of-Troops --- CTLD-i18n.lua | 10 ++++------ CTLD.lua | 51 +++++++++++++++++++++++++++++++++++++++------------ README.md | 8 ++++++++ 3 files changed, 51 insertions(+), 18 deletions(-) diff --git a/CTLD-i18n.lua b/CTLD-i18n.lua index d48dab4..5c6a4af 100644 --- a/CTLD-i18n.lua +++ b/CTLD-i18n.lua @@ -18,7 +18,7 @@ ctld.i18n = {} --======== FRENCH - FRANCAIS ===================================================================================== ctld.i18n["fr"] = {} -ctld.i18n["fr"].translation_version = "1.5" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) +ctld.i18n["fr"].translation_version = "1.6" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) local lang="fr";env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) --- groups names @@ -158,8 +158,6 @@ ctld.i18n["fr"]["FOB Crate dropped back to base"] = "Caisse FOB ramenée à la b ctld.i18n["fr"]["FOB Crate Loaded"] = "Caisse FOB chargée" ctld.i18n["fr"]["%1 loaded a FOB Crate ready for delivery!"] = "%1 a chargé une caisse FOB prête à être livrée !" ctld.i18n["fr"]["There are no friendly logistic units nearby to load a FOB crate from!"] = "Il n'y a pas d'unités logistiques alliée à proximité pour charger une caisse FOB !" -ctld.i18n["fr"]["You already have troops onboard."] = "Vous avez déjà des troupes à bord." -ctld.i18n["fr"]["You already have vehicles onboard."] = "Vous avez déjà des véhicules à bord." ctld.i18n["fr"]["This area has no more reinforcements available!"] = "Cette zone n'a plus de renforts disponibles !" ctld.i18n["fr"]["You are not in a pickup zone and no one is nearby to extract"] = "Vous n'êtes pas dans une zone d'embarquement et personne n'est à proximité pour être extrait." ctld.i18n["fr"]["You are not in a pickup zone"] = "Vous n'êtes pas dans une zone d'embarquement" @@ -167,6 +165,7 @@ ctld.i18n["fr"]["No one to unload"] = "Personne à débarquer" ctld.i18n["fr"]["Dropped troops back to base"] = "Troupes larguées à la base" ctld.i18n["fr"]["Dropped vehicles back to base"] = "Véhicules largués à la base" ctld.i18n["fr"]["You already have troops onboard."] = "Vous avez déjà des troupes à bord." +ctld.i18n["fr"]["Count Infantries limit in the mission reached, you can't load more troops"] = "Nombre maximum de troupes sur mission atteint, vous ne pouvez pas charger plus de troupes" ctld.i18n["fr"]["You already have vehicles onboard."] = "Vous avez déjà des véhicules à bord." ctld.i18n["fr"]["Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3"] = "Désolé - Le groupe de %1 est trop important. \n\nLa limite est de %2 pour %3" ctld.i18n["fr"]["%1 extracted troops in %2 from combat"] = "%1 troupes extraites du combat en %2" @@ -302,7 +301,7 @@ ctld.i18n["fr"]["STOP autoRefresh targets in LOS"] = "Stopper suivi automatique --====== SPANISH : ESPAÑOL==================================================================================== ctld.i18n["es"] = {} -ctld.i18n["es"].translation_version = "1.5" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) +ctld.i18n["es"].translation_version = "1.6" -- make sure that this translation is compatible with the current version of the english language texts (ctld.i18n["en"].translation_version) local lang="es";env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) --- groups names @@ -442,8 +441,6 @@ ctld.i18n["es"]["FOB Crate dropped back to base"] = "La caja FOB volvió a la ba ctld.i18n["es"]["FOB Crate Loaded"] = "Caja FOB cargada" ctld.i18n["es"]["%1 loaded a FOB Crate ready for delivery!"] = "%1 cargó una caja FOB lista para su entrega!" ctld.i18n["es"]["There are no friendly logistic units nearby to load a FOB crate from!"] = "¡No hay unidades logísticas amigas cerca para cargar una caja FOB!" -ctld.i18n["es"]["You already have troops onboard."] = "Ya tienes tropas a bordo." -ctld.i18n["es"]["You already have vehicles onboard."] = "Ya tiene vehículos a bordo." ctld.i18n["es"]["This area has no more reinforcements available!"] = "¡Esta área no tiene más refuerzos disponibles!" ctld.i18n["es"]["You are not in a pickup zone and no one is nearby to extract"] = "No estás en una zona de recogida y no hay nadie cerca para extraerlo" ctld.i18n["es"]["You are not in a pickup zone"] = "No estás en una zona de recogida" @@ -451,6 +448,7 @@ ctld.i18n["es"]["No one to unload"] = "Nadie para descargar" ctld.i18n["es"]["Dropped troops back to base"] = "Tropas arrojadas a la base" ctld.i18n["es"]["Dropped vehicles back to base"] = "Vehículos arrojados a la base" ctld.i18n["es"]["You already have troops onboard."] = "Ya tienes tropas a bordo." +ctld.i18n["es"]["Count Infantries limit in the mission reached, you can't load more troops"] = "Se alcanzó el límite de infantería en la misión, no puedes cargar más tropas" ctld.i18n["es"]["You already have vehicles onboard."] = "Ya tiene vehículos a bordo." ctld.i18n["es"]["Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3"] = "Lo sentimos, el grupo de %1 es demasiado grande. \n \nEl límite es %2 para %3" ctld.i18n["es"]["%1 extracted troops in %2 from combat"] = "%1 tropas extraídas del combate en %2" diff --git a/CTLD.lua b/CTLD.lua index 2ba32ca..94f1ceb 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -71,7 +71,7 @@ end -- If a string is not found in the current language then it will default to this language -- Note that no translation is provided for this language (obviously) but that we'll maintain this table to help the translators. ctld.i18n["en"] = {} -ctld.i18n["en"].translation_version = "1.5" -- make sure that all the translations are compatible with this version of the english language texts +ctld.i18n["en"].translation_version = "1.6" -- make sure that all the translations are compatible with this version of the english language texts local lang = "en"; env.info(string.format("I - CTLD.i18n_translate: Loading %s language version %s", lang, tostring(ctld.i18n[lang].translation_version))) @@ -212,8 +212,6 @@ ctld.i18n["en"]["FOB Crate dropped back to base"] = "" ctld.i18n["en"]["FOB Crate Loaded"] = "" ctld.i18n["en"]["%1 loaded a FOB Crate ready for delivery!"] = "" ctld.i18n["en"]["There are no friendly logistic units nearby to load a FOB crate from!"] = "" -ctld.i18n["en"]["You already have troops onboard."] = "" -ctld.i18n["en"]["You already have vehicles onboard."] = "" ctld.i18n["en"]["This area has no more reinforcements available!"] = "" ctld.i18n["en"]["You are not in a pickup zone and no one is nearby to extract"] = "" ctld.i18n["en"]["You are not in a pickup zone"] = "" @@ -221,6 +219,7 @@ ctld.i18n["en"]["No one to unload"] = "" ctld.i18n["en"]["Dropped troops back to base"] = "" ctld.i18n["en"]["Dropped vehicles back to base"] = "" ctld.i18n["en"]["You already have troops onboard."] = "" +ctld.i18n["en"]["Count Infantries limit in the mission reached, you can't load more troops"] = "" ctld.i18n["en"]["You already have vehicles onboard."] = "" ctld.i18n["en"]["Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3"] = "" ctld.i18n["en"]["%1 extracted troops in %2 from combat"] = "" @@ -460,6 +459,9 @@ ctld.radioSoundFC3 = ctld.deployedBeaconBattery = 30 -- the battery on deployed beacons will last for this number minutes before needing to be re-deployed ctld.enabledRadioBeaconDrop = true -- if its set to false then beacons cannot be dropped by units ctld.allowRandomAiTeamPickups = false -- Allows the AI to randomize the loading of infantry teams (specified below) at pickup zones +-- Limit the dropping of infantry teams -- this limit control is inactive if ctld.nbLimitSpwanedTroops = {0, 0} ---- +ctld.nbLimitSpwanedTroops = {0, 0} -- {redLimitInfantryCount, blueLimitInfantryCount} when this cumulative number of troops is reached, no more troops can be loaded onboard +ctld.InfantryInGameCount = {0, 0} -- {redCoaInfantryCount, blueCoaInfantryCount} -- Simulated Sling load configuration ctld.minimumHoverHeight = 7.5 -- Lowest allowable height for crate hover @@ -3136,6 +3138,21 @@ function ctld.loadUnloadFOBCrate(_args) end end +function ctld.updateTroopsInGame(params, t) -- return count of troops in game by Coalition + if t == nil then t = timer.getTime() + 1; end + ctld.InfantryInGameCount = {0, 0} + for coalitionId=1, 2 do -- for each CoaId + for k,v in ipairs(coalition.getGroups(coalitionId, Group.Category.GROUND)) do -- for each GROUND type group + for index, unitObj in pairs(v:getUnits()) do -- for each unit in group + if unitObj:getDesc().attributes.Infantry then + ctld.InfantryInGameCount[coalitionId] = ctld.InfantryInGameCount[coalitionId] + 1 + end + end + end + end + return 5 -- reschedule each 5" +end + function ctld.loadTroopsFromZone(_args) local _heli = ctld.getTransportUnit(_args[1]) local _troops = _args[2] @@ -3154,7 +3171,6 @@ function ctld.loadTroopsFromZone(_args) else ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You already have vehicles onboard."), 10) end - return false end @@ -3169,6 +3185,7 @@ function ctld.loadTroopsFromZone(_args) _extract = ctld.findNearestGroup(_heli, ctld.droppedTroopsBLUE) end else + if _heli:getCoalition() == 1 then _extract = ctld.findNearestGroup(_heli, ctld.droppedVehiclesRED) else @@ -3179,24 +3196,32 @@ function ctld.loadTroopsFromZone(_args) if _extract ~= nil then -- search for nearest troops to pickup - return ctld.extractTroops({ _heli:getName(), _troops }) + return ctld.extractTroops({_heli:getName(), _troops}) elseif _zone.inZone == true then + local heloCoa = _heli:getCoalition() + ctld.logTrace("FG_ heloCoa = %s", ctld.p(heloCoa)) + ctld.logTrace("FG_ (ctld.nbLimitSpwanedTroops[1]~=0 or ctld.nbLimitSpwanedTroops[2]~=0) = %s", ctld.p(ctld.nbLimitSpwanedTroops[1]~=0 or ctld.nbLimitSpwanedTroops[2]~=0)) + 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_ ctld.nbLimitSpwanedTroops[%s].total = %s", ctld.p(heloCoa), ctld.p(ctld.nbLimitSpwanedTroops[heloCoa])) + + local limitReached = true + if (ctld.nbLimitSpwanedTroops[1]~=0 or ctld.nbLimitSpwanedTroops[2]~=0) and (ctld.InfantryInGameCount[heloCoa] + _groupTemplate.total > ctld.nbLimitSpwanedTroops[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) + return false + end if _zone.limit - 1 >= 0 then -- decrease zone counter by 1 ctld.updateZoneCounter(_zone.index, -1) - - ctld.loadTroops(_heli, _troops, _groupTemplate) - + ctld.loadTroops(_heli, _troops,_groupTemplate) return true else ctld.displayMessageToGroup(_heli, ctld.i18n_translate("This area has no more reinforcements available!"), 20) - return false end else if _allowExtract then - ctld.displayMessageToGroup(_heli, - ctld.i18n_translate("You are not in a pickup zone and no one is nearby to extract"), 10) + ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone and no one is nearby to extract"), 10) else ctld.displayMessageToGroup(_heli, ctld.i18n_translate("You are not in a pickup zone"), 10) end @@ -3904,7 +3929,6 @@ function ctld.getCrateObject(_name) end function ctld.unpackCrates(_arguments) - ctld.logTrace("FG_ _arguments = %s", ctld.p(_arguments)) local _status, _err = pcall(function(_args) local _heli = ctld.getTransportUnit(_args[1]) @@ -8315,6 +8339,9 @@ function ctld.initialize() if ctld.enableAutoOrbitingFlyingJtacOnTarget then timer.scheduleFunction(ctld.TreatOrbitJTAC, {}, timer.getTime()+3) end + if ctld.nbLimitSpwanedTroops[1]~=0 or ctld.nbLimitSpwanedTroops[2]~=0 then + timer.scheduleFunction(ctld.updateTroopsInGame, {}, timer.getTime()+1) + end end, nil, timer.getTime() + 1) --event handler for deaths diff --git a/README.md b/README.md index d413e5a..d0b4d4b 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ This script is a rewrite of some of the functionality of the original Complete C * [JTAC Automatic Orbiting Over Lased Target](#jtac-automatic-orbiting-over-lased-target) * [In Game](#in-game) * [Troop Loading and Unloading](#troop-loading-and-unloading) +* [Limit troop Loading](#limit-troop-loading) * [Cargo Spawning and Sling Loading](#cargo-spawning-and-sling-loading) * [Simulated Sling Loading](#simulated-sling-loading) * [Real Sling Loading](#real-sling-loading) @@ -924,7 +925,14 @@ ctld.MG_WEIGHT = 10 -- kg ctld.MORTAR_WEIGHT = 26 -- kg ctld.JTAC_WEIGHT = 15 -- kg ``` +## Limit troop Loading +The number of Infantries units in mission can be limited by setting the table below : +ctld.nbLimitSpwanedTroops = {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. + +If ctld.nbLimitSpwanedTroops = {0, 0} (both values at 0) the limit control is disabled. +If either value is non-zero, limit control becomes active for both coalitions. ## Cargo Spawning and Sling Loading From 7286a6a01096515ec41d4aff3cc9cd0c3dfe4515 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 26 Apr 2025 13:45:31 +0200 Subject: [PATCH 151/166] Issue #131-Limit-Number-of-Troops --- .vscode/settings.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..ad4639e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "Lua.diagnostics.globals": [ + "ctld" + ] +} \ No newline at end of file From aa8bc78b2721bb9e6a16fbbb522881d88211ef98 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 26 Apr 2025 13:50:01 +0200 Subject: [PATCH 152/166] #131 test.miz --- test_witchcraft_#131.miz | Bin 0 -> 75811 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test_witchcraft_#131.miz diff --git a/test_witchcraft_#131.miz b/test_witchcraft_#131.miz new file mode 100644 index 0000000000000000000000000000000000000000..8f2671594e957fa97d14e8f26c94b1b674e26884 GIT binary patch literal 75811 zcmb@sV~{98vn@KdZQHgzdu-dbZQC~X*tTukwmo-$=iK)q-i^5D*Qy!UNiONgt;)NbJRs|19R;7UhjO^0D$KEg?uew&NA`k@JY}ub40#XiNNLlrPA}Yt@vE>nx;d@gz^~ibU8UlCyG>jq*3sfI@OiqyygU zXg%QY3x0jjr z&-MB&fYUvV^nvq-HCEbdRHqp|y5s{D&0HgT+UjW*k9Usm8 z=MJn7!ySDM+Eq^=39~KW^pvFxtBJY2aV0Rv?vTD1HDGa@uVpS92rl&7KXYdLqv-c- zU%8dMLT1{hk)nz#NBmjR0$+~=|~RXq{WI6 z8)i4p^{(K2Ro@4M5;%BVANz$X;U?ET(N0>vWf;m@;T0cn&9J{&3yLB64CC|>$EO67 z7<_ST8ID)04k8}7(!ky#ab+4WW|mcU6d5FxV8xT>Ok3co(u=lLptUE+F){fSZv}YN z%*EV0W*bvhZ_YqmAwmdi4jn71ko4=SR6l|%qs6qFKN5Eq4K440hGyh56D10LIrPY&YDC(efQoH{rZdxE1Ya*Ma^x(#$bakFH%-{>$05XB zy|@`9SsW>Tz@(d~n)ZJfZDS1F5v$OJm}sPlBxj7VJ-}S9mljqq{lY3Eot_P4c!gXv zxc9YyXpOh>zGGU_+ zho}(B>g^nE)?*_k^g~oNBSKQAu&13RXCvxr*uO;PtT!7-3K&Rrkl9VoA4a4Q)j_Cm z{Grl5GLn`)DgrP_8=Hu$df0N+rnZ-HryY94kz1)Lfw+44gn}@x>L~J8hn-!xC2d%xAM363Z~Uastn2-bb4ZF{REJ5QixD*KUT@;} zPmwRhLI3dmPdT4x&xyzac^DGTh!7iPJ#Hepa5O;FM)uF zkm$n+gT4l%de8&0n;Qq6vLzdRJ@Nh^@}3h1y}xYG9rDO28XdirW3Ty-3ka`7jkd(` zfP?~FFv|6V%CRHL@jy?ZwuDNJz%s4WGOf4@?NLQV2#!i~T}cJnK!qitlnSlk-wILX z>4BOGjiG9b)CzUh?twgvH3z63C^Chg}%XB8s0UE zI)Zb9=$Hs_ect+#*a^E0H$D8Wquy3Xz>KW!7}JeyJ<2LsOjYijNtfWz>a*IewNTc} zA2x^ne*pwPl99m2kV3rRBkGr!a6(X*(h&!wDI-SLyOdXo)S8LRv#41ssZ!E4wa1s3 zx^u(u1@g@vMd*I1lK6HkRjX0Ms0r(rI*^X4_yV-$0voL~Ta;+j7QB+eEEs1W7UVAt zmc!^Z$pY})kz;O(d+0`HO=$QQ&ik(RIlQhv`um%oqm~SO@({dn7YO+ zIJ|TI0w)Bo0JhwJ9bAxi4|{G0x0R-wH|v+6t)^ES{z?qRu3ym^wd_kE<7Hz@ZvA+8y-05&{U=)S60}#gAq%USp(GU|}}zH-*GrmqYdAIxj)@t0@Oi_C%kl z;99^R6^9OYc$R+zu~Eo|St?uccnq!KAMdM=#mBCy`n0hoz7gwVB$f8V zAAs`45+vUM9`=$@hI*GXgqUdhpql#*C{;@MD-`?U-ukhPg|6^~mfIWdlp4**h8<%S zUZ+QP|J1X5MNYKU@lb18@91KoBkS&^Mt|C9)4o$YcN!45dFC&-Qord=w3nPc0_+Q32}f^N zH(@rG9wy#g9}zi1VBCd&hU$%V%jWJl%;g*u?>}p&4Dlxhj{{3L2?>15M)l4h3KXJQ z2*BL#N#6?c{KcT+?@AtegKpCAL>*fU<71!bluoQETjLX5`H&t}K74de7@PImvQ)fp)U>ajaQ)cZs z5$ca=(!8dF?yKbV@vur5;ne1##m1nz#Ujv_VyXY&zN#{arV9h5)}?RBV>@P1OETV!QZ=zhvnTj#nF}J zpl#OQ`Qn*-=>x$HG-7bVuXoY9MX)DEKv+cUsJayQACjOvs-QQgYH5Q@1SzEn#W~O_ zq7bEuVfH$3q7I66)frbnMO27Xr^57;L41Rs$0rJ_%I0eW>*aIvY}pyal@GN; zEMw1}Qz0H58=gBG+I*=2(rNm^CsDXd%W9*W7NHJAE}*(<-afc0U;Uz>fa(hDIQTe7 zYn&@!wj1yk7lt|;RBK0P{m)N-8P5P*92l!Y!)qMel^s6y&hEMw_|WDe!)rX;l^Z_w zSEsn0{yJBcs_vq}jdfhBHHjT)t5FRGQFc>|f3{*F8+XSmSjT0`7_6TJL$c#JvH zR|ltB;SqFG)<5*=yE(etg}p9{!eiV3zxKVk_wP5N^%N0D=R4pX5C?yT!G{lH z+abhVV_d3^LfT-6(QWUkDGB))7@)*`i5SNL@3DE<2JmQE%@KFdF+Wxp+_U6X$D_;E z16gMz8w)ql3|43BQd|>3^43{zko}ySdV_Z?Z6RGBA;AaA#{JjI0Gq0Q&~` z?M}~w|7Te&Z;IGU1kLwP0x4I?BFVugNSd!*oX6O9EqBS%Mp&oMzM;}ZotrA`?H%CO z1#wG%M=cW`|Ax2?Zk`U_-B#?)EK&3fJbP!Vfe!~tO=3geb9er0;Lpe7A8@TwBSL@SGjuNJ4SoL!kP<4EQp^R-mz zwcjFbi$E})BdjvFh@>RGdw>htGX8bE2byaN>R=p)ioqQ-ewDB6SYIyOZ)dKL^GCdI zkg}x@vg|WUWbNOELSM~lIcfM$Izhnqi8$YZw@vQPbFa1*eMoRN-ms!PfD3DP8{KrZ z!N*H$o|y+CZd?!-E{F?fgr#$Wa$Sb{#-M#gk z&QgP{Qk#Ks(;B&`0Lud=%Om=;@_mgVqsZmyu^1QWKtMn{- zI)OPu2ezt%=Ixg5P6wzpY%vwhu>G~Z?C+ii{@;c+O;+PEjk5YGS?w3~+B!U>b~wA= zH;!$N-gVTdg8IAzwiKFS6wxV$WtyRDj=vtS*QdZ9?5}iJYc_>dJLx+llsBd zdD9hLi4yvaVD_~H_4@rV#I46CT1&o^B?zjnpsPf)0b~%ca$|Q=0fNN?9069HmTCU| zES}Ouc_*6xvMa*9qov3Ry+6GNGmrhh0P<4c5U$jBZ5{{!00cn*|Hl6}wgUYhTVc(} zU`sC|Dki8RtxRuhVdVUex-f9`kQ$e!8=sn$qNz}>Q`xIHIfzzu1cdS^QynQQMk7Zz zLGv&ER+N&8L~@E0Mv7`Ylu0%WuuMsIjz>j5MNcs<9mz6G5@wU&icTUZy&`pejBop*Sj|Ebi+b0sSx7pC};Wj{iWN{OkXB*cksOY#Rf6 zMH44G7e^zLNf{c5KeRLSw0~q({vY5#Q11V5bCabRpPUY7Q4Rx{Bs(%kNj*9KK~8aQ zvqW`vK~;QIM0r?5aYX!R|1TgoWgt<)|CW*dAAvyq)4CZrnwZq}ebKR$-z&{uYVIsx7 zU!K6~UI=)gPgxe^o@@@R?ZBjllAGXS);ixUcvyO|yktkV%?!z-V!9tN=83&r5g)&I z5MqIFV*dEP(*BY(XerduWXR;X zy>{7#o6TXmPZ-&sU%mXq->tP3|7`tMcrWpC+60d~{%BwGqmfeKD!TsM3AsLa0o~@_ z@cm@{Tj+)R*&h^h-E=V{^jolf^4X1zoge{5L;yrYln@<}?GF*|A1-ke9FZOP-%@gf zv>=FxfCveb36a@9D*_@UP9vhz|1Fsjq5rqk9xk%L82-;&LIevtVeFp=h=>RgGFhNA z?muU;fpgEu|6iog|C)s)(V-)kPzM|lAtA#4|COv4o`bHfdw=WRu|&502ny}Gb~1Xp zdA^|kb7BbxnPj+t005+f{->?~AAZr!{y%5bsajLY>97rvuhuU_%FOG}1GUq*U@DDN z1j|cg12gkq6lfwrM5}mx0Qm;8=3ib|AQHj&^(ZzlQ z7GrRHUvd&dCiQ|3IC>~rY&f8HQO$9?q$iYxKe>BB_H3d>3HT1t-vA){<;`4A^MIf? ze<6){NGKE6@{kHx=DBDRc=Q^a+2=Op5#kZbkle+?=dz7tD1?+T{!pYvH{eR*!V!TD zGYMrpwbJl(i~@;K!a;SHbdtqdAULHa>gA(Kk(xCPyF$+$Wqh)ZP)M)gb?z2v@ee~3 zr}07I2%4#Z*6_s=AxJP|os&j7#)CMW2A^~jj@-3^LHrRk3V6qJ(gV>?L}7@h9VZ?E zz7od>i;Rs^YAWGq()jXl@b&PBUA)T{J*CB0v_(y%AQF^4uHRQuh_!&8ik6gI*iN>{ zu6;fYE;jyQa&cq6KCA@@cZGP9&j;SX%tKJ;Q*04k5(K^8CxIM7D(S?zmX`OALaYXX05G# z)ChM`u5mrT!<8^fraqWUY)U`}S+abZUQrgvlr@n6mD+AQvl$x@-YW1SI@mG<$9LMI`qf;%Z(4A-L5TWBsQZ1g z`P-;L1$J6RBb~6E6FW~ew}0{rFCRr&{ZJysytoT4h5?WS z?F#|`DeVIgU9 zN+$7O$?B42CuK0v%sen%m>lW8&CM;`w^&u2ieqMDsrtJ`w!z0#p{G{+V@DvrzU zzr>ZZ6eh%K&l;<5{u;q|0!4Azwy5*LCZF7p`PUu<1k#2`dDm zJDjOJ$q(1Auoq7qkA8g)2AqQXfD$UYTPjSW)J25H-oI4moqG7)INQ2S$j`PaG_B{CJ7uhAc9JulSUWtPz*U_lt*8u=Hm!@n~gP27Y)vh<{xv zn$um53gDpGm21tl{k*&VJ2%FAa6L))@I8ZaEIb{Du{LV>4%HOa4K%>-EJtPhUas~H z!3E>4xtU~Jud|bX1aocZTai@brc?xcouyFkUPAm_jysjJ{|waNI6HtjiUl7YTR*qF z-6SJRKy;#F!%CIPI~rjWS88Yqrzff8VD?o%GB}^w+4i48s7QQFNcfZ2V_vQbp%D|f zV=)pt&Zm37I@E=q_xSpHxG_K9run_1Lx=Ud`?@j94TKdfJNpQ!xm@8XpvX6gC!LOt zcn?di+k@HGjLc5TtbSZ>e1iE%7?&;_u9R1J<>6ho zCaM?g7&2y|`lO>P7rALU&sl>LKBN=l@j376Sz@a{!j^)~$s2k(Dmp0~RzU=8X791; zI@fURoVsUWf)!W~S0tD>*1wl4Av%qW)0g5cb)|E7l%CGq^s>snaO7R8TiZB4)tDU< z9QY|!tHt}id!hVfAC=^PlsLP3aA*3mQUByM=#4A4c0NwdZz1lP)ckekGr{1Sy$D%? ztx9`0-^ruqL(=0lu)!#*eoYH>)cdj5(x{fRik63kn{n<>%I5;>UtySr!4?hy8Da0i zdsr@le7=CPj3EwRqSc1HHp}(NcttgM|X;9QG;0? z>%pngNyQQANdeK`=JnFgwgk1ip&HSKeKe9T2(W~Q1%LZu65@Nt!oMhO1f1 zLp&3Q{Y4Ny-mert0m&sCs1e8ML^JQEEO(<0Nz~(e(r#`d`CEB3BRy?ryKzW=m|-@| z()|>1VaG_rb9uK+k6nn`vXv2~wXf0>@EWQz{FgL8;o?f}veF}l|C9bdf##+O#1hND z%*B)Fe+o2F{{@==pVaW*Uu|bPYZrs7ZVM-D@kEBNj5k!?Cux8>!Ek??^`ho3se}$w zXHdy??!mB0b0C>I8Gsm3U=qRFm)2g7r34k7ZF9w?=u;hT9HNfcD(e5Vxa#VnqU!3A z%~kNQ`AwGDMQbcvn3Hqs1X=NUgVdNu+r!G*!fuk&CF>86Kj6?}v>=PI3p|lUf^2x? zz%=+KX7gDlI61UsPdV%d=L}dQQEx0WNj#d_qtI1JvgqT^=`cm2XgOp6M0zu(hsKH0 z=;=)ya)sW2Du?fN`+Y%~LH9^jrAl#JK9a= zT!);~LR_(@Yu7>dB-k55Ho1=mXflnWM!8?FOd$`0AOLX&Xo!2mES-V7MIe>*PT6g0 z>ne^JQ`{`WNpES^xpb;+Z7fBDFr{)q4b6nuQN&{oJh-%n3Jd+uw<6~nO%th0iwa_m z5U{ih4xUN+DLnlixh6{LKiaK8%@8W3;{@Z6?md=s0tj$57_2V(XuE}{PMTllO+(Go z7?KT*jn45ShiC7)&MCUZ3_^X6?B`EJehcOT2T|5N*?mF;N96PHr%}UK<3ZqN>6vax z?kut$kHkDY{b%J9Gn1H3byEA z@A^#qZDS`<{qMldpfa--dVqDW9gUyk9E8%{z}etY9*I2ZL!5h z;rnt$b}E)!DSzWQo}Dvd!|vMGFa3I(Ut8V4?RN0-tZiP|{(`1IQ?I@H7Xb^Lt0$#6 zutn+vp7?_<8W?56>W31@d}HPI1neSyl_Av?1Q`b)CmP*iV~>01PV*Z#9>es^9ysJW z6Pd>*CSVcBV2PDcS48HE*Co_0+;d>{CDJFFi@6loDGf0T42^*Kwh>@spFhz&E&^;| zi@-=&8s?aWef;T0vtSsmQz%gCx>hVkU8m{);3ySjo7LZxPSUOp`=dvawBAqClSwuh zx9Hs84spT~avqs)u%&j7Fn;2}BnR}Y3v`qLbb0EyW7`r8nJJubJrYV6q|_W_znk|1 zMDA~svEtE|DP-Cd%!jxQ>)a23fO~ft$52`PgNjf}GWX7?|07pROW)#|HpOY z+(hW#t4X7A23v6ROZY>Z!7HVWR3 z*n7me6ZVeH*SPoKa^>51*DeMoKlU!=qcuGWdNghWdGMAPO>OLofAFLb&w<+oL_GRi z_?}0$3Z1D%wTk@LG`Snhj*FT7V=v&5Q|F3zQp=_>`>y2PV~|8Xjp#W~7Sxid*WKtW zmUc(Q>l4Ztgae{XE>L+ZU@;39arC8#8H<^K zqN%@YAGb0PwyAP&p_90tit|@iIe7S3^L_Sg=O9VOwP~YDROceuT^|tJ6}|v6#+gcB zCr_ralQGnR!=JI_*+fC8QKBS796jd_9AFzAQg;d(13Lh0PJbD0Al2j0a}Kqz{r!8m zt67!cru#I~eAERsV1Lu;z5J*!L-W!u-BkU*fU^5M&6QzA%zaVmeA%OF_SrmI-BIL{ z{lp8!ZOz>CYAUFJ;zYOnqF%hluT*#? zqal$YAXh{5708FSrLt)PNi7=rG}PsYLR`T6XK9D72tzAqIP-FM7`BAjuAcVL`Ls5@6Ja{b>gj+@eP(YAd z(;%)3CXNu!AdG|RiPH*K06ULPx-S+&dxn& zccUs?+SzJA@-bzLA#TA{l-|L?lpm;adynAXvcVWm&-r@2zPn4i9oYV1fB;&pfp<0z z5SM@uRXS5?k{0VA3?0S}?BM6#46%$@G$va8fY@e^ciFux~cI)hWr*-uHj8Q zpbUDOv8y2Zst!9r8!QWgsjW0yuFA-Xg8m+Q!V`<<*96tH0%e})#pL7O^GM-y@6R9S zE@scp&wsx?V)pud({xY{?b0f219N8?u@c_!pTaxB5+(&Ssd~1W5TwgL*__i`E^;D>&pP zB_3cT8!V4zB$SMWuL@Lla+tznn%y<_AO#4(mbUi>^s|r0Y**BO7N*gRN4p9_MpfD9 zI;&wyxj62PmMTY-MM{sa6C_-Zb}V_5zRZD@mXv|2Ug&-XPdNS${TI|)Oh`?A7k)Nk7<|^V{i~2 zw>nt-K7z?8DoMa`m1hXCBY6sOQ|~}3B=|)d7WorA*D-N&XyBGi#G34gAr517glm(BagI$lNTl4TkG+F8E%?KL>oR&5%4|0tNJElIHaUz# zS3~;MXfA?o!&ZSX`*i)DJQQY%(zgVgM7)gczKh&#fiKmbnwwIGFsTzs412&aHP0S+ z3Od+Igp)|XO7-d?@zhvzuLW?XTdCsAwbK1VlU_+Mr4sLIT{jl9YY1RP>;iMc>twb~ z(8T`+t1GyOhr*-4VD8!4LSRz*3e-IENye!Y+dpVCu zn-80>*dpTQ&~4rzoxp~RM(i2Fzx9mKm*4IZAp!0o>(4ZLZTy z>*7L?SvPQQ?pc?9sHn)sJ2v>E)yI2_vZEsrpGkh$SbgLPHrw!~*>SN`qmxOd>1-X^ zo1bGc|51#Ixc#t96`$t}BIo17d1dR!WLcP=}Y#&sM_@b~D8!nY^9)OZc zI#q_Bw)m9dPBm2D^45jeJ8*D9J$^)L-0R_e3kf>(Mo5kAEF3{WN@ z>>v?hNHZO>Q%XpXAcXS(M-}YL`U-Gh z>z4C0lj8}GTVzwI?K+;4rALM;Gc-WvKBxeI+LoQw=!q{r$gR94KY~vUcw)Hc`*@db zK9BeZK!Uu0Cly7$9p(sF!M?poPLUK0QDL=V!!-WP*=9*Y(@DJs9X6Zpm^vgZ+`fff zscAn!7uM8d9&dVjox)URB4q89Rv7`m^wlfdLKtW+xsAAqbS_;Y{C~sf4kV{#7qS3iRWp5 zJL6t5xc8;rzhPtBH_8$ZJI(e0Yc6uJ;w$)pTD7P1KFuLmE4SDfj(hU+%JM%5DhU|(A;xJsW7pmlf3S*eIF(a6ZFYdda=;nX_fm@$4UL2hvB+yZpX z2rg$fD=e*K7xY!+O>v3LXjlJ~RM+wl`?PMmcd&=4nSh68HRCDs=yE*ocdC{o0j*pP zVWr2NAkdv81q^LOmP^VS@|HD7(#BIV(vtj9Ny}`VE$VmV121{JkCd_QGb9fbZ-I_K z)g=+Z=O(BX$E1!ono9+KL}NIPP-CTtLfiMW*^T(4;b6Z#K(f;;qqkbq>UdFoBLGIN z1*dYCRV7@k4s)x#6meN;wzvp$r6c?HJSBv1&dNrYTujNGF$%o!gpR#7U0I*-xFnjw z<3f!pzmcLN!n(4WU>=MoDIM^VsST;EUKQqH~X+gx@`usY4sqk|C(P%JbR$a482vdXK! ziRsoE+7@-k`LCE5>05*j4m!!ZEJRU`*>K%KG#PAX13i6H@W=Yz#-&BH?!oNEYiOT~ z_q~d*YJRZ?ZGvhf{Cj>P?NhCPTkC~~Y$a=*iGpA$}XtGvcgC1Z=bVAiJ^ zU$a2KfRq4m=Ymhmzfa#%;=B92Zrbl8EA70`?jWX4;cUh3%BMvk{g=Co-%D8g6=>&9 zvn$9pcyULki79)+)3h(tVl6uO0*^E|u&&HP?{9_j%1}B=aQuOu`gv`>Cj~5Ult*ot z31{d1fP_7;hfb8eSUq^M<^2u6^vnV2R&O(_Nn)Bm!BnMrZ#9KL>;n0+}4sa=q3%`zY{Pv6G9>u~oP8l*jif!IBWO!PaGm5FTtiiZK z>&E0D=kC;l;FZ~)>2CHgb8o`$h_gE~rwo`b98#W4m+9xX=+}mb8kZA3hDC6YU4DX| z$zSE+fIHbPnx@RnE)WHu6F1CjIrdtC2G@kDf1q$|bZ3qDaY040D&0L3-jYHygR|Ht z?-!)l@tbTEl#t(t04oGa-aZOkD9FnZ!8B7g+6OUs33%2$<=61UI*PTktgUfacdv1J z$Y0@~U?H9EKI5ZMB!~tcz@~jEzGrbK-j)0nstLc)ELZ(|d7lt~!$HoO1d!_GEx&E>S=Se7-Kyg z*>vY&o_mnT_;rfA8a-XTXAcVSoM(5{>l5wTt=_ynQ^}0{tZ?(m3lpa6fxq38Jf+&Q zVlYcF48XSY=XsH`0HpzS?#BUxBMs!cmZw^AsF~|nCe~fak#xz8chhu3GgSa{OwBeasFT|Y8DqFj~ z7VKPCBLB3RHy*BR*F)w#)yf&@=S@xJKI5syqkr6%Ig3@j&H!H^%=tN2CV1JgM& z^b5vz{6X=`5Qg2{BE#+cDNX%)bwS8aGIT8bx$-1CzRA-kq9yIX#)TC>W7Y5`s(R;e zJ4t-gr7n9S(CzO{(a)Xhu~pD+#7@t;RNf_C+0#O@~vb(UOGh+Gp~U;BQY zK=Vt$bq3nmimz$xLB+Lm-Co-B#P(AfDHJw!>w+HBp316C@wa<}Ek0m%k9uoVUzP!v zZflIILWtis8&~=Wdc=mfUU*CGaP2g@K!5KpFJ~F)E=uzEFz`e>stx?@@?vh7-t1n& z&M2Mn64cl;vO7Tzdi-V2Y_FMCx7jP>l(>&y_GDw@07qJqhC#NUOsU%0$UQ#8lK8jU zE5l0TPSFtd6<%BZGq{&~4~=4Un*K!tl_S^VQ6X4=I#q6dq9IOvjS=avduqH$NuvH2 zX-~U`IHUg;=_SR8fp;!xBJ&RrH1z0D(JAXpQZ+4INAKgS8MmEX2xJWF8X@2EtlY2` z7cXQH%e?L)`>sEPN1!iTtRzuUFIV=bq1mr+(tsJr41sGqfx7Z=Wpx1E2h5F6G3#Z;~6!=p!EVf zz)oP)4rnV%UXBCcoe2y(U%dOJP#6JN)Eqv|8~)BdQ_#^D2$HO!&IxZek-+xJ`Cg!u zbW$VemcYYf)LH;=jg8#&mG+!?*Bq(tz#RZ^p2JOekE}F5K+2rp?KoZAD(1gZne|0b zT~423_|3s@kl}}7TTppZZBsy8kBJ4p4%t>VQAPCv5K zuceGfbg4FsD^lk8ZpDl=aJ!;!lbXqYWMyOlLg-_yq5V`7YbL#&bHeG_7|t9@Rd&JP zyoGD^iK(w+2M(0+@UpvlGnP-3#$Xcq-eMMX?}pIt4*6$`+|eQSMxYrv>^%??vNTV? zP}olNEyo$Y*V?zVVQWT?nNdD4d03@Kp;x{CF8Qo9{Q+}I5`6=syP8%+3}BWYXV_=f z2;dzWRN#{PMx}NsZo9ODLmPp!4#|%_r^2CG6@!>y2gax{=Wn(*mR@>IpkI0=hV`FQ zObOM1b=4%mGV{bO7&!=fYVL0#*N>?^G?v@|d<1U3?TgnBa%qCV&qn;Uyzf8Jo^*^< zlv5n$qxuj5?aasV>RWB({Jr+CbYySJ&{u2?lA(4X+Gr<_K)HoA4cJmv`>ax};Cwr3 zQ7n)Lt5C(Kdu4RN22_>StJiIvH!Q4OQs{7^-tD5>Uj?L#;BAd=lP$@ak6TlZvwhgd z87CyVHs`lV-+A2%mR0hHP40r=!vk~iqF~u4CM~4qMA*)7 z{tClneG{>j4a`c!M3s%qrr2~RL-0e{73u0@KL>LEWK1IUxA1RkiVu0+n0pQLWe=pL zfB5VT>62%_P;cIJ+F)mcVnTL5JnkrRAMxY>GtR0i~3 zJ7R)yF2bI-ekoaLLNTO(anKzZXmp|W$p0l zqUs)eI}C2!Z5L}a*e}>kKFdNHHXsHD7R`+-tHiL~-ptgMQyT`;v!0h>;u?12k+GTq zaG3~wjC46A(FMN8=gl!F!0uxEA5vAq?~?N zw9D1tF{4`-rvX6eKI4u2dPsKw5}HWL7>7_Rjn@=I>VIx)CY@B54;}suN{o#3G&0s) zS1KI|<3WP&KdM zHb37~VC~z%)FJPC@(XGt{wNc})uygR;hqilwZJKQ6PJ^ZiS=1%DuGF9A{0D)$aoQ% zXtq3ChC)@SCbwpdR_TVqH7e4QPDuv!uPNhStrWt>HPCXowsirmQ9U*VToSd)^h9iN<>09=*-d|?pefXekftJpRan8V7Fb$TOSgy0aJ-{ zak;NBZ+OFo5Hk%t{abB=w-QRgh_LBP+Vc5DRlM;93#PO1n^3vGPg%*sEmjbNM2~FQ zpp%qX@z+#tuw+g@QTT-NIDSGf!0}aKe(I!c&%CgUqzavyAdmL`q#1N9Ek|KU)ENSl z#5YqL68FWn)yfO`rBi!bzn%KAC7nrTkpV)xa-3Gb>}So&weUcw$?D}C7i&PdX%`}k zzMdJpLH-_j9>f?ucXjUu-(u;tfj@r!$8@uG0g`HJ7k5&K>!o#w`TQJ)3;>>or@qas zy07U|cw+5j zt^AUkUin^qMVZX0pZK>NjON^t@5t&j_Ks&2tY%%?J#VMFgq5Vgd%NnH$M&Y~JKtqj ztrEct{iU>F1gJW|y>nRu`pP%eREPO_wFFV3k$&iz3f zuOAvBDn^T_Tzg=+{~XW} zOj1;vDqRy=N?9Fy3uLlqgmf6+l~uTJI{rV0>iO+46=^Z3} zzSNe~9U(!@(1MsJMEN0GF2XN7a2o7b4&qeKD_*FHk{CscAFgPi)JkzDGB1A`tW3;aAwzF3q zup!j%geuPS?zQ#c^+)D(MMVQnLb~ZDWP;?NRS5!kvwWw3`5Tq+Wm~o3@ZQMv)^(6PFuFv8 z*SEGn@QsbYw>26p7GdAduGIKN3F+_1&?HU0oT$)%bKGgW4PTrlbwZJ$>KBV z#Cd~$cm51$vxJ5W;zK>=idCWlI2UzmFmI4k@s&V}Q0~y8dVZ2T%AMfYJ_7?pZzT|9 zZo79nj_TJCEn3VU6%l?ikjqY5Ix)h(3x=Y?mB#w)`_g@o)+UQ_`nf6sCiYYwM0W04 zS~URDF&8jFtAyK#wZL4uaiFDv9jo4RcV6?U=&*CB01{n1636?f8BMX8EsC2sKkDk) zLoH+Afk5lq8D25SCLHdDITj6kBUnTP0wH0bW*^Y&{VMsbg`fz>go^r+xu-H-VZ^p? z^v2Jrvz5-Rq}tqxyJLT9uFdoX4Z!+wm@=l&-h4Tv@0Me?bh0h7d{y+Milj_&Lg;Qv z6>bveS>d}1B8pGCWzdE9d~@wDxrW;JgzpXijwn8ykmYo`-zrz5RReB~wxS^T2-^Ls z;h!Z=^xQY9#c%)6d#8Q_=Hh#Oz=6>(?Xi~xQP>?$Zjn4n|M}d81Vblz1YrRdP!(`q z)j-p3%E+uQHBp0pn@$0G2jzh_NO#pT|KqMCJMZhVLm7Qv7`VGc#04JOrKap!v&J3? z4jv}a_|OpgEPqp^4i#k=MpYkf7bx0-p<<2CB3qizIS(Vg8#tEU_k#}S?GrD!17i5P zAX<*f^>-gJKzCpJFB!Q%SV100N?OD_0Ld~ zc8M1I9GyK$$1PRoL4Uj5u&)TV><`f+izGgnM{;vXDTL~XMSMCBaG*6D54K^O{Qd05ewTA`Er=Bcb?(CI@tu zs`40A3a(xx<$xpP4-H36L}`iGlR-|PJQD;&J8dVa;|#P&DQqdZiiJf|B|uZFM79zn z)7k}H3pD6Z)Rp1s+o22dOBp`OWu0^_bwQ=NmP7-Z98xR~CAw61@Y7$^$xdxF2tlcw zjK{OF-LOwd3|IMg#`~-#A2^#TP&h;pRIGGE?kYa#6{VxG2$G>7n`t-Yp?l%74SP{z zr_q+vsR6UH3YE6gqy2HJxV#%X@wMp6;IBi@f zx%dtoBK6htA_o(h^cpT2AEqMr2e^|ek<{6vmM0RO}rYuMGO`Xe+ zznFv>Uifzdm+-^M^sBcc+Y?;Jry*>yM`#e0%A#$;&vFPO*MH8*atVR(p z+ZG(R9TmrJ;7O?iLvAZ)c0zm7of{r$+nW(S#zPbxhgEV`-(_^S0Ck^(46M6~Qd*GZ zDM4NWN-ag@d2(XR{){C$(7lzr@D^>ZTILng5+5#7G)S_r7UbUx8k$8;~m>vuCU&i27n|Lr~EHM%QCV(|2RHJ4Yh^!KbPM ze$+e-Tk)EeRj*yK&KzgEr6tmpHCN>|Z>nqh;j^lWQ3uQVUZeV|y!xuLItO4Y8Q`jV zfI5&G)xJyy;N*MMk}LC50vM4lh>xp;RI09Fz;%e2tN|l}!s#v;Idyw2Y881%4TP^7 z4vq)#u&ARg1sqi4Nd#B8CWts>;AVRzu{WtI^e@8JIR?BZDU)Txx)Vo6JKexoSI|n~ zT09$gy7Dj566A;(j#hCAq~Lj|{N*2@J>yf~IIR)ZTK4P;{5ULC<;rHg;po1z=rAVl z{{#f}P*W|pQqucSl|a*~QXr>Y_V!b5kWrY&jdf)|W582FH8W&KX}WUI?`*|jUb|e$ zs$@^dYk!eBSznd*yf1dBtR`*_+~ z{hhtn?>CK@8D5!DHoD+l_4eQm=rUqx>iF~&=$$du`;K%`GW=dY zdxF(3WOZFWC?ru1R4VIWvE=%TK(WsHD?qW1$NZNR2(kS=&GDDjSq1B?O6t^M@Qby| z8FI5Inj;SbP}|j6LXQ^@uYDb6~v6&1^w z1E7(Wl5C`qkC|zA-X72&3~1Y<)he%E!Q4v)gIrTh2}iiBl5vgl?Y~_+MMLL_jUbG z!e`CSX|CMXbh}M{br>&WA`({g3df5L`pTMOQh=Svwz65d#YEDP0U2ihAAC6|~X^S^Pdi0pbMtTVq57y*8C0iWweU z=-LUj>}U%^Bt1i^NGOnZqjyjqoxMi1m=Wl@sP;^3SWxMiBZsS%bZ9!e*pJs|HiWt{ z^d(*$E*cP`iF2t-)C6cY%oA*9LB!~a`VHFH87rHi{xn;tM8-K30754r{|+4`ckP3i z#uUNMUruCNJD0ypt_b~5^G3JNtqN4r4g{kMR6bXqf5)FH$spO7sh==HixFBIl@|gJDzAye5gTgmqas&Z_%d8$7Zj=E>a?^&!qK?d_Hc>M|rx6eS7SZ#QM( zX}c3qy(4ig-l*%AUPm!+Q*JHwts;fyK>=@Wv1@Kw!>`S}-0?~^ucVM@uAH#cAQ}$u zjjm+C;m4?y7r*uKb4#jJHL15hc&a*BvtuMvD4Askgre2#br5JX{@&qwiC(@p3Chr*PV+mj_dmSZdH1}$ zZFnY~eu%=lkVtqgPZgO$L(p1v+r>>h*{6rgMlNRJ&2<=|o3|WRpy{IrN5_z(|=D!^akWcR+!Mv4|GWG5ETXkXiyxPaKWlWpx@Edw7nU0aG*p zqRQS?%#?O}jiRR&S_5p5ZcTi~HKq6*7;NFztk&q+|9!)EiWqX?F6aNa@Wx$LSX%ij zv$H}^To(dz*hDHLk`s5LeR_w&Nzhgk+@NtTQ>Qc9O#u1l5S`I5%HLYW3iSgk#8^NM zFPg^SQ>}{jCY8kb3Q%U%2nsK`>HF2y;>MXjnKXs??6vX_^ za7VznK&r8M0Nb_t(x{`X8`ktM*Wy_-q2=R`8l`3wWx9OpHoRlSc#Ffa0;-9nM33qu zjAHj;a6C@46>djR1Q0jiZuBx+09T;mVBu3gUF>sx2$Ghpy51Bx5I$Jlt~-24zfrW` z;8`l_=^ZeBRJK zV8O+CB=5Y#f-+qRTLoSDXdM8SpKo{Usb~57nc(pJ6uP(}s~pFT==#Gm%vIdEcRuN` zQVMylx;kvZ+dmwVxH>>cP)A3s(7kl@e%3a0=9vMzxcOM!rVBn;!r1v1;=o1L|F1W4 z5v|LIx2`{KGZWbiB2UwCR_uzEeKAY8nZhcP5J_wMx$s>jK(Q#Ec3|Z^dwwZVyN2&y z)K-IKF>547Y~$l|GHSFXtR=Wms>nuG#17Al_}w;!A|c3gJ=)DFG=?i6g;M&$jI@0&J?M`E zl0X`qmIuP}C>ulJmeN&5<-s^@vdSQ~bVq_7iD(6;xWCnrG);z7f;*C#tQt21S&b2jriRTGgp`;x=Qnq@iK3QaRuig86 zamnN(#`UqdWU_l>s#4G$_whLgN#zqz)tb!4i58{0?<1}o1@qidUc_NAp+nbya%S|Y zm_fHY?2&Gh`w;X*XMy^mGk*kQu6)7xPmUri_PXgo!)C$z7u11OT`OZL_x(njS3Qnc zS7a2-b-J*6y~Cl>j?K9!IU?qr%{t8)J*-A5K9JU1KzcETz}#FRMad5a23Em}u(fVV z9L`lX9}l~na&Yqer|0huPoBNrKYYbcYI6A7Fm*qKRI_RN8l@&IsyXNbgiX!nBzNpZ z;>se7lAG@hxr^!0b|h7()@Ynm5p?%&MKr5Jqf0x{WyHb}hubN1vu*vG!+-h%>;SF|DME&I z1u|-Q&FMzP;?*5=P&vGj4}{ai0IK#U=5J8Su7og=dI<1bpZSJ(JZFvhsU3`2Ozkn1`r`Q|qpgLV zYTc$!d|)d^JHP1Q2a9S!Ts1)|m3m|j5his)f5`0M%H`}tIhf(C=?1?(p@J8@7-A|u zGl3_dL3)IqV_2AfNu({#rNQ80R;ah(QbpVjqVF2C_Wd6N#OIDZ7TQh`8%xq%64;!x zo97d4VyiB&9$5zdX3xYEu;*zF?so6_&K{oe>&or!?;N}`%Vh!UGbzMiq8IMcbaNdP z94#5p1`1OE8A=#KJR~xe0dqMvE3-C2#Pit5M$|aCu#Sfu%t%I2f)EXOgl5_&veza> z@W!b}@jAagr_*4NrEVh>+KI-I4mWNS*a^B@Gp-Xl*S2AuERS)^piZgQwvli`p7yJvrdanf zpBDy7U!IPRoF$TB!u6HT52@l^zf^EustcZGK_+nsM+=Hh$dvXnjS(Lg=itN*@v01J zM;U24V)dH+@%#-PAKNKPhvE1DzsUetOmX9e7HR^u-5w$=vRS`%@rM#!zom|Jept#D}fwQ;v@9Tm(*-jZc*8)Zwz z2otEZF3^^{%geT=jp)LKyy#LP_US1WUBjCn9T~BPyE=LfOo?(lPHGB#QjYG_d53mzy7R8mZ*2IQ4&v$-$&e#yREZIe? z$Bz|98`uxGoGi^lQGEF-rfx`Fo$~#Cx2dzy+_<~23Uo<+#dqXZaSOJ|v4|BWn%xTe z!|B-yo{-z2$ZnuPKR;VV@UkZCv{Yn;cVu|6KC*&q8f4sdCgh4m}A4DzAXxpoHxh0x@a4`UAwG@Cd1G{pgIr$n>ZOCfo zOrr@H(~g)~`{h{OJKssB3Dn_M$)Z_l2Dko+sJ?YAQGqoEwYPsUqKW-f59lpK`S)Sw z=BI^2DN&ev2aV;+#ae8J>H%a{poz7D`3WJr^FE>(;=H{Mzm{0Q>WHDY98BmT@v=w>4&SlWpQ@GKiO`13G1eQvD8&b{1f< z2%edy{8~=)fx>WB#z9e|@~{Kfo0V=w>6)IRxfBSMm*Z}9%YBf`_ubj_ffTt%Zt>?zv&3VA3;z-NM0W;{|r0n92eF>phk0Jht zt1^{RG`?b*Ky;-|#EQe*5v@K3Uz(|VLm5wiL27NjeSL7K+@C}65fn)0So94#F~U=u z5I#_`i52&rHNC1-+*o;E=G8Ln=f!8xUcB07`GFmCuWoPqz;z4!Xy-~azRyy zg){VskT>*=4}egITbruXW}B6rwC;?OET=aJQyXYndk}*f_ko@ZutGMU(~1SRvxqA0I0H8nxt5@0V=;6PM9R>C{i6%Vl7k?C?|d zNxw62@foi!WoPgT4 zENbDw{FMT3Yeg(u1y7!3UQvY%K~Z4Ptk-jOJCYX{SjMGRh!+w)vuIW#Y0tE>9B%;w zRi6qC%yRhKjjF{G^6m0LI}xKx7R;8p<&w#YwHHlPsT2 z?9NxDW}k>p&QIv9o~7u?e=M{+RI;)1OSrTIStb*My=6{|VqA8SV$j6T_%V9?=k0C# zY9X?Ih#qclZ*RM{Bu*=oZ`63z5K3h~wrX1vU}skNtgM_CQ6xAoDpb5XF<$(5nN*yb zmnJlQac*>8>Ezg!Za+J!Zg6_&AX>ze8DbWy-Wpy+ZGCUb)|L-U-y0~l_sr__wuC;u ztL-a$NVLD;Cke&m$W@H!Dna5c)xm72J?f6K3?%H?AXhoFcpgubbFW-qebhHfjbJNj zCXAGRRbUHjMm5Ts5$Gh|9URy=fWZJ?r&`k>?_`59mk2PYmeJ9^j-hbdn3}+c3QA}F z&f72%A{?|b!Ncnx7^N<><~nH-bV&I?8ud#kUgT29I!f5q%{Hl1{+FcK+Ix|k=R_bC z=~aSn`9flcZiL_i8yo<8Y(oRs$tgxa4Og<(anf1Lu8BX;Y&IHB%va$bmHjRIF<<#E z%yXxmc?>vSYsi6z=@aMC^Bz z>0r|l`a02Ox#PENORTY3FH;MrTbQ|MC9F+dz&u$hLzRK{1{Cy^9Zm%I6*<;bE4E@4i?88IDah$ZR z?XKh32wUGHqV^j7G#)iI^=%%jzLn*=!%=-~i6tkYsxFUT70_JoHKMchZmK&g)1EpA zQK+YbwRlxU3>UOGf`F$j^pT5mJ43MhKzM!{ab7QxYq7i!%a8E7JKq&~+^ z_|`&C>%j<6nL>nyuly8kh!{MI14r+*Ja$*|gQSYa>76d_QdB>CRh^B$!)Hg3LD;aO z@L01UN*mLLB|oxz@VFD{%7T(|l9@$8j9o`!E*~~rl8K{sX-4W9q~HK~aM~}yJn%n} zc6Xw`q3pR6;bZ#~6}=0b zDNF#{+&GLW-R5aNbIF^M*pDvy#sW3OnYN~*DyEd%E7=R9;QOVn^%;>#s6I%en)>&9 zc-=LzND6EpKMwkgpOYG9{k$UfXi$%jn0;tsaH-N(H7m>^l7N#CK|6;0ooD)O5h^HX-W&!D`c-J^?b63zHQ^_hiOB64{%Oile(?%Us)ykH}>p^gN zXh4u_QNLz8HVV7SEqV+jAGc-0!O}lHfAxCz4fyw?&{pN;7;3Zaa0qIRWQ$?~WT&hj zL9*AWXvlLnD4H=ad=5j|!mJGcq;M3gmAOz9T5HW{1bu2+?|`Ci(f^@S5m2zCV#s}= zQx$v{mQ-~e4?30d^I1uyXCGg`*?ax&Wl6=;u^OS^ZxwCzE+0zkBXBd5w*n13bpNn< z>h@86t#cbJTh`C^&{g-YX_7UPv_p4JgF)?rR@E?cm*keuQ@Bt}X77$?;9lGH-ZmjbnR99UQn#C*)9y6LZ`N4U5~^I*14)wvu_6%|mL~a^sn+&*ET?4- z2wtOU9XY(YPr|FF4>ZJTFm_aN64h8?B8Mxn$hu-kkT5!Vvp|i?>(p6z5&gVL0nD4e zlD<2qvm}uk`|CKd>Uk$%pkVlDyRqTobLGU=f%$m5)at+)&@5h|CQPll(q(Pj>JOaK zk|eWT*&asBzNhKq;M;wE)4bCv|DzX&d;I9jcDQWi8^JLIFCMfm=@<)GL?O$D-FUh; zM+Ff(-m((H_C+{d65dEZOWGQq1S@hk;B@Q{FQE?jw#OKwaz=nZ@)fpJE0V?&W>5A0c zfmC<|np?neqkFdy6SST|^O2l8*uZin zxLAsZQSpVKSTdj%--%`=47F2HXa~$V-VHhf!7ka)E{U@9a4P-gysyeKE6qhn9Yb9M zygNV%yvpEoC^J*%rs=dyP7|wqi|Xezp1h!<+qs&3aL{LSWRF6Q)>MEc0=;2=o zn(Ey&Ox#AVcQxHYFN(_q%zG3#RUpryJ=! z;IUi3vNujgQh&s!#Ic=vdi=C+pOn^c-WZKudhDrhzs-i^!znr#PqHC9Mbe7XU|*3h zLCll$T?zCEoLwDj$mApqVV@?8s{{xI`bb;Q`sLbP zW~gTlki1s^fCB)xHG}gaOl6kOzOpE4y;XBBQ3Wl!ehR6C7WC}TIuW~dT6A!v=%s*n ztvcuR!#G|BP$3&v2wexF<<~*gnB^&6A1{lSVufj6x8d*5+OcLfp0h6Nw3$G;_CmR5J(T{ztbjH{Q`nsjIo1vTQk705q(&yf83&m0W$dg~ zIGG^3urr&$^U4Z7exDT-f%mo-A>4Ou>d3n}2l}#x0{#|ot5og-JfIl$3)mh;c4sV% z@W^FdB%mE-TGZ^7@a&+fz&ExG%oJf39S;iD<*V@zsq?%SRwku z_2wMIccC`bgDF~jnr3$C0rE+-O+FdnJcQ#5&KQ{ZM+Ks!#roKd7XH)0R@4E2?K;lV z)$Cxo?#IKoe@9N7UI6I$pk(}KFCER}*|`X!al48lOHCi^|F@cd+NMR zpgmVJ)9P*iSL@LhjtP8ZuY&iGx59#VJA&?M*3SlT&tT$)y!V93x6ZD5(Oj9$uo+S=uOFKnD&o_~+Yx z_hBErLE#9YM!!ogKT2@o&6^fHb9X?fHzlm-VE@`y(FihMC6km2;15O`NOeLKs8T2} ze%BNzA<_?;&a*Ipb0IKzEcx7lVAFpV5XPkOLQ2o^X^unSgLdkqW>a@0dx_DRJ{!Y! zwDnYhw-Lg=(6jbb{q7822dQdz%wJycRDNQz> zO{*Gb5L*hW*EnMtRmW!q8E8R`Xj^~EiII$q2BUi6fzf^2)}L~SJ{aX}BN@DyPr!M{ zJhILK=>A5ug_K?u-Pe{_`UVK^69fGcj~nv`O>#HLAGak4SWd;}w5s6kK|f%4#BmDI283wNc&{Z^!k0Vw9}g zC&DGCPl{SLj5kEP+4WM}Q$g-!f2VMcIkCmYE0m<1!WN>^g)PJ|%obvzrhWvsPza3f zirGR64F3pjAsF;+w~(5ryKEuR+85nIemnoDEo9sEx6!@t45uk)cN7rbWsTql39o2# z^bQxJlS;5wO5-W9;W~YY=AT;;3j!fWU)`X_*k*v9ZbEWBbuA&u2Hx9O;C})_-!#$x z$&^ZvTc1U{>%amibFjQ%PbnO)h+|z*^LX-NYk<%f6IMXP>R=``NJ(sWg?cLS7{b=( zD3hB)u&)H|h-q6>g?DeUQx}tNIlJ|2zZKtfsKpL8*HOJ9iO)@FSBr#2wnvQt2;xH( zN1?{0w`N zEC@t(UaDji$Ff0Cxpus;_IFN4X&Sd(Wj(K+3~_~Zl3~pezeth=Hb4`n$1T7sXeKC@ z0xmgQQF;@AY?c|DYC@p*PSKnn?<2b`l11Ut=mTCJ&XeZB==$D45BYF6 z?!I}y$lyLQ)6FYW2kiI>-HL3D85>mH{_Zu$1@?bb#er8d$qg9%9}`&K288 z`v>GQ7LGi-X$h#8PfN>-qC?>tyR;8_9}JzK+Dem$ej}ZvTvjl8%3W~d71=`itrb-h zRlsFa9=yM`MNv=TP~(ElE8BO%CWpe@PhC%R$}C4tNx4Xr@dZRP#m6qTUIA8uog(O4 zeok1l0YNnR$HuSxXG3XjU;w}Vpa1;r!$;kI9|SzOk+yohZy!H?*zZ36?z@MdI=VvN zRDjHf{cpREA3c7^H6B7;Na*!?{ZCw+|F|-!|4}%s9_dkJsNIXuX{(TV!+O=ASBJt_ z&De|#i@{71Y$bs4^p;6fCvVxt^#d{-v;YEPcxinCKB^(+l#^nglVgnUJX?|vA?-+% z9xyEIhGzrC4LU|pT93+Lo-8PjEF1dGb?a%03#;LgaJy1n$EZrU{HnoK8|cg+YY9|^ z!S2Mm78uHt>&aBRay=P3uj1%?QRCkz`kS>53ncXfb zC<{Stk`;+4@pNiakFStep*zRyLR~OL8*U>)fz40UDr}?|R+i~DaaL`e8B#SXs9Q`H7zU6w=jb#U9cZ=naFAr_4Hoz_*l$+4_suS&G zJDmu8(oarzrXwH&AP>UWA}McuVD&&`CPMN-wY~Sx52APP52M4Kzdny%zdL*%?dWEG2H*>65)Z zcSB%5qzm9qgFv@t>4QE6NP)khy8GF9T(3Xr=p0d7;pCevk}P2xR|j>d`F4IfC0=bS^ivzi))O4~8Q)g>?w}E(IyJ<~LFH`r#T4+Lr`#M}PVR$YRjby!kPj*dE9!`XwD=`6Gey={N(B-pOEor$8GntED5B@OH)cD$<)<#ffu z(dMARKv(n0nFax5dB(5WsxNv?Xw#}G+}c+Oa{JM^%(jWdzM(18gmq)`tL9 z4p>iECWtj|7Yt4}`vFz#hdl`N*2L)}(pv5I_#CZ+q+z4kVypbL7uZiQ7zg;iOFo01AQqJkRM-QU)H>mf9KSg&B7M@oRa_30?UUinZ@!wk-ab6Q;&& z1!~=8wpQM3@u0R8>9DgU_(-1}xI{1yg7iShRJGvY*Qmh8bPS(Ce>?iv*(dVK>!+_t zICAf@^i+I&a+KtxTQLs9plM#yzBOzX6c;TAtob9L6lE`5K{nPfVFfOhE6w98HTIyI zw`f+sh;Ql<7jkm0ptrcHZSKyz@LgG9D|)k?sOD&EFv*Uyk1x+EtHm)ZR)#GW zMG(CZmDghmv(DwW9fc8G%t6Dzs?+xQygSc^_g);}`Rj~cadzuQukY^FgQrsK#*W0| zvxolsh2{4P%kLMK;DtqPNtIfM1^|ayg9IoETQvp1LjDp{aW<B<9h&u>SjghZ?^NRdW5UOrl1f<~rM06tCFMveer1+}`T{rFL`h`czLw;@Jp* zGS)MbulC!|OS{sh2pLP|DXY>$b-RR^R)cnTPrU7;>Ddhs9^D!R5Qh5CreWvY!? z;AR1hOi2F`LkiMm`g{o&?=keo>o)S&XzI+;Zq(z$abp-ubeeFUX!is7yRk7%*hvb# zJmM%#h~ivIQS!)%V@ftLe}LKMe&Y=3lIodplg6?IgV`hlXy{A+1S))h*Z34jC2h;i zqpjuf52ousZlX2C@)RgAdI`as6^H5Lz!RzmMvYw(*I?pt03V=6d^w*I<_uxJSz9|rQI0|i>E2I900~iw==%$1N z-Y*8qB@P()w!ugoc)Fwm4vTZ4#>Orl#;BtD+D^g~y+TJ;L8xT4Vy!UmOunsVm#zYq z-@h6F0UoI!-KtwuL-Z&JVSQ&L7=VIGQn%^`RuH`i)nDHU0R&(uovT}QL)8&IDS@)S zGX|91K61GM!iMfg5j6s(-S!Gqs0J^d*aaKT@_J<)fU+Q!qa~0VN-mLG!T}_TQ7Juv z5|lU1g>nu+Zz72rl+7d&1-t8u6C0`>peq$17(!vD{d&&C`*o@sloh#NVI{m8a$&q0 zRSk)1Tr0FvUL9}YiuI}*^cE7yaM?P##JjkRfx1O-rI$%IAEhz> zoFn&%Kc6MzQA+-gHo)0TJf!>=aMxxd?AQ4`PDpI}@nR0mP~!d)ltroF0qCfoNAJOj z*@6>xPA|^m8~*)z5woA%9k^o5VP0bM!9Stxc)p}DJX?-Ou}a>(Nr1iIPiD*0F-0TX z*aZhIoHfs4)zXV}iZejN0c%?Br zg70q!@ECE!OGuw9HFQ|27tziL%zyJ7-U$GA_A=6nMY(z$HIJ6ZO`|B!vDQ+$d=eK1 zK}^IKF{?(&7TO^RIYpp@$DSC?n*jjW4IQMQu(M)gDr9Zbxx!TA^N1dS_)cN(65K$7 zy1y751(6wyHqoCRNh-iv8=Tf4BWpK_e(ORmK2|pp?gc(Kp%etYXLq9)*7E)d5x!rW zOm5MYAMO-|%T4t5Nfz6>8x#(ZSqqPlCf9>xhU3d}Jwb4Hgnpl{_{t@Dm0`50n%Mle z=p3$vz>J%oi1x8N+_9>jIJY&oTkzJF3}XFi0>e+)K_rT>%yq!@(;HHTPNb0P?7RTq z()um2k z&SFOjf_^kUNn;7hZ2??D zf-yYAvTf;3o-ZflAL0qScQ`e1G%a-?O0G&v7Ci5Vf?-Ke>UR*J<4n0S z(Gi*EC#NeNlb8KID|7dg32zPGj-6JMyhG?yk!JENMh}KG5xIhNs$29&QWF``nUf?_A!FTpM@~)i1_hdDBR6Yh zG!fOlDUubwuvhn|(YHR~|F+Y6q*@Uo78xp80JbIAcG!pBdn_U;$tJc{6N*(1L#NCV zz`ZcwKP$K-MJWM`c_mP{OS;=vV717k1hUF50sgQN2pb|=fkElslTC1G|0%aw;8F?= z_Jx+%m!?)WF0}}_3{REdh{O!h>7e$-fG7-4vZ#`K4`ikx15t7SB3tpDRYKAdnK`cXuU$e%HRc9ic0$UW=TSRjy04s;JXB9H7;#ls) zj{F~|2EQ{HzXb+Ei+fNh#N{s>MUa|gz?e-q3Sdk2bx)6*ZMrVq8 z;af0;n5z=NF0lPO$8k>_$Nf{8-{gz-7o#L!?s?qZ-T&H}6NZIwx4myEbFX{wHvR#cEl3-FL7VLuE*B&6x1@CniybTwF=3G{6)ACDl$!=tAxefq z4;HOB%rvtH@ypm-NYk#GWSCqmqvy~xbM%#M5rR4$E8}&lx*@n7tKjSz3)>MiEYvQT zVq-9CiukoiClF2=V-2MkXNbqH4YpOOvvCZz+DgsXfZ+(y!B9;QWd)ot&r@)X1(Den z=!A}(`(r36eRTd!9S61tKX#6CB7;Aj;Ly0P^pA|({~DJa{ygL z-!?@d4x7XUI82g+Ud8;CHQ5ba`HTJh>v+?TWG=s;+5)5TL?Paeozc#@k z0w5S_!+#jZD>z|z(zNDpLr&~@GPp>FKgDA^xdMT0BH`&f!qMfP@8Z4?)5_78R-r5L z;hFGgyCLs+VYVV{ix>;n9F85IJ0v3Hazv z5CDFSq9SxyRQ8{p9R2GXg$e5v#A_>!Hv$7)+j&}*(yrI*b)k3STmD9`8VbyhPia?# z6#7*h`{Hm2`;KS;D)3oYC*Gf%GC-17UE6=NwJg5R7gKllFo8eu$h(7w?7AbJ=D?A* z06>Q-wVfsuZG~KJF-%%tst8uU)7F11ZB+r&*l@^M{t;86Q3>&sA;C;rdwNHDKk|!??n3YNK=lySk20b! z|2qYv*Ne(hp(CPB9!9F>ei(m{QDs9x`X4HI>+Y1L<6^B}fqP!8`iFD%`rP^GzQv|$ z+<8BJ?q0gZK3cmsZo4n4_rx8Lto}auygg6{B~+Kz;_|-`uf~Vn5u+A;q&2xUS^G{L zT3zOh8~H-q*&cWZe;d!{&WS4U(pfhh-3}wu!QTLbHiR`(3+C}> zqbFDKe2M`9l~+Od)y=3V*(w<|~x zqwvvJ2AP$lDilYZrn3fl5|S(HQjx*0-F^A%>EFS>T;95Ye@~PbM7#4qY-Tf_Sd(^4 zWCMH^?v70LMFDRm!SS3H{sE~3e})5;{grHOm56QV6lpbWJ7na621TikiiK>;fi<<2 zxl)m%uNbDMu+NeDuq4 z@EEugo`A2K)hHu$p-%rJ@njt_uXzUiOA_bs9=ScazE8p|T)+~^HQ3Q|=x&D)fFF0F zR)D;g5mSah%n{O6psDuFxurkFMYVML@d0NP3(|hLYjPDlYlgeYU+}7_XeO;Hi8S|4~;3(JY)KU z$Ez5u-i#gYWxH(rjl#W4Zi~-p5@R-TV5K#Umm*Fj<`8P~M~AkQ!XEYB1=ER%Hn=l> z6`t(5zK8%#-zCK~#)uRVFR&*_Qi{r=O&&6EQaqtf|4KRX5N6+2 z6hNVn)IBjpw^?{XXzy&yDWRKf+ML?mGwxU@>~W0rP*9L?S51!Gbz%>p-|!g3H_92g z3SsXHCvZ&}YH)t)>}56O9s1ilZm*YJ+!|{7O#!aAFcjtO496Ono7o3>fp<5mgFOz$ z=ncDt!Ix;qKInZ9QR@Q@WS`Z*H+}<+GKpCU!^PucA?={oD1_z7%oB(=n&yxyH_0q03+;0ySupRF zlmxius8B`w6-E21sQg(Z3w6OBNh&i@+Y2<^n!^a-lt|fYz^s*0<0i2Vr`MF^Is}St!JKgTH&nggh#F zC6Zl~>m!6dLwF`2JhKp<2?)=M5FR3ghq@2F5Cm?w0Kvm&7|z$&mn|FU4hu&xR zd@n$16XL`%I}EqzZL_KxoampN!%!M3U~{Ka+gq zl67pDgsZJ)V1fXe?inmVV74GS>jpa4g||o1M4Ma&Z&W_h@zJEZH^a464@oBN#-!co zw8509tNNNIS87f|QbyvJVFJ`mSIrc@A2HAsG9-GI2E1|71TYv#S$SmC8my566>ep`d zYuS?x!VUNbIl`hYJx7991Z6P&Qy(cT6jb5X9U4WY1H2gsrr}9onY2LH9V42*O{erH z2Eq@%|LGI|#@C8JQBbjbw8a(L(bw>RNrmu5lxopD5gc3U8^m6sl zc1!n4cS`j~sS15$U(DRQ^w_(X$95*p@6jr94UV=ht{&u~&4$`4&Th zsiALOc4vSgRa?Acs0aks>$OYo?Oe|Z@7JA$mdD_*n2w{b*8`x{2z9IVFpCWuyI>6t7C0?7fXfq7|Eo}LZ^xx2>7N46*% zp%E8a$^)?S*?Cgd&Ox{xs**JIl&a$_Gyv#JNs9@dyXh1*I;aUP1Vg&)%_ zObVG<*p$UbcF7ZpH+CZkwrMO3=5{=#J5dpU+An(#lBZCC7`nR^*YTrN1TI!T5>^c7|)H7+jEwftvbCWrbgrM1%K<5hq^j1jX7guPRbh z3{{Z=L1Q8M$ijfak93gYY6a}M(&-8Hcj=K(ws8WC6>A#MhHxpWsdOAz@)HhO8YF6W zCB$cHFUg)7y^W2Hz=r3o#(pE&&_Z5mdgQmg-8Oje0vbh$2z(|k2+_BOkO zv%7>rlL`8C5!_m1<9A-|pT?`56z)}Jtkk~-2UTRCZZi(A!kWPZO5bioa<3ORdU0%} zeAAa;oN9AIHTf(S7ulYBeA9`zGH+PlAa;%!wwN*()N!D}4WB)6q21-eZ1|-+9Ka_w zrR+elMO+PYd3dl#h}tIw73X7N!@4H9-I-a@^Hrh9>Yq5c`#I0vygy)5;$#8n2|O@s zgY?*G2mI=b)V@B{NNw4~VB7p76#pY+{t=#C{s^%@{`hFq)W026b3d~tC8XkUz8ocz zx}RXfJ%_}b{mO!N75cSSfhPtC#>p@yH8ku9De!@UUN(U^wFw-Mxm_-Q-za4A zR_lbq%c5}r3-wL)eyDoi${aMUcxp)mJ(3W8yG#b~4luGG=ub-Eqv;>bLq|}LBhxlz z!3zH1&~3WAv!0?-JGDX!x?~esBAHTYds<{sUI3r1B;CBJEPeCI??k`Y34dgTP813_ zo#=a=D3n&6K(ReaZPNUwG*3@SrwM1JqdMlCerFB}C>)SJo*WDzRUJ;(Xn6FMK}dId zgo-$uqd1dB&JwPBnda-jLlOJw*OiWy3KMACRxe#&w27b_tHNuAhF!ux>|BnN;VEgJ zn&+$da`c}a{P%D4@898VI$OLWUmDvT{lELL#SkR_&g=!5Msg;-O1XUa^uxM`YxR~z zZ_6K)eWOM$>?Vr+@Xt3o`&#AvjdT8Hb5<(n@0{~@n{$IXW{BzsziwK-40SI!=YNgH zhz zdmLU;7Hhnt*NV?rQv<{&_`Dv01NxnLrj+;K@L~SWzEo;Hzv3QV3b{@ly^goJ?rKV$ ztKC>~W5WXaOu^XfQw(VyUEqoOzROWKvtMXR&R*Gz=-tf;T1sVHX>sBcQ+s-w+bOBoS5 zJ^-k)R~Qk!mA3IGwcBocBss8AVUub)aC8K3D`3HiebFV~zT7`JpqYEpz)W_=0oPPx zM`|O2N3D1752K}w>y0tLR;M^gaJ=N_*>Vi})aUVZltAQof)@`4qz(b>G2rfLvbahB zp|@!r9--a^IHk;i7jD2ST>%Ab?^+0~paNlgY$y>lr_%8!jP#KpQXjHh*bTe0MDIXc z#6@c=t_6zNBky{y0 z{sq_FGc@@fCWCZ>1Zf-(60W)T_5f?1DXl%wVgUvoZ^px9G*3`|JxQm)(o^hph#x=9 zyBqIe-sV?nURk~yTxdNFcVFg*ItzQQvf$^P7hm1;V$hTulP|H*DwiM$6wnn34LaB< z9~Z=m-`|2mko0J411>ZVFD9)p+WIX8;Kx1bZ z<-50GGD)?@CL5pQi;m#;rOxqt?} zWr3@W%hHN1MVXz_drU&*2FtHf^pKRBz5-S&N*kuHrl)ncmIkcy)Th~aS`aHb-(dg2 z#eOS_Y9UI+T# z*0T$te|;To_3)B0&)~uXvJ_UqiD$C}Q66v$kB=E`B|__~bC6hG#V8k_LBq+=YZshz zY*0K{>ckfH^=vtZKiqfIUqD(I9kd9SPSu%zvoNBkO5P_|S7gCbDkd>XfQv>)__b4=NW3ooqUl+1B%juHuKgNTC6Q<5D|#@QX2-3=59 zw?b8+F49$t%bqI>z}}JIeolYiC&c$9bJ0jS5;|16nLp_6(n~4z5F0){$X7~NknzMK zaw?veik#abazwr_6=9J#CG+}vuZBI?Szn51h4SrrDBr$!@~u_aSvaZ5@QvHe1TT{x zc+svR#(lbzNXZ}!tc%2Nn%(SG9a1TwZ|1RA}IP-b&Q;mYOXLR zU2hsb=G;)ssL>G-9$zXXH7ewrnBXxm$&UtjchymHV#Bsb8WWfZ)K;Ahpc;Iyob0&Z zcRf?RWxs_o;S%X?c16)Hxgd;u^Vu$lvM6U&03ZxTq_~9S$W=sgw1T4+Qc>pFxz3W{tzUA11~65?gn3(JlMp=Ki?`BjO2_N$WLW@X`RR+ijmzSw5qEEm{Z$+W08 zvvOL=xS4xr)WLkhT|pAt-9`2n7|cicoj_tTjx!PUI!o!kyj>3?i8;ci4BE-EF;j351TW+n`VHR0PeEFgB2fJO{r>0 zpVd?_|-+#nZz>9YFwl%;qhn$PfDZjMVtEX<2ahRL}@5 z=HI>b07xXVZ+_ITO8_c?iKRU7dVwAEJ2Il6$Ukk%$8r=T-Pv-ulyPYnZOJYIXdG;W zexPqAv@~8GhG(4Imvtak1gtn+@U073=Yb%JFOqR9(6w<{OoFk}Mm`s|6E-9!C3Aa{ z_QB|6HXa>PlnmI2sM_TT&TgYbhk8O3>p-{i53|VOq&X(Z6<7Jw$#YOVa#|p>t53k{ z7rhxwGA;Nv$i_(4py~f(2|~q|7LLT**%&Tbb2Jh-0RiRjFf*IKiO(QX&@q*{f%q>1g0eK|>YW{h^5jQs9=Sgc;O`-tZtgsKZPXiB6bY z=PsWDP~!%`(lFqH>{rgL)WY#;HN&{eB+;;c^9=sE@F>h&0B^C#V0w; z3Tsq=eLMv{WDat@sOUin4V&UfAA30X_{1t1)>zXxz$yKgS~xL2f2r?L`$95 zF0Z%JoTIa5LMljG<6E<!NF-0 zYLI+Y6i_s)i@f<4Fh_NWRe`3Tc=e#uRIT;)?xrv{5+F(a|bbf(^aUM&!ssL`) z2PiZ$3b(2UHE=5j$Q4__)_o-ROt2MChIF5|xnt)3)Nh%SLt^MHjdth-pp>h1hQLv8 zb#Acy%uqo&5Jrb43XV5|reN=VY*pE{!41&rC>CV6C0^!Rof4#UBpv!%J_%AaYvFKi zaWJ!C!M#g@!+jD5_m05n`7q#kN+d=a$Smt@hLxE)D)5#z`!5MSC5c>`!zoc%zhqWG zcI2E~K-M#kZ_g422{JYbZgv3t7~lWmZPtG+Ahhh@M2b=g%4WpRz-bX%X37foM%XZl zQDbf?vP&@=oS_y3OrY>`6%7^ewueA=#oT#n%xci>#Ak{S6j_FF>?ey;hwl&dinbvS zDUq>BX-CD3`vhD#w61h$yk>@OwSi=dHa7gBye{BSJ)HwL!KoS% z@%E81P&5j}*LMdlTCmCLsEbOb2VMy!lgBD^%4N2cF7Ov`;q4R%udK#5AZ^^4j>Oxd ziJGvfCIiwRXviLtzrd9B1Z{%|Ugkf%OJyvhct%UOq?P6_yvRMW>J@KnIt2F6#O zjhm-gnx%HFG|-5n<+7zauf`aZt!gMPXFFd++CtinRn3P9_@qaL-nBa+OY)T~z8#CU z=^x^e<(nOYmX|jPCNP;=V~M%QbY{UseEJQIpbOeEk%*vgbB=o>uq#0J+@W4(gimGi zO*_t3x0wvR9^^MR>0ymsiUIRd(jf7Es;bIV%0{$G6k@tbd(&(L-5hNME2BJw1Sg>9 zCKANi*~DgQN^6ZiM3o82A&l+g3UoX5^%wCbZGS7ey9zjoWe}gCRREd%uiEkU1-@&a zme+I$%caBhGQG&_o=qxmP`tw*+MT_XaKvOmq?qzMd`Did)?s7Qiol+j^uCde>s{`7 z?K+FAif!66*Ksp38%Y#9x5eH!YxcmYgqZ$CTW8rN)#ydtwv^c%vd&Oqf+3;dmJ#`X z{_(DI9NeYfOc`S?Ta~k%GdgN_Nj(R@jnm zvz!vo*uYDB_LTSzT70%*dF|=uIHIk$=EBck3H-asE{I_LB*19h3=s=2vJ7#%*+85`lAWvyY%dHxW3idvCQ z@?=}KqDHQ4ZP_NKLU{hQH9Y5r8K~KrMO7*n~MZJua5cPLg5b1(ehvH1!KHn z3w%E&ImOzEem>5}BM_Pw7}djc&p-`@h-du1UFweKD`%(58!|A444Gw~%5~%XfZ^g; zNSA83WY|jdV%hQKmtKC31?A6|9}BjI32>J# zy&teV_FYvybB@?b^rEUp*virtgnx1^;EQE581U)#T~$3teQh;sp|zf|8@jB@3MjDC z&ht4Ky4;S)m~zqG71K8Rjq6p`o=#p*2lL_ngp7)9@6_wc>Jc8>zEiCm z<~TJT%&gSttf|GUkl(8QQV9gDICNEfUefC1&R~8t_1oijl@%!w#{MMOw(f1jbXx@> z5}+l`1hF}Bg@nO_iv(xfb9`x;B?{zOeZ`Stg_+en`Vm6zPv`B{pEfxk6djImfIWWr z;w1^!uLni0^c)sk5DKSkbi!vbA5l34>t|)_Ay`A8A{DHAz{>;d-M|II=*EzdhLs0? z=K76eO9lm70TrIRjXe~pAwK2FQ`4~IG? zaH@_vvu#8THw|kP7|xrBI7HMwNiZuJ-PFz4g<{QE0*VaH8V|&p9h!2h4~M0mhg!7y zeRw#m?tnu+wyw1uZ55__I{Y-J$=e>?fxxv7d>xal*NJ6LG}*)qG+Nv9d`M@J_wqHE?zB28@uRfrrGBgq zhk9sHwJIM`%`NCUfno*QFG&L=ZxpaBWjW?w{y2oqC5Ipci>Z_00FePUPXTb-U@fpR z0va;y7I(I^lh}@_ZA7R(=xROo!rP@^y{*(xb{iz~etNbUl z+X)xU30xXxENb${s<&#LRm#^bTc_NzGF9JG-%`=3<4W=f3;6^EZsxH_Dk7Gmmpzy+ zKDRU4Wv?_(8VNPW?OJ$D{aGw-EsmfO~ zl)7M?qx4mbwz%Rc%N$9nj%7^ZOl66;Pk?N7Gd&=67)q=Ebi`1!VwYoeM7~_Yte6;m zD|03dQDt6=2Whz4mtZxdcH=W;CmIUB&z4`XNH#9d4LBH$)em}AIZ$xx2#gNJs~+$g zUc=K25!5|ThbPCZu3}U+=09Wa?zHlWqTZ8!P`}+z`zs#6@vTS~oA zl-M85{x!+GZ>F1~^lX({MdvM9ucq<8VLF%RFwxT!wzFpsem_fjOr}`m<9s%qFJ4Yn zJ-#)@U`g`THAqgI7;gvTg2~q#^PCK_m8Cddz%RCW=g-~i@g*Q`FdRW!k| z_=R1pN9LkmeRMiverfgYiAu^uATs@r*`gaJDj#blK28?@f&l_RBer8_zg{|KWqC*t z1wZE*It@Po!@su@Aqs9PyJQgv6ihs8Cp3B(PHHre+?H-+Eug{V{3T!tvt(a!MDbV8 zHiI{G>EblWz;eAD6mN>MhvA-cIaBdLXx?pz5Q=!C<0;HAZ?*YupP?(h%?rfr0r#<) z4Fw^OP_DnR;i5}K0;FLXS7J1o^RxA{`E8QM|Htr8ved`%;qjawz` zu;6$a&uEaxM2-R35~R$oEYS=QbG1a`?U z+nO%iBS>gh4S{9y`K?>6G(sGi*{n|<)k_bkcM3#+1~C1#A-lY^p$sW9l^rz-ZcJVO zwGmUPZqwIdH3M7m2Z8HI+-lrej02C12IAH(d?fxqOwAJJ;c|Z(byfE6j?o}J<1-1O zH4ig7U|KpB3pr?#F{p76zRdH)K1jF+{jDS1m&j@8mk`&7m&VHMa-|UE&|5Aq9zXBoLyA96E`Ky zJNYXDv6geyg*o!=gKkx-p90Oyx!*7`=8 zm=-n`Lf1K=Q5-0HIc9r2+a66;A*N zrLgfZ2zI!+vC(i=gS>|wIY!d(jPs}70h=h3(!`~BROh-)IUDXx6D(|0$Q$B>+m3qZ zb=Jw0u;a)|T+A^bM99(9%6_AeRb|3U7R?ML zANIORah1({6)>sasZW;tq>jPa zt=+gp2(T6&@`BF~7Pwp1G)2;9Y^pu%(;7UKllO)WlPU^ii9**P*QqitiIE=LP)K_` zo9(LWPbD?CNK?*^_zyvgmZRlSznAU2gnzeMT{pR-uIpl_5_~$tubdI7Pt8^CV|jws z*InWzvhiL%1uh+YSLc)OSR$CP2cVd=c6roV0!fNrr<2kw1B^%PC^7d5Q*}8@R<3{U z)jv;WXEsw%2N!Ye{+8HtXF0zV2XX$EdguO8B>|pP>eF_D6o!_`i89JOxzni1(r$uB z3`~Fv!0~4Z2IrnlOqE~q5io=850wMet)7z$j6$-xq1CEY<&E!>^E>c`sKdb=<`J1x z4ikAq@gQ2AQd+a7RsAjDa+qqjz}MnsJH|;RVJNQ{F4oiXp}6` zr~}y0-4U|D_-jt7I$jqD06YoT-AjD4(KgZGO|73(jYl;`UZ*$GB&>RD=)<}}K{;1) zFchCrS>z`(Ur*YM`J0FLku&FWZZ(E++jtO=JXd{ctmPjy7Kv5N=C#@(+BCGRnx-P; z0lKX5;B<5ibYjXM-9HP;OL^iNC=qNt5D30fSW|3D_YTzB_Kj6xchbhnCK{V-HEv9) z@g8-O*2*Dv6y~Enaw%$kRO6omypznTw^mMPpe@KnmETIUcDADwHVgNu|H7W=wE&!l z0MyYQWdqeJwB4khk6`m}Rx`bt;T?j)b&wm^X`XMa4pyJ&nAfL)$%YMpAK3Qwz{@@ zFz|P(K-~*?kpbCjh7{MC<1h?tZBtE5Ux5+ZKa}8 z8j3*$U3=RkfD!_vbQKtu2pml+;ezg_3bbYxzH)aB<}9=2O!RPdAK}h(!U6}ux4tor zz31B!dqCl{Ii2wLUU*If@Km4h08;{2?Iw10Kau1T^u0ZG`^~D@xwSLXH@Yg~kGf*a z)FqbrVy=swglN9lYeR*<`KGQY%}?{m-AT%?_zjNJPrR*mJ)v`l4d&j`Ri&s|-bO9? z5ry56v4n#PA!W)^mn>fB`Pc=1e1p-|+6vpt7Y~7! z+vy7X$=v9F>LcY3uw~>xCQi;tqrmBRuIH4;l?2e-C=qN_P}s`=vwoDgTPZ8I5JamJ zP+=89v1$xb2yI6YvVfnjf>eX!zz+~UVWL(Pd%HaN2Mul(z6)sf49~zU4XgWH`Eji}E zRk(-EN}#+>h#iGg&uyM3N+b;B>^k)m{?e3qLAyZ}4c2iuD6Eo&R3yhEku1tjmCy-qZ)gv23+0@?vYsi$lkzuWXdN$kaTr-qUcWOpMv#n-Tr0f5 zhO!af`IM-Q!I4DrvJiJ!TL-D$RW!yu2`VJcnJ-61aAbVtEu4x)vea2S%;R7S>r=tLY|!aoW0*sE z%t{ErVCx;_iV&7)Fv9-Ith1l9jlfh-ExE1`!H`&r8#x<7bNV@YKXm3TrI4~7STFu% zM|UECq?TyeC;g#zU@r%cO`Cd1%vCqQp!>^5L|K5u&`Rg%amQeh@}mn>sZadoQtVOz zYn9aW!E>b`GFjrkEqD9D(FWJ9@DXMmUUZr(FSXz}U@V+7w0TABfEsu|2qX1~Ifj^B z0^8|emOVbe3+tW;>HjD*^pHJtd}tnV88Qv-T50%1jMZN`G>HrO>_ zZcYc&<&;HrNMWb~F5O_D0th;fs5^KBi`}mbI^R+*)^`&1H_IWXH7L2sQvfsL+UN*+ zC5ueHK#T40p56)@0PqaA`2>!~A%arHusgs`!hH_NV=!e>sTo+L3#nV)km>UvJH$vlo) z0Awp_hGybC6-keG%{de(Ecd%~H;4OF-HsKTzJJsJ4R>-D;MmK39zs@u13`3l-SCWizGOz;AFAX7O*LlIw8n#O_jhpN>2zWs9 zPEMe^MRqowzX2L^FgPV|b!1FXOeC%VcK1U{B?fMQl=JS#=7**g>^?x^;froVxV#Jl zTtE`N21Hh|ABls7XZnVp(9LL+7eN7bT9QI{cD%3S=@bm;{&5OpG#&x925>#b;U$GQ zfwI}0Ld1W73K%L?eh!E=!C%i$A%X#Cndn|LLFd?{&R|Ao$V;9VKQ@~mT(7d$FN3qA zP4x5PUyub8fG}Ddi}fM{ARjWXJFN%#bbd4%v@Vz%wAdMxT085T8+Q;Y{@7_J9NC0 zBs{b)B-LTVl`v{s%6&*pAg4Jwq9p9(vN)I&tbae6=d>cQDb9V!<-*cjLe{nYDD^|K z#Vp=pdY3-8skjOQMbF=CelN%WY^3wymGPBEScgc+RN-q;x&*$@348)y!Sx2<9mV$q zOhGwL#$*5hDUJlF^?YQdkafewU{CbQ!&iUyfyFUr5HF`s9=G|YLY`j$);If0&_2xb z#TAi75Y;H&hZ0F7H+owe-+doN(;o&zm+N>4PfjX_E`hY^Qw;4Vk6%t7)A#n|L?Jvm z`6fA{I0y*@=~H>YE`b&KmV$NqU;s1l*+wfCQ_pJD4>(<7`(p4FtI2upY2k?fUUk2fm8^=}np3IJ8==N?! z5vA_C*;W{PsBy7y&|>Hj-ZAW`4ovy1Xwy%HAU)H9yn(|SN3=Y6HPD8^2uE%;qyd27 z48JtW@OZ-xY9moQAHN+^Oh-a{Bzi?e;M$ zr^X~(86FR%jsm6&xTZldWw~-VAAq<8{B=R`8K4#iU{VQaLF%3#y&ixrb3NM(WGA*A z>)*WTC%E?|0xq-}6;@EI0Bu^?;utkGmBNJ$Yf;Xoc})5sEi2I|v^L(gd~qoVB$7{T zyWc1LQlPzElCy2F$F+@jYdswAAF-j$ZXXm2H?XhKy8Vi}Tix#}>uy!lz1h80S@*k& zx_|C|pRAjT9vm^P3+J_JsWaUGOctm$>62}rHl8X{tc0WONx}x91^sl#C=5vQiTdXA z&t$_wq)19_!GYrA+Nak@6I&$#7BT)NBU0(yJ){ECeeyn5FOm@4fg5Hi*^D&rTJf+| zFmGH1Cjs2(n2cur2@BYA7%&$oJ;5iH+CDztg%kiF~NfKU<}kdXv^8=LlP6s~!%OvmJ79R;#8&XgWdLZ^vFD*z4f?C7Zr9sw+xgDAjK9O5 z*VHqxtS@2Vrl>+_DWSs-ZfxAz zym@PLYh$Zido-!8z0v>S=2mJdE&iKn{9RKs-B&$M{rETq#OV|FysF=R!&mPiwR)ol z&MPh$VlB3|w)*`Qx__g8v-e%9=YFm0ncJ6h9yb*|JH^6A z8EOhQNCXBYn+}RVA_=jdi4lE#iv{~SB{G3M$NzW#o(T*%nj9SeawHdZaz_v8yE%U7 z28#oO1w>PxVSIJ6ym#m74g{JKis(cmu3e{&_SDDg^WXA=tp|@J(wZku%~ae~YbteI zjz&`k*f-#WP2gK18X}`iZW;?x9EtL=pz{x$fyprRsrSzbTz_S4Pz_-OdP6JvKm73h z*2bSVzl&YuEd%4PzpNT5e*v^%>vd&c!svHm%|~nCX0a8N=@b3-Z#goY9KQd@Q5E&u zg7t6o?QMQQ3ZSjK_!AO(dOA3MWrm&&BM?wR#c3uFs);lYzaZvRU?W1IBk{{SjtohK zF4P{bD0hC*ATxpN4C3{IYtAWLn?XdvG3c^}=U4(esqaL~1JbtxvQ-+8z8#RhKOkJ< zJcCeM&}+6OgU10lhh%Mtu{2uHojp;TUiJ_&K!715ju|oc<)hg2ix=7SK za-=UXGI^ViGXl)R&9nCsCd)5D0oG76axglC#Ki2{IwH;HP}k4e=j%9u9fE^uQLKj( z%QN50p2}?F5|h9)5R-m_#z6u;AFjBDc3}nW8CddWrOQi`7I_sQFoh@jf8RqG1q zGOa9b$GPnchX26K6oLdsl~1j_#Jsly5{^3_c^y5cCfKU) z08F3Q+G-$yg^S3JXJz33W2bd01}~0|pdvj?v{SoOlz!0ibd6w>SS3Ck_~qr4HadIN zAB{a1DrMsycOX}Ez&U7pA;T!pk^AQ26;c2r_2Jxpc<+CBuV@FoW_3LMJa<3eCzD+# zY%r-aWx@j$f(SE9-SA~@@IXGmB}3r~EOEDv9C_PUA<0@4_xb zS+??#BOx2qF|iai)od>*bl`B(5n>}Lo16u6W5)@W(-Y$PdhfeTx;xECuv4yqf4{Zv zt50x9?l5ZTOl%vmG6a_YNeK}eN*{Q5F`U7ULq2)>VEi+1op0c2+W!WvfPBkQC>T3B z$`%sN{Pt`rnY79${{N$i;e{jid}>1C1^dSMK$kRoo^6FKHU&!_>y|YJNfo&>)dWh4 z^{a~YJ3&+sT+k@@7;GK?w4I>_z@UAaC|?=BXnT-a$9|+ zO`=OJKqRs3w*LK=-RcWPKAgJ`=QWqk9rAoeA?a*z&ZLzH_`paj31Wl7h)&&MWE=y< zUk|Sg?15KEL==*sz_ob^enS)-&mnCra#~! z9MRYm3hKRAB8M(sL(Dk8h()A$^{n0`pk=KQNMX6yQ32&CsR(h*=GxAX_31LC+A+Wq#_>xNuA59+qR_f3u8asUlx+3k)kZ|8Sgrm= zJFvb>Ky10aNF*^f7F zK(y6$SWX!4cYbu^Z1iSy11J2Y6PJ073Q+lnROYjDk6AUCagq4Sv+Icqb(F8%^bI89 zT)3bj)-Tt9*+1|r+NCJ3(9ma%0IZeNwgx9Ft?Kr6`RiV(R;-Ycg6-1t(QZUxZ@Yp) zuoF3+R6l9|ZIx(dFw|}_z8Imi zMMi2Z$|ot4!z=85^}6E9&#^8QM=QliegwgH!9{O0oRRv@f*zpogX>BBDl_u;>GnNEnJkexkR4MMV}lAQ4};?Bh;JY@v)>wkM}_GHq`By^S3}HJoNX zQLs6LmEu>++3y#@&s>;Z>N++K{qfi$8f1P?s<0mu0QGTGqIN5AgC<1i0SREcrP#|@g8 zo*saoVsUtigf?evU~D)Ti_MLepnE2OrHZ?B*v;UKAlFey0IxnnTZf_Y7LkCqyzrR% z#2{D^wLC^1u;&2WVo*xy7>{${m6U?n;0VU3An!F%#nUkZ>*6S98%u9xiW38;NiHu{ zD85g#TKWY3a_-A%F*u^Y1J!L(VFMG1FTUW22H)a zINAJ+I6%AV;~t^}6XC_WDvdW3;T=}~ku)tJ8k9^|VVa4>4VOW~6 zzJ$1UR9YAlUS~evKPk|OxOIUXWMvcQevK z|Cp&2#{s1FInIp4SBfQIdzIpMRRm9x zwM+^X$MUL-r&D{1$N1mE@xRPwK}D~!&ClZVyuugQlC~I`H1OAE>|}mDfNM#MF^&dn zjhTw#L|LL4T>Y*64gPPfS-Ks>L`7{gw4QW<`G`uF*FjNX-fNXL{i#)$DOR15DE(qc zA}HKRrwlTaZ-}`axiBKVg`0Gzqk%Sy0`@70pY7Jy|D;^E^XT0!ypP_G+Ry}cMp;YI zj4!mTZP&>@z34QR&ov4IdLg2=ga3N#&=p~Gnb<0`rP~vU7d%F>h$;}jd3%7`ZK!nb zZmY-?R-}3Wo3}ux6fE1R)^JW~xYu~mt(62aGhx#(%Z}8O6Y7Y;FGQ5g;`at3uZ`L4R^AJjd@9uSn=gek?{8V6AxtcQ)zlUy0p}1Oc5*YkjQ~rw@vu-K4TT%GL-r=V zq8kn7UVX~3w)=Il2MpgEIo~bwlPLU8U08UJbbq6pn#8BKUAt5oYn@Kh58G>3^C!I17>y9k}2t76$bwH zUWg1(V|O<#;i0~P-V1W4wFy8cN$5>rJJOV$A5fH%<+T5di(ZZT7lXIp4O>b5UxEC3 zhnn(xOjYR|02BBH=!~F`1H3DwXh<+$&*sx3Xyb%@;V0Rn)4{*biC~rt(sddrTt4(K zkeCk_sG}n75y|v&+&456W{8?$KsYUA_)+m_<&TbeDu1&ao4qOYHY1_m4bOv5b{t6a z>4QL>14g8v(cPsoER>Ms!;9Y-R#Tb3lxdH&=Yha(OEZpQc_p@Kd}t!^J&)EQC7*t` z$RxdPyrs1d7Y264p~J!(!46Iwvybr2gd>6mN5t2KoG83!gQ0xW5xq+cxg|uvHcF=l zQ-RKs$e%H4XhzjYCTqNdB1F>Y@K_8RQTV9w2=sMEBvXg;z*udVN)*y{;#WiiPm1RT zI-&wqL_-2DQBJezc!)Q5e2KR&QktPz3Ci58xGU1Mm&1q!5@7k3M2mu?evgr8P+2~L z-neOHm8?|*y&jkJKCoCVr0E2RHU_ifh>?61@gFWEFw}QctVy>^_`Lg-`2aydaTl~8 z0GtlNAG^P*;-3t@P&dIcC0pKYRera$9zj<_qCYAQv%4v9XThcMNS_|7Am-F3?v$uF z22zB+#&FqC8cD3WZm>$%lA(#pC5Gi{2;gOqjfHzQB?FMR7-b0@M)FBFnz5pbP!S%Wb= z7jh3pS1ajBu_Msw`a7!=WJs^*(M2nDC~(0}Sv6%Ghbs^xvS$yE=v@}J_2PJX_F{@| zd03_3Or>6KtGCRaHLab@fyqIDKj9mmQYL~EL|k2%&+_DzV1f#5qC`=clEGWAJ=O*2 z_gALoa>UyPcj3<-U4tL2PU)qWsF4Msxuv~n3mVsiG12SuMb2?!Awun9zDa?2IasXJ zKAwt;uL>=NC2c@iDe!Ypps&bU5{ehgRwuiW@^T5rsJvDYlMrt~nA6diGW~LyK9?~n z008q7mUCi%5CPZF6)5;~w4h6k;NpD*W=CE`TbBZ7)dScr=ohgw(|n~krXxycfJM6! zN(}y9ctAM&0j6`1HQ{A*NTq+!x06y&-A(ER#En}txiH4ICsGv2Xw#6SIRHPM8mCd_ zU|qhQxbA~n?i$N7c-u*1#&xI%z|5%r1il|{Rd=F-J0fGj z56t~k;M8?Ykm3xGXG2YE$o=>)E;>tw*o~Pp%qY#My-f5bmGW4UeHHyevv<7xRue0y z(7|L9q`bDP*pgVNx9!8>;aPjJ)=_EM4yQfJ6NcpU&YEXMljtE1(jChqncix<2c#Rq zWuRXsHBvyNSTxLqBY}f9?(X((?Co`Qi^(Qqx0P-XyJj^b^U$Wr&RXj->Cp5TQdXe$ z#FWCx?zqp48LmAycV2f^LhFiHmC(>xm%^=>T7m;RJF&%BO^Z&e#V*3Xj;O_3%8F<$ z&yLERMP5Fq8qE80Da?f6aq2i5E5MjP4nxdNvv0u1P|%QEqU-rO+&ZGD6;Kt&I95cx7S(8*VQxM~-ppE@hVg?OEd(LufE!b5O@Pw>ti6`s56> zk64LWWhOOXP=7D3`lI0bh1b3YUfa9+zDv8*W~eG>o}x{sGf(lp)~ToX4YN+=I7qx} z4~{YH%M0MB#T`zW19Qf@@}Z#(TZwyrLZ$%4Z<^66O3C;{3PABfkY{8Nw<()XIW)?;|wtV)UZrg#msCLdnDsQT>_C1ZX}O|3;J~T6q5jvJ^gt4+FXf&)S`SMhp94b?r}IGX_MU#q0rqs zFa8E2TS!!<>HIwAUnx06i|l-QN}1J01&eM6^U(~<0taY#Wke=2#LE5zkoTWIefsim zFQ`!Qx5pgDlppCj>6Eg)56Ck$8y|9h@6nV%j7P8M159lf^UjQIKJ57Z1LIqVHq&BZ zcX3Gmwzp?}j8r8gN7EhGnLRbFChK#ANDt>Hb8MrCQA!|}WN&n)-BugUP(Cv?g@9GpY zc@^VDOD_(8q-khZXi|?l{`i!(;AMcqpm$1}5bw}_JQzbPlXk}#!?MrMtN9cpeLfE; zYcob%Mq^l94jplult_UC4ln=~oG-zMtqs<+jcD=mx(6g6Dhve9degS}Y>I>g zA!FW6%xj1@*tycGw@#blvl$y0i2F!j1OPj6QYbuN{#M&Feq-by`eRmm|5%JZ{uc7L zIHTOD-(f!{0&glh5O5Gtsabr+8!L5C-@RS5&5ius+k9l-z3muYDh`E0_2S*z9tUl6 zWigC|;widAV^+CvN#~FOUkO^m`+&wiEmZzSQi!9C$~xCeiiavEnkx*WJQFQ;N^qc( z53peSi1~kq&JCWv7nlft_&#-w2BZ5t$$5Tik( zMwXnwiTV_iDGDIqfElqQUKoF6LE(xdG+@xqbNp;Z5iM6b7nRM0ZL3XAYMm4+t1K!v zr?9AaB5@J1L9{HTrDLkbC5uWO3375d(nw?Z(P?A$x5)#yJ!r!$@XBY3atX?cMqViv zpb5o%_ed0Dop%PBV1bDXo7S6zw{YX%sV~H(hSM2%4v-~&k=lpzUATgv> zVyP#W(iVzPJ#rI}aBCEdXGA@uJQmetkTQ?A4}C(xMC@!~_zKeKyt2jloK-vJ&{9h} z9vGT;I#66fQR#ttiHmd%WiRh}YMTm0<9#xMut+dwClDu_%pG>Tm!pn9`<|&wL)nvsqD@-+1y3#n#$0N|YF2o70^u0J9&Ej9c+h~%_CnmZOMb=o_CAEsdAmC_9>JQyyEeI0RqjC}Kng(htUHzW~ zFyYgZ19VE@5(BVP8&+KeE7xn?EfEZm+<)tZ=sRlSj@MtAVW!vRs{FAGp#4<@1ejpU zReD3{WedqAef*EwgNK`ns_av2M*ivVrx=Dc_Ej)a3NodOu#Y`HrtCQxeu>cyMKUb_vB5J00yjg9792)%Ht#cb@{L5ztqJIv%|RxTkPwhGrIFgjg_9eB<jby6- zc>P_q23(pquXAhl%)YAndX+}6vQdy?nv`@=^eiRtIp~kES-2^)XAD#2TD^X%m+D<@ ze(Sn-$>nEc7hdmGsJH4*Ole*oQG+rc(99>B2kz7FMX_Wqu0Euf=<&)`u0lVqI7p|x z+29=GSktn#PrVuZ!`b|zdG&(5+kYDn#a5R@$=3 z=QXw~i}CtqzFuqBLMEkF!%Q(e!7sX)cBN>UFNVlYSSu`m&8)rrxS`Rw-Y% zY@Kq;%2a(%eTy2))-ZG}U%{96^D6i5v8zIm0C=^-#oXDVBDw3eUZ+#jpW-?_bIo1; zQr!tq(SC>^#lu*cI+%-im&M6@-&gry2J}W!so8z*8P&uppA6=2+GH?w0J(~W4xgsC zWUv^{=5W|%%Ml=ZQ~|6+Fuov|krYQ4`o`Iefoq0RXTX0P*ePhz^EiNHmA`Qc-3qS* z%%iJ(4lqxm_Bg<2an(eS0uMSca!ISgyu2nGr_TBw6tT%Ty)rthxJy6}cGQsiX?n<@ z2VALNvg^X^b2zYAiZbx(B&H_QAts~zmg~EmhnR>`UY^_B>X?L5USgJYXZ$8Z0KP{U z8};B54g*&(?V3o1gllXt&^v1BV64XmV?8z)>s5mR*9HTd?*!5frANhNbp}cx#m{4D z#wgEM8SIiVvPCJ=BWLe6nXU*{be!nRW2nhFo)Jc5w0iHp!7nOPraZqlNjK@)4o?7o zUlN@~n)SzWh-nn&)8VPfOnBYv$|SpW?KnSoE?>lk36itLmlLm~Z+IQ?o`FTg&*$d( zJwLyXES`foWXJiqT`p}Uug!Y2_*C)MdNui9(T>9;kd_mXKn)^p71DLHHf2IV<__xC=}ZtvXwdjI!+=kJi5 zPoqi+Su18PGF7Lpil7(63#=^>Hq`J_>;k@FN>U$vJ7v9hH{h$<()?(5G(Kh*eulvX zDsxlGhIu7Y`oP5$3dTqq(4@t{OYKE9=LC7^K$knrMX)q>sNZE!v5xhTT4CM3&$HKiS*B43UGQPIZEg`{RLf%cQs?n2DII+t>V$YZ*8wxkpYdhOfZWYJ?{h zOg!aH^cgNHHiWED2bc!%2rYh)QmNF$Ies|Aw$8ApR3Rt_mJN_t3egh`hLA)zOn zT*joPetn+JAZaCHNQWTU>n%7h9z;mS>{GzVOV)La-wxnrE~-0(l?LNMst34Ep)YSB z!PYn*9_2^($cFC}pHcV|ee~fU?w51XY(RQUa=Snn=b4AG0<5xe0N}=|_Jp;}j2=t&b9abI4^5 z)omg#BLqTj8*?yXKp;tycxdtFLd9Y_lY-w-K`_gM5gLqx8dy$M1D_MLXrk1zel=L- zqZ9ZtIH5b^sXvbsNNCO531)?Qec=)|nzdR0y_!LdVt)wI>pqPHCc7v)@ywh?3|7Iw zZKk-ghl**Xq7i6icb5Poz6J08ADd=MY$8k^?QT;BkVmZBRAcJprtCJ|uz4Sv&7CKA zEp@S8{Y^=Bu+N2?&AmO&9_bR5>ST*H%V?vq`=Le1VpK5v(YhebuS+^ACeoN36g+eo zSDhTx`@V}QMY&7-Z8}5BZGe_)ndG9Tvh*6?!%zHGs%gt#xL8KuB-M59U(qa^tz|RX z3Q};=>|z!*cGEKO9d~2qm;w*;!*uSRT%cYaGy1^W!o=lulTRP*vjJm<4_~Zr+Intj zP$~0hYVp9~qpRXC4;NS2Uo_6Mipg8lfyz=F`f=gHHHex%Xv$P8Vepl4)VT{&@OUtL z3e)j$@_%V_aq}K%H(1*+7A~rhQOp4}W<+7aaLn4KLdOqzK-P^*>tL0Q&g!k0&`K*b zLs5ENfD|-kT|P9~dN9tN?B{K}Fe)2&{Zql1eC=it6c^Vil?KoY9YWX^U=Te#9q0Fj zB(<0AH_*s_Fl$L($Y$Us)$TAqyK3pnuWos1`!6ow2*hk5a~y=1@S}~q^k&Rk@3Jh< zY&t$aGHRB8ZLY`b%ICn>xMbMbt_v20{*H7#CGL)`C--19=M&iAa8rf)+^(tAPgi=8 zth7~8=~b%IcNOS`mAHc!U%0Q8xi49vHTW;Xeto`77qT@sWT4nvY$C_aJ{ZY;Q+%}pfx#1#PpR7_Eshi**g*=tJC$liX1;XM>0#Hw%$ z5YKo3;q2J&1v6qEniKVGaL&O63;=o0&R>{ByjX0O4@c$z_D2&lVTO^h&cLl*#BXm` z5ZohDizQ@Qy+{Vn2!(b_+G};BtW5Iera7b&hiHh+k^|HrDJSS9&r^sfGoZ`1;5{l! z=fLnbYM;Q1=@BZewlg6V<~`y`&i!}uL#=PYZ>t=~)L%UE@JIP9{wxk`9!-3W|D+G= zh8f;uffY~xh8_J6mQZ`&gIQ48qwC=y!o|BX_h6PY9mD)!j&FSW9JmZMX~HbXv&UH2 z9Ytm2$v}qRS7BQ0EORO?ev-YU2U5+n9YOO>F*P*IJ&^=amXPx@01*p($L^*c8`BQK zk#&CX?k7t#A3-qHySIOxfjef-=T?XBUE+8lSw~AP=kLh*tFnXBg-N66;cX9pv$Djj z=uQ5sDc05%%F#{#$gJ5$CkST-nBw-8?X?l43j4}>^|}DaG!xr&XN~Q!B;7WSwbYFl z1u4LpS8=6F&xd@CB%VT;x)=Y8H_u1Q2u!( zE}Hp$g$L}}AzDUG(dV86GN2ag0HUC|t#vkVx+#?2J=L6%8~$*+u9j30o~kHO7;;Ac zcC!@)40VQ?pHZp9o?`r?>y94&(GAZT{TP=?OnW6lyRvK|gRO3||7$UYg*KcXoSsnj zhh;2wog2bNz+pGMD?@k=nCwQH%ciRP6ph;iqH*6640X5JNHO8fMvB>P)aIy7=QMk4 zI`}DBW;-(GdXs)j?Q77BEoZeq8`k-9mz-tBx#Irw<1xp2h1>S{g}!q&FIorZV~i3% zZ(U7^SBE42Ou1%CM)`$r{?}D$>P)1xdH1~`6PrtWFRTu1`DdS_ahQCuK9ojC_zn zhE9e2cs&L0uQT@k!29><2m@iw=A$C-Wv`CWuL_7G-E-y?Cv=D@r{J;`GJ_1qbkPIX z`?O#=fg86-zFo|Hs>bdB_ydZ>$faCFIa^VfzoM7<9#hD+t-t`EOy45Pm*;;Oz~$NC z{qW^{FdUs0nj8MZamR*PwTftb;)jd9hF{hWhvhiG@X|GT>BjqCKNZ8Rcfob@-TU5m z^+t2Uel@#jnBS$RFAV!-8Ft&xUn3>Gmmf_dju73V_+-~+ zdF7!vBuntKlB~L=1I~NCPE~xt#>kif6D@2COvj5-LOuY)W?G_`5Jnz7eNo5IC0}eh zX#*}K!1_nt8399z^&`dls#twXQ(vO_2$nG`TfabujR8!t88v}%BO^vArkEB(9Jsy( z*RKZG7jXRuxX;N7cDaONr}gRr%w|f0(S`aSPlAE1b2+JnH;xlr@L3z%6pj`5wwf_v zF>jPr%VyX@KWL%vx6m)Q&@Z*n*DdsaZ^NFLc$QIM0dV&|K0V4mrDe}%Oy=2)7VXE1 z_Cuiu=5vYMvsoqhK=rchW2Sxy;C?mWeoXH1(Y2zF|GK{PQ||q$0?=k40r8GmA_Q&5 zrJ%^TsUG$(*~9)7d)P1au)n;A{ji7smZA@i2vBRv=nx1#$p4rH(^cfQYo|@U?Hg{? z2|pV2rxks4%U=Ya&sFe&8M49-)1=sHgV(X8=Sq94P?zr4a%%`hsB4jgHUm*;vs*g% zyyul<9{B=T?q5~^1cdYbf+5|$BD!#%d*M$_;v;>4De6bKBmn)CxR z7}0}!g-r+>Z#eFc&cPi+zZ3c@q(~V?&kwD1HU)xTE$)d z`#GV}v?z0AJELq8oBkZJOvVadRn#!Yd^@nEMGM`Ptc6#w6*jiO#(&LO1Frzi9m&3J zv0_vIkn5w)$*Kqno~@ch*x#2n#tn|V<%iZ1&7D22&^7TArxjMSht-!;XT?(oNAJs_ z^X1UV+ShdI2xRQU5{NaArJulA*k}qWAbvTy{x~NW5~a`N;7XrcV~kD%=64f`w+pct z4{{EJay=5*KDMxrEquJ=%^>|LuS`A{S<8*@XL|s;>0uXQe*AJi{)-D``mCxx1Ycm# z-Oi2bLJv9M2A75NQkKb$%?*9bt$Z%7>Jk8MUn(vVNX8pd5tZ6Ij=w7d2Pq{%X$6fy#B+S*FS%D=XJfi+0E-b*E1y8 z|G-REa*Nu$DoS6(@M}8sE!F+(5B+GwJB;He@m0)`QyCPm0?hvv2S&fZ<5B?A5X7nS zvMp8j-6j)NmF;;NfVLk_z)1~67{?=89d7Cq72!)*gBHgQE}&8!wYZ|SaHtcNaE#L} zJ%*L~5*fVhI!sy`&Ld!Rm%_2UaMR@rxiyeG zlC&BUvWww9!aOFFT7v^S)1ArT2p0uL6NE@8V6AWn!TpZGj$vVQd`NAu?tO;=q>piZ zEFc;)g=99C%mngi3d90XoHL)y_9~dmc%C1OD3=B7Me4``ausInGl)8Pj0x$-Lre@o z-eYGI+x<>}|J_kB8xPK(BW^(bdYi#_N-Vu$UJkkR4uGOXL}V=Zh_mu=NVUwDgTagO zlx*a^3I~J7FCHwWbTHd*E{6?-dSuhI__r4j;CLAMG=|ZVNk`&D;b@GpWfPB(eDby@ zc|d6OOon$La`AjRwia%5=yApu@chmH(v-MXoIDysdgm^AbP`-gLSLEa`&5rl3^wg` zCnG|K8aY#L{Pi2Ln9_vQlt@}!S~6MBy@FD+GJmx}XHEsfQTj#2zp>9%IAHVz;F&CooY^0s8KE@ zGo+TG*WXN_O04q|X6S6^fQdHYCE)?H5B}eZ78SU5HCspw2<9 zOkn$3>JgLCwmON$1C_~y?j@&Pjiem+%Nb&xEO>A_7iu1CvFMsS0qSUR4;G~BL2@D5 z-KHRPQcG5q>B-c*#Ow+`S)41Eo!i;-9Js~2)kzjrE&a%a+Z|*PAb}W!u}2;NSquJh zLo!-eg4T<#(=a3Q^3LVM`_uXI;~`md+m6e+R|t~J=EI#u^Abs_D+ZTTJh-^%ysS*% zZu~F~&0+-EAO5R5JeV7gYtlt}{8O!yRWUb9k5uba$gvT8&|_*0h2sr)D2TSL1IyGA!5E5XstBpYT8`0wQ72+gYaoyM}GRN zko%US^YR^v9^2N0*FMP9VRf2qCc+~u$Vrdpfu##|H;-Y{viNqun zZf2tahG$tJX@dhau+|YOV8s zccv$=K{y{~J8&6cbOZ8jJ^S0FRlC(?`?n@`Usa4Pgb=3WL%~|RVzZV+9I?<;1Z4Q3)8rDOS_@7%5qu0BYwKXj<~M%^+|T-r4|xMjx{je+_?6oQKwv; zGMDA1^kgw7rB2Z0tkxj}3;gJu;$OW5fB)pfeVPV-Vz9(EVc#*fiogj>~N{2uKweMohA)gCA16D2W2>w#dR z(5aEwNNi1SG^@JfgSIKawBp4;tF<|dpt|ZE45mybFIrX-F2YO=`vU_^;l{@8RzHl+P#ovAgv@NP2693^q_S@ZtryS6htj9shnmEPqp zQuu=L6Wp8N?b)U8NJ2{xI(V`-tFFy>K&mcP8j%S#7lbyJ#7DR)v7tT6 zwO#b%RWdj7b#FFIQlpgA@^ezea28uvymBa6vsH^wjj+w|<{6>K{l5Md+!I8RUQNs2u=n+=>gXFbchvQ(;@udmu8SAE8hRX+!rgq1DAgN57Ao~C zC7m_O3!;unJCg278BSc2TH0d%5fspF@~idC4Oj4Lx?}^WQK2lZE~B);4TaVU`ng7p z23A$(T99t7Rl~oGI(7-%D{FOZAFiTP{g{*T5mcfL-RX~u#iw5VRylKAPf3?2(!^YhoKWC{y3S&{tMgU^R|m?jS;QzBrvvg#D-Smp43)qBh{U zFt+zNe(0k(Bps2N8soZH(&rRMFJ^<2`=c?)0&O^JP|gN{4&L2{qHmr~I@XkX1R^>* zCo02iF7HG;V^-De;PqSCk`FPl}AAJ^~zhOVH6o!z1 zQqY>3=R;Uf=-&X0?+`qn=4}g)xh5jI)&W}a|EJJY|VJUH$9axKZS{D~~OUg1p zyF;0D{3QB=!t>I=IUS7AHV}?YnvVrguC}X80GXFR0!G`wb zUE}Gf{8uPJTOxziUE%GZs2ATMEXwQA?0(ZSD~yciG#-rPmuOWG(+NxsdL)g9qFkaA zoPDs+g>Xtk>l%W!2i`l)Ku2eNwGEmU0k6rXQ$<!Vv4a1JP7HqeY_cN57CWpmrUL^1`sD?H@2qs`n+>jS^oSe?p*NnM!3k{M zcbq1U_y&U#GY=9w!4n%T(g8$UGp3(xJ3Ig^90Kfk)ONrEXzCio9vS3YtZTrzK5lW) z^{@ds-?*7HWGr@0eRUl#LF0sjm5|&ukq1h4ZqsUg-AeNrBKxJAm9K?5xF&)Ab3&LQ zxW{EjIq5R90ynFrwr7+J6J+V>$!rYB&%qrU$T0*7q4*#MjrDbpUn#XMJhgKkD~$#& zmBL>Zqzr}ExJ>b1f04Q&amsnaB6`=14F%4P#66rQJv5%%o_p2aI4$t32K6On4LSk7A8#Q}a&UWv)^PzJO=aQCKo@^FI>TiR0=VtB1uDyeG-un(IOE_nBh> z!*P2E*5AvKVe+252io+Zrz756Lb?iYjh?g?>`DzcTSz0$pleWOI{k}+Zhb|U>MLXF zLq`Rs0~}fuS_Th?YS(+p548v>C`|0Ab7v2Lttv9`_c2CBOrBGWUHoYB7h1iwB=D9f zS0l`fuusy2$2t+0WSVwmkmJvaGZa$rV8J^uU@;HrV!G+EubwtQE0YZ4Na6&=?_lMF zyh;#TV&zbq-Sv$Slyt9N?~e8uz8cL$iESm(c!RGcc(&IRNubrK%T^Dc#7s zjwXWsYT#LbZ`~#-IZ=tMm6b(4J|ugbC!mBGWcY~mV}v8}pLF)30Ig1KJaOP|7*a1m z4yPF=!0~ip+yY8qy{3~`%XcT0mPWFK>7+q~)GZO6^kPS(N<%23I2DVnW>47*yN-YT%ZnQY`FR?p)H z&tEiM=0Ld3W^;)trza!bdy%zqa*X6lS4Zn8e7w%3abgugP8m#&@=lN84Gs6% z8vMOzD)rV&#s-B%V+7Uo7-ckIYqNt`>+7$JR>$)DP*F7OA0^O}MH+iN^U!S!rZ zRF?03+70{Y(bDZo0mbrz;|bqTbV1eu)maPgDd@%mExT}?4W~5T-Rw2?41$T}W6<}a zAU=3K!t=hjY~$mg%4_)fJJ5qikCsq_uHXoNzYqrD6+a$j(4;|;f$KX&AzDYa-guiI zY+>MzY&L}v>`E=Xy&F#lboY}%k$whuiDz(a{FkOl>kC^;tOB}}XB?O-IeCYuE*>vN z3;2cmm#0#;WeyJqx-Ke}>27Z^m>f{~H+TyLmr>9>^zzB$2vzVS_?4(aeG+i{`R_pj z=9KT<&B8j3@eP^_x&eNTx3`nwI44z=*`c?e^A6Q1m{sm&x953~&7P;H-+`bQbVGu| zy@$VD=>YugI;8o>7Yg$UDNOd-T)}s_7scu8b-JFxo#ysR5Ah!0em$Iy->}qlHl4qr zSn>H{;e1hqp7cIC{o+|fs}R_D5P+$3Z(MF|R`VvBH&5 zErn!lc=v2tu%&3HH)}di7dzBepJCsWT=|wSn27WPLyQ-HAg2DfR1V?-$6N)93`cVg zBrGe3^BAv-Y~z)_%R-2r^#TlxK-pMi1|S~qQmj3lu@bMN#VCf`#ru+JcJpJd2{kOg z2kT-+M4FLD$Jv;K(Q4Skx@N$A5c2l((T}Ksj`J~YDCAcpUv{a>{);cVmQTv?)FL>K z9$kUlbUh05iO3G(HXI$11--Ac!80*yA3)6V*XT}qG{B3Nj?&^ZY~yq?zydp;o@Qrg z@&Vs_rDi`SN&9`&F~2uK`~CGkX7oP=m8;YteW7@tNQv~B5Vb}~UCR8oRWbtyUuM0_ zRI$YQb8X@LE#qIVNwNj1VYY6NzAF$cJ^@Gf`>3Km8TqSJN+lFo*QF=>eW9)&pSn^DHp$IhxIzyy-1ymShESl;`E2Ng$!MeANW>YRP|&B|42FDh ze>^%mCL_T%t}D?F;7=v&n>h|#=m>zV+Qk$$Z-2|j{No<~gPraue-F^XjB~xpcNR=W zZS#bH6m#YcT>BTh=D9%c7fWMonP+%LxDxcJzdxUnt(<(uC;j!WgERLL9`v`J>5lsb z5BeK~0XccSGvQQQQpCM;v1d@*VS1tL0bR#hPj)1Jf%3+8pZ$%}`g*h9>mOpUB9-TH z%GUcnnWV_gBzptrw z`O_-xLXBj*YSXCZy{#0*!Yi=~GOl8$6AdSYMCjSnoU*>gGAswAPL+eF=>f1qez|m- z6IE#Nx?ot9T>(x7+(3@n(sW0RFf~RyBpSIP<1Y`2$jiYyu=0$4SYoSo&sF{N8ui}?_1~-d?-TU}fE&PC z)9?X3*s#0Z>>Pfb)35jN3sJZ*%d0^Hd0Bu!HGb%Qzp3Hgz!wI%o5ZCp3}^Kk=$b8n z)1}*T%c53q?8Ct&8unTaN%uYS+r0TrbW;$e#me6pXtUnAS3RHP^P}@bH_+w$UdJmx zyf<9L_QJj7PLLWY`0abL`tHToW2xKmP$caT{(Aprfgba6>i?Jl;|;8>VIAOS%LvO5 zB8j-(1Oz-dO?5hVSEx%TzNxHuuSR19AaGV05O~n7P!moYQxlJ>)oNyV!e^>+b?C1E zi4K{JP8k7YhCrQLt6=uJn2y1uEi*@qK~K&N0(EkuHW=7#`55Eh!D=#~4Dsvr2-M&E zDvDMWOe8?rgADC^)S5GNi7-1_g4atVEMOx9N|_QMLv(5ZvK=gesSDvA(FRjXCMJN!`RIh=SAg&@{g)AWNQafh12sobz}He9-58_eIpz0_^QC?)v=*EgwtA$D! zOg^cIU^wHrtOxhHE6xdsEiuttXIYnAPK+;Y|4sX4TLLGyB(uCrN78&|~vMeM1 z%Q=P1K+78NNE?CEJ3%QVS|=%>P-O!SJB55V%O#gy zW6UfO0~jn`&PiC%pm`fOlAuWl35sZES{BTpp9V1-lak!6L0f5Dw#mVRwJuNt5~3G4 zn4|H4oLEmGZVe8g4N(CRb#9N2Knr##YH*r?5qekV1?~b$l1yENCK)$c+z&A|eYP{k zEan)ufQm@EL0KGVQ3(VAU0T>L)U2mJjU85gJEv-t%3-S6N&)` zvz=bXF=gQ$Ts^!+HLbk3Eg;dy zG}j#z#9};pV`B{2AQr{qe2n`{ri=4&%MLP8FN{h!dzR0S2k=2gVj(dy$S95X8*fi{ zp5t~Db4QAo@~bT$AiJI2dy9+U7rLaFbMPrX6OgJJ(kP@VdpjB!Z!7phaTw^f3>~V+ zqk{#It|FY7`(Uq!0oX=EVh)jZimMwXWagbWa9gT?zUJg%4Y)Qj-j!WJqLhHTj|nN4 zZ&IgLwxXx^EeK=hH-#pjk+wDjB7*iCD}7m|HFU?1bPPn|-e!h>N?rQ_-u!@{ zez-()ll&Bz=~bHqu)RKh>aWzK{PZRqSD=}5HLVw2J@EKs2L>NJv@zJ5O=thmMtl#} z99e;KjJUA`kvka8Au3m^+rmp|d@-2Mr)T&B?{4tlf1VD8`0pXeW60UJE;PhP0EZFZ zps>S}X(pg0fHepnBdovMr^n%-0D{!&?r!npbg{_k^LOSs!y>J$yL*$~P`ovI1%+tSnaZT;W86d=U;7T=d!D+HK9I<8vD7KSMPdJ&qc= ziI4d42yTe!dsc#EDcW#8IBOTq=T!O8tG6Z;PQeiwh|y?8vP8*j%bJ4=Q=lgJ(6liW z;0}ALA)9D>$b)e;fKObU3iu#k0oV(?GP9uh30D6a4DDJp!Hlb8f_KzSFhjUcU9?ng z65|sUk_->jRa5kIqYo5*18=Jb!>7O@MN-UgFeD!%I+<3*iQ}JM2R`-N1P-pM#foNv z5D8`&>}1p4Ok^AjP$7CIsK8vBt};e|3G5{Et53g(HsTLUL-E0oMU6PYV!ohLjjxcW zdKh!z8aNvpfRBSSQPO%CW6|GzjG5xsLzv3`?ikb26yL`IzF4vbx^X1U86F~8&c#{6 zkPgjyLyARL8d3bUWq*BlZKE8}4zyZvv4T*Jw=C-}f7nZOuv~82b}HGV452>xyjVnu z_%p%jrfxQeQ?9Rzmke=%~;(sR@V zl2LP?=d(N0*?Ic}W2z;W9F3OLPFJ^qhhi+9h=e)qIgaZc@^Lo2caoD+F}A*o<+r2A zU1JP1sSER z&&K*@25N`VTbN@o)6AS&ILhhxZGjYMwCb{J*ErD8pEgmBW|;~aQF{?}mS7)WWaKl4 z27JT(Z~%%s)@lI6HHzL6!y-jh-bMpT>Y5muWm_+VmdRIQ~(M~A6-F^GSo?gueAxTvTJ4lKe zJhT1w%CDC-n5@w(rFiw$Rbb=HI|o!|Pk=#rI8+h2r5#7E6HAn>eEA^s8sHBpZ%S?ckfX+COB?5LO1?+R~iR*CeU}J8T6y+ zFHuVTWu0lEJcWLeD$!M|PeP;pAK+1c_qGJ8quM1wD%pyQkDXH%IJnm$lc=0!GYKh5 zFXVUEO6VeKca5ENZqmFHV`Tc=Rblt>m3_L19Sb>GtS3dDwFo~(7|;c;sK_a` zQ!^E=9a+8j#;y$yj+dfXwoiK9-1%ka$%A_}7Eb2*Y1dd0$pP`^WEo{kWB&Z!Z2kXvG= zfI@t?duM{b#q=$f;!?h>vPcruqD02C86VKtsB+*r99SAwk3U~~Zf<<=JJKZNYlh*` z&LGMOer>{^VI2+=0?84JV@G`CqR&e^UK%08Fs%{THN#fQuqn2ZeKyR8AQdWpbPJ0! z-z0cPOxTx@DWl30JFfG19KZ6ioyYczTN`GSqY+T6mW5^t!zC|6>kR`hgof&H#GK9V zpf$gUcZkJ8uLHp-N4Qm~P+rGO;D8CI^Iq=N+RO!X(!2+RDZR^lx;R<+V*qg#@9^zk zWFeEIuosrNw5Cf-dJGcNc(a_wAw@YjZNjEYnven>a2=K-9B=NW3kxgbN?JauZX){V zOX?_GDUJIK4M!ya=yJNcTU?|OsIKXy_l5v2Oa$k)RVXT{%YP)I&RxNW2*e>)& z$wAPW${6=REr{`FGj%HIyo?uxTb6QBerb@x3ulzJjQ3_Y6X$m1)Q$T(`H_(iJId~2qoS_B0mu!wDhiMo z4YTqHja()jIb8Ik;wSW-R{(=w>U!Rbg*z(^R1RAf;rrl*51oo3T#5zBmp=9d1qN*zK8tXL&68b;OIgrYA_ zCy10Mf}%R-;o-v}8Jvm)I`TD?n87cZXPdARfPUiR{rdzfhLw;fExn%((zKA+?ZG>O8#BiBw^(uFf&!U-l@twZiN z57JO_S92;hv6I5#o{}!LD!e{tQn%#wwTYiSjISQObfZI{1g2(`{k3vRId5JPUxET3L ztZ@?DuG%wP5HCeiEPW~A(Fxy@x|-EYzR~E7k|j8wPHo;fZnhB)mA>9#6n?hD62C_i zJU42ohtv>qqs^Ex5E)i^Ux0gLBTh+`sMi4=*(wYIUuQ3$Ypj--XaqsGN{SCao$hZC zt)k5zmuoPW-zf^F$!4l|zrBhzp&+bWOSS{ZeMm%2GdQAuT$oF1wlVNGeKWAN61F|P z%WRoMZXY${YsinGDK?c^>qMpJDX%~ea9Ul%93EX4#cJ-wRW^F5p$S=iLDTBE4*a}l zh(V>49HR&ae9ad2v@$IYcX+fU)6Vi9)*~cUdW4mUOt4ZsO5;}A&)aYf9K8E!JNt@k zFH_fsxQA@<)Ux9SBg?LOw8B0aVK}%-bjE=AQPCsa`FOr`xy5PfRC&l#v6VY!M%k76 zOx7CWC#BJ5yFsXX?PTT;`!O|Tb306(sTLc$v4)oV}1-z(03(%osaHgu|bO%uei(n9y=$6BIKcSvp5y zbyC4eBtzzEB)&|%2(?;u?2%Kmyc;I-OLC*dQax4yB6`XYtcg*b4*Dyksw^k)gHUz~HQZIwDv-xmxLp;Z}Chx?TmDt!wH9n3y zb@C~jes#mNyycw0iL|`A*Kc;kCC5BYvHJ$RwI>p*o;>KlgntgvbuXXzR@J=X!VmHS zf1I?yUWcwDk@h7rX*ZgUkC|4xR$lGTKC$-dnYAyQTDv;8b}6~`vTWPcXe`~=6$!Vm zd4qkt#M`T8-mXqq?mt~e_U$nJcADm?4BR!-O*;}CW6-%o({~Bn$dcZMLzWpnSer%G z3|pH;a&AoK7NE@c` z)v0m#iC;0)yCtpS485GxTH&6dP5M7o#%tm1T?9Ok`0}A^|p69c?#o}@$;%7>*B9kVA+NJ@5 ztjrQ;RONB;75P}evV>cR^hptJEgtR5Uz3#GVuMc0K1}Skn!Bkc)-|vb>nnFj3<&lU zSFS;$l6au@&8b+&iUVK#{)gkGIG48;y=ny>Y{^|_0Qi2E^tSp6woHM?${@1Tl~})= zZC`x!RHzu$Kl{3NA2lGqpZ3dYeYMppyJ%O(O-ma%ZNW6tW}D%l7>mI)%~!E+^ti}d z85YzVY~HFx=t|uaNVQmL#Mzo%CUI85dlbs%twOoK@|Fy-H$Y=6&N7NoL=_uZqO;3d zHFcLQf~A9B_Kyk&ekll(m!b-PpMAHc1j<{^NS>B^kBXF*0C7x&C?{?VW-Vr2Z%e9> z3Tm0S*b0jM7Xu<1)V)v$2s4m)GKktUF6=}d5n-7`T8{+$8P~RZ+^l9DGxP~62d+7t zaU6@aX5}eJJoR!jE?PAeMqH%iG==mXv}IjmaAr-nePTOLY}?7inrLE7Y)x$2wmGpU zwr$(S#I|qVAGf~yy?3ALs#B+Xt?u6YM_1SB-mB#1-6p==bi@i};A)VlVf4g=dxUv- zA;J?e&O=|L4CRjj5Z@J~CLZ1R$qJ{lej#cJvHxBp?Aok|*hxd-#GDOWee^MZ6{iL^ zmq|lKBVblb$iJe1TO;Y}ST!u7 zFoENF%iky-hO|#OkB*7?7pwKpd%i>_XBLPWRoOpaXxMfMh&q%`r@> z@Z(W%tOGa3I;=HP3Ue57p$AG9@1v$f<|W=i4anSslIE&h($EDTua`Y2NAi@f)6W@u zkk6XiYL!=ofL-^{VMwks2g%ia_N8$jJ?b*jX@*=eaF@nqzvu=J(jxD0LiHdOW<*gJ zQ3S{zZKD~uHZ+*vdD+u<8U;INzqMoy25IW2#xU12Hv0LUEY7yiRU2eSa5usy^xZG` zugZBg<^Po#YRQNe;giBGq8^Q{PL7HaD8g&u^J;Q@v*Gwkko+C3%)s1BIOHG)sHF%4 zJOMG6_OQd|K4Qy+kju)$ou4}!=n#MOSa}aY^7is(^msWPJ6m8xx_pc8crrd*>A*Y_ zgDAA_YkGauUH4cS%krQ5`LN`{=$-`ba8i<{<&JC5sU-#%LO>h|VMPO?i*sc?+J6^R zJHhzPLw;pnNZCPZ5R@StX;YeM+j|`$WKC80NX#?JUhBdN*Q*J#V8S?-h{Xn<^S#iP zs`b)dP;;!sw+yk|-h7CIJ=_!TdY86M%14$)af4z@diX2jE}8df8M)z_qDGIE`au72 za`jRGZ1|!U9d%z8BVgc7p*~k z&qrHO-LTnOFx%kp_`Tbp*}9eZOR0TdsR5%>eR%nKTSH{rb9kq$= zsL6BcM1wABNB2~WKG-~0WJ{b&>mj@pYK%ip%%IK{{s^dd@iYca zli+4h=GqJ9>9=65wU}?Ln<)subHVi*C-Vcw#f!#oD}cOs&mNOOT_fUXJoQ2ISh;h5 zmP0ut)sTIiSbd?}wfl~l*SSVZeV~T#KIKl)%hJJH1UN8B7Cd(*;{gw zEzQNgl$xm&!;FUYRLwsX0aA>z$DOztspPFt0zG*>*rdqBVni3S<^G+R{>@SNh4(g4 zvh-u{ZxLPH_#bqV(wBnX;@k$)Bs zA#K`Jp=Ue#T^=tP7a{*0_I378F0RMtMR;+PdAR8`Z2}g7d5>R%DD24g4PszOQ(J2T zS7USHJ`)$bUP-?X(5vVPd&>l-t~Fp148ko~DHHRRuLvibRDbPQKnu#XQm#RF_Bm!8 zG%b&O4m6ecJ1$fVR5VjBlM@Lf_LX-12`xo;p0(rpfuPGXWhy{r)T_odJjlxQ4u3DNrg$@6fPovr%_Tr zz@)@Pw07o#Zs#Pn7e)Y-8>7aNOH>iY`XbX_WyYCAvI+`|$dVhSN0);#5EWl>wC0mQ zT2_eOx26B$uuAUm2DfqmDVq8Rv+2&@TQL9IBXu#=>*X9X-(@C&-znyYd)yQT*FBIR z>*MoW+rt6vNBrm73wp0*AlV>>H{IhE!dVTLb4!396P`$BfZed7ly&SpcS9g^+s8%9 zR9X4SN#pqBJ(H%nIZUZ-tYd!)m>YgRSlx;KGDH2)%ok{DyUgyA^)}R_@R!gH_2!?O z7>||(ziEN?oM!<&#Rzc5Mo)@0fUZL%mQ;vRP!^x= zw3j>1YX+x`P*j#>NVD36l2|$y!a&yGGzr)mx z(W=J%Jzj^PrKWF@?l69t?vRkNcS!xoblg6vh~m+@6XyC?GDgPFVRMYTGEJSNszF15 z*+cvkX5@V`kxNjRR?F+={+E^lE&36NjFR0HCTy36!dzeiVNjvDI76zDw&(9G45_7} z-8OH>UG}xV3FL`;_W8%Zg(3Z|THk(}I1SY+(p{jXw82|mBowZ<4RioHun%=>XFNKw z&vbN*=GOTGVpQ%@8A4z>%Wui(8E=9hd-Dvz%~R@Yd=b{ruc!s#dn171xa0n>FsZ!B zo~;Ml7h}QZpf6*YrM789MDq+O&DtQi^@9Z2q*t&9y6}Pw&eVTxIf~1k@IY|q44)L* z}hMLFWT8Q*VHdH3yvnR^hK6#KntS={}Gi(KTWIAGrUtBJ3gH9!r0c~!W!YuN$zWf6GM zXu4gCisIcePaM+9qTPaP;<|-7mYieXsh^ss#ZJyG2!7s~&XHq8SAfqaTcPx~QV~I- zyC$E(XV>k3KzI)BF2QK0h2s8l<@ZnW4oQuk>79{-Th8p$O2Rg8$C}jn%G-Yr_ELR@ z3-vold-yPd9VBa*%{CERSCiHs%Hx@>`|bvNVo)*EQhrd_k85B&BbLV&B<3e-IquzfuNWIWL;Tw$ZEJ!YLkxqc z>@j*Gm~NK7rN9TUk)g;Uhp5`t?&I2$Q|-s$9W}|$um&E?dRl7leyT4Et_n}M#ZVBzY+BStuZl@e9U zlfs%z42lgFU<+W6zVxEr z^Ji?7w)$^jacenp{07?ZN}Sr?HL7h>QjF$s`(r3)<#mUh_#evJ#OVDIJ;RB1HYefW zQRbG@^1EFNNJN$HC3+n8%i+rMrN{)mK$As6S> zXXC~wL9+jV3T^SFS7T!%zgrGeyrd;XlZhsB_F1j*3p{O=DY(IIl}xvwykID^P@OHqUwvSIOm*^xEc`J< zDmr1;oGXTeXccpF<|#Cl`d$?B@ywkat<$Gdw`SqMelF1MvoHrbiP)yN3~|Fj6`9+A zB$BzwjEJxj=^|4`+KkK;)|3!rislFvoP8GTqJZRj8m`jV4hLZcA89rX=*qthss3pf z`_~+LYtU-GkUNdGay$zw*3`2j5a5?To+q&iNgohwOB5mk*L0>w`A{KL8fdj89bVOF zoH%+DmFYo!jcMEU;-ru+D(d$;F;4*%30L6135lb?Ah1ZZ2osqBj<_QYF&B|bDh-*qHBI;>Zy5nx@@?@pc9&0ah9o-Bq~ zV;k5M&{1C+X=BIW2+fRtmg`6H5yA_k{cQ_q5R4QnQn#uODW%*ep@#@jht;ijdd;E% zo|L%Rg3fl{g3fY>i5Vg@KQZyUH8u3;TEYoHKP_A2bn^FBf8aGsD*}Ux;HpF?~*Z0d{=c3=Xw+SBetl0%kBAG5Q;J}>@L;Yqb&SASW zk`Sm8&LwaZt%$q1i-1qGz9_?jaL9LBu}W$aW|D+?HB&F_U=+URT)~Ej!RN1ZF_F&ca+oRQC@?0j#{}ikk%A z96I|!GqBao1KAG5O2nIL)%6N=$>S?6QS)=6W2Fk~qK?4Gb-QRvVuADSdmILhqh5Fg zOeHyS+12qMV>+9FS97*qq8*yq^$AZX$9Fq47Q2$fbeuU3W?WC>fcwSiFig!ks0o9D z&El2aU8`$*1!tu4wf0kR?8jYxWFFH6zh^m4k<&yrO@@*Hq`GyY2=PS({oL(f6AaHcO}Ny&sP)8w6*29-bD?Ae4U&WlnlLQZ-Qy$xO?o(ox0cTH){0u-4`3gfP~mu zrQlvq^w3Jy;3A3Rao=awr%?oSr7z2!pe@%pqmY=0E$MjR;AH4Q~VSw5`!hbv7&G-U;hz~&ny2#nZk+WjmFsp>gRBk z(f!;8m-!G3YH~^mJnyDzpVq_1Z^ndaUS52Gpl`v$@K|$@>!DVw6{($6it-yINtHi zv=1ie)1jh|Zg(YW!m*9bvZ8C0;$EsEzp}zRhG%)x3M%SE{m`}WtW>4F*3?i}eo+Xd9R4Surufdn z7&|=?uO%gSH6@n1>R%n|^9xDm$O0m>Hs=?Q$LiY4Gg>ph#CYMy=8c0-zg)PG*Zm=| zcBr0G0RyFHV>yRbA@eO83kKS6dpH+`t=xos)Z4;wxa5{$e7&>qupwgXNIOU2MTswB zNuI?P{G^3;syA-hja5sHxeE9okwsJpHzGJZ|nM{ZzFXUA?}+G?ypr@4TCn|L{I%cRUq>p2YLa`Sp~6BcRH zSJ`_^q~OzbNL)b7J0kJMu1&8pD|Ro1syAKzhiF+kN0T z8`phrZRF{l>-0JEp(INMO7}({S4o7}<3otjJh)5C-LZPHus$-4(b$NyZIHX77l8AQ zVl5^Z6myvGvQl66Z9{S-z1{aU*|ifI(Ui1&dkS0Qr1f4=pt~r^i*HM2QEbqn!*I+W zM>4OVJ46h)nLWi~I>50_fBx+$ONw7+8yer)MV1kAx0vcaev-#qmz8aIYZv})MDs}SP`@w`HYb#COdU^QOQ|M(ChH3-}^V@9xJ~5wUsBA;gj^`LU z_pBG%J@5`LW!Y#}wfrF9hQHz4@7&gyDPWSwh$Hn^rI#Q&qr(xt# zrWN7TT5_qaIs{Y69Kijqhtw&3julR14<(IazX8lt zY-$(Ct&RidB#$v@Q6v_cEa8Qw*c2;4nEdih!DfudJCx2!^U~%e?pxHvD$T4WZGZV| zWY_thyc9tHdCc4R$m==O0RF8P7%*!A2WJxerb1i5HTLMGC*U-^O{kh-tMUM)Z~~T= zau5&r8nhfy2Q=~R*XXB3F%Pz>JzjoRi=-+cS0&F5ts~J)Gn-&H&Mz)2C(F@#W4vro z!%Q?mB8+Aq8XMf%BsdN?QgsvsU3V?nq$MQxu40L5e=KL{tS_9upqZFxZ69f-`19Oq z2NY<)Y}~*oHruEN`#^S+a+Mq`gj`at8;J~CQW9vGtKg~J3_%h?nr*z;-@WXt)Vsdj z6gtT#T~#^C+jE_96rCGX#{3-)O3FCPzKJK;J%Ir+*tgO8>=lY@H!m)?y+*H;5*!18& zz~rJ6+;FH)UBFQydY8YiYo|NNX*kF0)^y9DxsbdG*IadYJz~{0)1tR;PWOy?Aj>n_ z&(Y<{g-tQJzQwZ&*OHasOl!vG)l}<4=Qp37l1M1@Qy68| z1nAJSYk6(pHTLxEp)!bIyqa_!%etVgSQDuik^dI(aaLgb$N?PMf2Ub_u|0PKMmdlP z^Ugwoq-j|ak&SUb-un0E!bg}Q=880)9Xl(D?LpTzVMC~uL})!Bg*Z<67Guj?dizqF z$M-Tk5D^T!( z$5;x&Q*{f*km&ueVybLQPqer)YWbgLA^8?5{b)5x>&rJ(5x2EU^ygm_IfkPu*()}C4UXnlu*G`10&Y*Unx|? z=4$zaV>BTSbH7N1HP#mJs}~$s%v0Xv!K|nk38}9G-YbCVH@zRngJ0`qlGV&D=(h81ov-MvW-?Et5A>G)cmQZCTx~Lf}HePMQ zVf*TibDpt@^_i)p_zS|C39}TOLD5&Cb#glE>21L~{_bJ;D^kVe$}ie?Ov!F7w5%#k&)>T}TldfCMn2o<-ToT*_hu7mr~2Ubf;LCq85@)LciV zFSK)iMhVJTVkHN0b$tt&28Lv=GG*#t;ErSIe;b_m8;PDR@h0m6(8YykcRDLkaggd3 zag7YAYu$bMuJK(`W#CeY`J2_eL`+gBE&49xEg}Oj6B;tG4%VGO{E(bXVD;|VwLa6k zd<6MRmX-yO5>DOQJszlVR@bxK^G*h)J3RGB46TbhdGc~j8E|haFYM!q4M91}a${*P z_}21u)6|8CtaT`KIP^*kbAm;lG+)RU80TP5{ji#iNHOK>@=De{dGnkG zu6p-#e>WGKYV?$I8=tN|lL~D6Mnc}i{bQf?K=E3GhrzuY_q7sPlRl0u8UGF=Il*<~fxmQ>Ou_77x9vR{zf+JXU~#}I2r zIIBmB{yJ?}a{OQxC~uRh7J&S-PS4xCVy0wz(2(*wS|aen^ibG<4=9lzhRRXK?r|%qKLvo6bWD*_LEj^2@iUqA&zlD+o_u|qM7zxj=B3n!Au}`S;-M|d$>B;YS zzou3-6ZZH?7uttqAjjIO? zR1RBWpAc%%=DMhu7Ck+nRU;2hiMGgJFgPcI857nqq?L8B^^~6EB@@%Iz9&@rS4~M} zI!4A`QMU>2TfndiRgc#qv&0?)=%7?VDQQgeB*d`h`@-CXhj%MMrIJH);2I;mP^QlV zisO*N{eTsXSBcjIbpc!<*}2Q~*U2NEr@E zTFqIECx`>5)Y5{_LrWziBf=DmQ|143fjWDV11O`8XFXK=?vurk|~1EUm6{F%S~7 zmrO&AIPn#kNi<@PC@Th2lX-$D57+bk9@@K$x|5IXjz6nM z2wEvg>|PhjIv(`d#|gj}krC7k-NvcSQjIJniUrUyUHXaYN|?-{;D>^krt_@Rb*E<^w`RjLJP+hBf+`13qrFGx#CZ_>Z=C!b&Ic{ z_QEiMbHpzvfU(ck`yyfyg5Whz39e&3K`g4w(%ngyZh1!@^`?PlP3L)v+%+CT?AAFw zb{93vw8^?I4?#I12ex0ic2n7}Jwf#D>B|M1Md4dbRj%)vC$EyG9$!3+(GC^}UUq9H z>w#@nz}b;{>~Ndj2M#A14FgEeukf&ZGvkK`B`j8(0!jm!KbSo_V7G4oL?-!lkjp3jTOW?6VIxz zAf#H!T=TWM)RUnNC{^(28bp;g=M&U}Mr67tH&&?hA7sct)^o#s2`o)tV-rr=VYGKi zsE&@5=T~))43&`!@jPappvdRWRMFRGGmzDxFGSl|P~#U+?-VQ~bHO8ii>g@sI_{WA zlOy;^Av|;?4m+pSXa;Z{0_>>!{wT)!eumoV0bO&jM=Z_EIJlj623p30lkBz6q=ds^ z1Q!@wbi;$^qgTyvzeb~v#$|k_T?7~YN?DFRnJXDi_1zNuP5hPrBX2-hE&}&zKip+s z2g}znjuH0VVh)bh91>j>+b6IsSdVe8UDIzWV;uK7n*7~rR^GX3OUoBwmo(7bd=F{W z9PzKq>ScDXkITjS%GOrpD!t}imed)YVqtW<@4vsl{eAk$lEL!cN zJ8W4&s3*(+8f<(m29x0uc5iEUZBCi=3$M;t`TbFpT{j9$)FQ#%6Q7!&@Q6leQ)kSQ zMJ+m!)<%Bx1ErmnmU82K=uQ1f(z!TUhUv1(_lN1{d^aieMGgxti#Xc5MkR9CU;+~Y z7KC97T4t!&p(BmdNVxkBgdX*Q0!v&kqF7OUix{RWW0hX1jYDXfE@IaR$b?UB>a0*p zf{FwCWnEskwO~2?GFQd+83e@jC1T_CatlBkvT|>!w*2#?B5t5uJBIGla(HS2-iuXy zsm?LJ_*;T!hh+5-cd4qK$t(%3L?`w$^4zSN{pM0LVsY=_^?S{lgeYP{T3=``+szi# zQTKTM;XsOR>wY;GiIOv(4tiJE^n^%BTMD9tLbI1)eE>gG0CrJd$5VguCpcVf$&8QO z%ISlW*bBkiO#!2>j;A`&IyVAbVmr;Ei=cS03|07-he|@oQdYPKMPi%Z#~#SW_>-3k9M6FHx;maJ(;%W1rheKIuTVDQhK@-6 zTpk?W1m4UcPDr9^h-vsyky#8-{()^05HoP|yZxcZf{Q+Tsb~gB8$G}S(Zs_v=A`$x znlJly1qHAE*Pndl=d10Kc2C#o7aw^kC}=}y*$8|n5C8-m1VCO23>+N*3k?nUw?Tb< za5Od6ceFQlkfoE6rDLFn{Avyb9YA?N2K!a{4@6!H1XKX_KLpOSSIut7002<{0PqjN zf18z=gM*ok^}j){=-}e^UoHQB70mxaRDMxeGBa5-iinB{DNFxkG%_=E{9@3zcl$R9 zUNLyY_b;&YSHb!(5@VEqkyz>5DjGZ3IN2K-|C@!lk41(X6abJC1_1t<*=dFhh0NB|6GwA;Z`oD1i{->v}w-JJW582GYk-^eQ l|KI)o-&p*&zo{ue{x^K`QV@{;&_jJag&+Wc=~op1@INOuq_6-0 literal 0 HcmV?d00001 From 74b40fe3cbdd14fa36c7c5e5378dc37882f1c65b Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 3 May 2025 16:10:22 +0200 Subject: [PATCH 153/166] fix bug --- CTLD.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 2ba32ca..d7838d2 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -808,8 +808,6 @@ ctld.logisticUnits = { ctld.vehicleTransportEnabled = { "76MD", -- the il-76 mod doesnt use a normal - sign so il-76md wont match... !!!! GRR "Hercules", - "UH-1H", - "Mi-8MT", --"CH-47Fbl1", } @@ -5895,6 +5893,10 @@ function ctld.addTransportF10MenuOptions(_unitName) local itemNbMain = 0 local _cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Vehicle / FOB Crates / Drone"), _rootPath) + + if ctld.vehicleCommandsPath[_unitName] == nil then + ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_cratesMenuPath) + end for _i, _category in ipairs(crateCategories) do local _subMenuName = _category local _crates = ctld.spawnableCrates[_subMenuName] From 8bfaba399f160c3e29bf18b70c0cc3cc3d8db84a Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sun, 4 May 2025 23:46:00 +0200 Subject: [PATCH 154/166] bugs fix --- CTLD-i18n.lua | 4 +-- CTLD.lua | 68 +++++++++++++++++++++++++++------------------------ 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/CTLD-i18n.lua b/CTLD-i18n.lua index 5c6a4af..050e6ce 100644 --- a/CTLD-i18n.lua +++ b/CTLD-i18n.lua @@ -264,7 +264,7 @@ ctld.i18n["fr"]["Unload / Extract Troops"] = "Débarqt / Embarqt Troupes" ctld.i18n["fr"]["Next page"] = "page suiv." ctld.i18n["fr"]["Load "] = "Charger " ctld.i18n["fr"]["Vehicle / FOB Transport"] = "Transport Vehicule / FOB" -ctld.i18n["fr"]["Vehicle / FOB Crates / Drone"] = "Caisses Vehicule / FOB / Drone" +ctld.i18n["fr"]["Crates: Vehicle / FOB / Drone"] = "Caisses Vehicule / FOB / Drone" ctld.i18n["fr"]["Unload Vehicles"] = "Décharger Vehicles" ctld.i18n["fr"]["Load / Extract Vehicles"] = "Chargt / Déchargt Vehicules" ctld.i18n["fr"]["Load / Unload FOB Crate"] = "Chargt / Déchargt Caisse FOB" @@ -563,7 +563,7 @@ ctld.i18n["es"]["Unload / Extract Troops"] = "Descargar/Extraer tropas" ctld.i18n["es"]["Next page"] = "Página siguiente" ctld.i18n["es"]["Load "] = "Cargar " ctld.i18n["es"]["Vehicle / FOB Transport"] = "Transporte Vehículo / FOB" -ctld.i18n["es"]["Vehicle / FOB Crates / Drone"] = "Cajas Vehículo / FOB / Dron" +ctld.i18n["es"]["Crates: Vehicle / FOB / Drone"] = "Cajas Vehículo / FOB / Dron" ctld.i18n["es"]["Unload Vehicles"] = "Descargar vehículos" ctld.i18n["es"]["Load / Extract Vehicles"] = "Cargar/Extraer vehículos" ctld.i18n["es"]["Load / Unload FOB Crate"] = "Cargar/Descargar caja FOB" diff --git a/CTLD.lua b/CTLD.lua index 7873397..3eb5f18 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -325,7 +325,7 @@ ctld.i18n["en"]["Unload / Extract Troops"] = "" ctld.i18n["en"]["Next page"] = "" ctld.i18n["en"]["Load "] = "" ctld.i18n["en"]["Vehicle / FOB Transport"] = "" -ctld.i18n["en"]["Vehicle / FOB Crates / Drone"] = "" +ctld.i18n["en"]["Crates: Vehicle / FOB / Drone"] = "" ctld.i18n["en"]["Unload Vehicles"] = "" ctld.i18n["en"]["Load / Extract Vehicles"] = "" ctld.i18n["en"]["Load / Unload FOB Crate"] = "" @@ -3867,12 +3867,12 @@ function ctld.getClosestCrate(_heli, _crates, _type) local _shortestDistance = -1 local _distance = 0 local _minimumDistance = 5 -- prevents dynamic cargo crates from unpacking while in cargo hold - + local _maxDistance = 10 -- prevents onboard dynamic cargo crates from unpacking requested by other helo for _, _crate in pairs(_crates) do if (_crate.details.unit == _type or _type == nil) then _distance = _crate.dist - if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) and _distance > _minimumDistance then + if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) and _distance > _minimumDistance and _distance < _maxDistance then _shortestDistance = _distance _closetCrate = _crate end @@ -5889,9 +5889,9 @@ function ctld.addTransportF10MenuOptions(_unitName) _vehicleCommandsPath, ctld.unloadTroops, { _unitName, false }) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), _vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true }) - if ctld.vehicleCommandsPath[_unitName] == nil then - ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) - end + -- if ctld.vehicleCommandsPath[_unitName] == nil then + -- ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) + -- end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then missionCommands.addCommandForGroup(_groupId, @@ -5916,11 +5916,11 @@ function ctld.addTransportF10MenuOptions(_unitName) -- add menu for spawning crates local itemNbMain = 0 local _cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId, - ctld.i18n_translate("Vehicle / FOB Crates / Drone"), _rootPath) + ctld.i18n_translate("Crates: Vehicle / FOB / Drone"), _rootPath) - if ctld.vehicleCommandsPath[_unitName] == nil then - ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_cratesMenuPath) - end + -- if ctld.vehicleCommandsPath[_unitName] == nil then + -- ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_cratesMenuPath) + -- end for _i, _category in ipairs(crateCategories) do local _subMenuName = _category local _crates = ctld.spawnableCrates[_subMenuName] @@ -5971,17 +5971,20 @@ function ctld.addTransportF10MenuOptions(_unitName) ctld.spawnCrate, { _unitName, _menu.crate.weight }) end end - if ctld.unitDynamicCargoCapable(_unit) then - if ctld.vehicleCommandsPath[_unitName] == nil then - ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_cratesMenuPath) - end - end + -- if ctld.unitDynamicCargoCapable(_unit) then + -- if ctld.vehicleCommandsPath[_unitName] == nil then + -- ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_cratesMenuPath) + -- 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)"), @@ -6083,8 +6086,7 @@ end function ctld.updateRepackMenu(_playerUnitName) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then - local _unitTypename = playerUnit:getTypeName() - local _groupId = ctld.getGroupId(playerUnit) + local _groupId = ctld.getGroupId(playerUnit) if ctld.enableRepackingVehicles then local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then @@ -6118,24 +6120,26 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr if t == nil then t = timer.getTime() end if ctld.enableRepackingVehicles then for _, _unitName in pairs(ctld.transportPilotNames) do - local status, error = pcall( - function() - local _unit = ctld.getTransportUnit(_unitName) - if _unit then - -- if transport unit landed => update repack menus - if (ctld.inAir(_unit) == false or (ctld.heightDiff(_unit) <= 0.1 + 3.0 and mist.vec.mag(_unit:getVelocity()) < 0.1)) then - local _unitTypename = _unit:getTypeName() - local _groupId = ctld.getGroupId(_unit) - if _groupId then - if ctld.addedTo[tostring(_groupId)] ~= nil then -- if groupMenu on loaded => add RepackMenus - ctld.updateRepackMenu(_unitName) + if ctld.vehicleCommandsPath[_unitName] ~= nil then + local status, error = pcall( + function() + local _unit = ctld.getTransportUnit(_unitName) + if _unit then + -- if transport unit landed => update repack menus + if (ctld.inAir(_unit) == false or (ctld.heightDiff(_unit) <= 0.1 + 3.0 and mist.vec.mag(_unit:getVelocity()) < 0.1)) then + local _unitTypename = _unit:getTypeName() + local _groupId = ctld.getGroupId(_unit) + if _groupId then + if ctld.addedTo[tostring(_groupId)] ~= nil then -- if groupMenu on loaded => add RepackMenus + ctld.updateRepackMenu(_unitName) + end end end end - end - end) - if (not status) then - env.error(string.format("Error in ctld.autoUpdateRepackMenu : %s", error), false) + end) + if (not status) then + env.error(string.format("Error in ctld.autoUpdateRepackMenu : %s", error), false) + end end end end From 7bfed7ef55be6249e4dc93773d7a6e95ade41524 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Thu, 8 May 2025 19:30:19 +0200 Subject: [PATCH 155/166] bommit fix --- CTLD.lua | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 3eb5f18..c726594 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5911,7 +5911,7 @@ function ctld.addTransportF10MenuOptions(_unitName) table.insert(crateCategories, category) end table.sort(crateCategories) - ctld.logTrace("crateCategories = [%s]", ctld.p(crateCategories)) + --ctld.logTrace("crateCategories = [%s]", ctld.p(crateCategories)) -- add menu for spawning crates local itemNbMain = 0 @@ -5936,10 +5936,10 @@ function ctld.addTransportF10MenuOptions(_unitName) local menuEntries = {} local _subMenuPath = missionCommands.addSubMenuForGroup(_groupId, _subMenuName, _cratesMenuPath) for _, _crate in pairs(_crates) do - ctld.logTrace("_crate = [%s]", ctld.p(_crate)) + --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)) + --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 @@ -5952,14 +5952,14 @@ function ctld.addTransportF10MenuOptions(_unitName) _crateRadioMsg = "* " .. _crateRadioMsg end local _menuEntry = { text = _crateRadioMsg, crate = _crate } - ctld.logTrace("_menuEntry = [%s]", ctld.p(_menuEntry)) + --ctld.logTrace("_menuEntry = [%s]", ctld.p(_menuEntry)) table.insert(menuEntries, _menuEntry) end end end end for _i, _menu in ipairs(menuEntries) do - ctld.logTrace("_menu = [%s]", ctld.p(_menu)) + --ctld.logTrace("_menu = [%s]", ctld.p(_menu)) -- add the submenu item itemNbSubmenu = itemNbSubmenu + 1 if itemNbSubmenu == 10 and _i < #menuEntries then -- page limit reached @@ -6143,7 +6143,7 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr end end end - return t + 6 -- reschedule every 6 seconds + return t + 1 -- reschedule every 1 seconds end --****************************************************************************************************** @@ -8339,7 +8339,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 1) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end if ctld.enableAutoOrbitingFlyingJtacOnTarget then @@ -8496,7 +8496,7 @@ function ctld.eventHandler:onEvent(event) timer.scheduleFunction(function() ctld.logTrace("calling the 'processHumanPlayer' function in a timer") processHumanPlayer() - end, nil, timer.getTime() + 1.5) + end, nil, timer.getTime() + 2) --1.5 else ctld.logTrace("calling the 'processHumanPlayer' function immediately") processHumanPlayer() From e13e630830fd34462b4451342543dc1361fb75e2 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Fri, 9 May 2025 10:07:00 +0200 Subject: [PATCH 156/166] commit --- CTLD.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 3eb5f18..ef78670 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6087,6 +6087,9 @@ function ctld.updateRepackMenu(_playerUnitName) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then local _groupId = ctld.getGroupId(playerUnit) + if _groupId == nil then + return + end if ctld.enableRepackingVehicles then local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then @@ -6143,7 +6146,7 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr end end end - return t + 6 -- reschedule every 6 seconds + return t + 1 -- reschedule every 6 seconds end --****************************************************************************************************** @@ -8339,7 +8342,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 3) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 1) timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end if ctld.enableAutoOrbitingFlyingJtacOnTarget then From e3424c6af5c893a7c0fdf27f97327fa3401c3003 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Fri, 9 May 2025 11:01:38 +0200 Subject: [PATCH 157/166] commit --- CTLD.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 109f6a6..14017d6 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -6094,11 +6094,13 @@ function ctld.updateRepackMenu(_playerUnitName) local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then --ctld.logTrace("FG_ ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) - local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) - RepackCommandsPath[#RepackCommandsPath + 1] = ctld.i18n_translate("Repack Vehicles") + local RepackPreviousMenu = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) + local RepackCommandsPath = RepackPreviousMenu + local repackSubMenuText = ctld.i18n_translate("Repack Vehicles") + RepackCommandsPath[#RepackCommandsPath + 1] = repackSubMenuText -- add the submenu name to get the complet repack path --ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Repack Vehicles"), ctld.vehicleCommandsPath[_playerUnitName]) + local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, repackSubMenuText, RepackPreviousMenu) local menuEntries = {} for i, _vehicle in ipairs(repackableVehicles) do if ctld.isUnitInMenuEntriesTable(menuEntries, _vehicle.desc) == false then From 359bf1418dc98213d37d9bdad3cb648e94c93971 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Fri, 9 May 2025 11:54:54 +0200 Subject: [PATCH 158/166] commit --- CTLD.lua | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 14017d6..91565b4 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -5889,9 +5889,6 @@ function ctld.addTransportF10MenuOptions(_unitName) _vehicleCommandsPath, ctld.unloadTroops, { _unitName, false }) missionCommands.addCommandForGroup(_groupId, ctld.i18n_translate("Load / Extract Vehicles"), _vehicleCommandsPath, ctld.loadTroopsFromZone, { _unitName, false, "", true }) - -- if ctld.vehicleCommandsPath[_unitName] == nil then - -- ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_vehicleCommandsPath) - -- end if ctld.enabledFOBBuilding and ctld.staticBugWorkaround == false then missionCommands.addCommandForGroup(_groupId, @@ -5918,9 +5915,6 @@ function ctld.addTransportF10MenuOptions(_unitName) local _cratesMenuPath = missionCommands.addSubMenuForGroup(_groupId, ctld.i18n_translate("Crates: Vehicle / FOB / Drone"), _rootPath) - -- if ctld.vehicleCommandsPath[_unitName] == nil then - -- ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_cratesMenuPath) - -- end for _i, _category in ipairs(crateCategories) do local _subMenuName = _category local _crates = ctld.spawnableCrates[_subMenuName] @@ -5971,11 +5965,6 @@ function ctld.addTransportF10MenuOptions(_unitName) ctld.spawnCrate, { _unitName, _menu.crate.weight }) end end - -- if ctld.unitDynamicCargoCapable(_unit) then - -- if ctld.vehicleCommandsPath[_unitName] == nil then - -- ctld.vehicleCommandsPath[_unitName] = mist.utils.deepCopy(_cratesMenuPath) - -- end - -- end end end @@ -6050,7 +6039,13 @@ function ctld.addTransportF10MenuOptions(_unitName) end --****************************************************************************************************** -function ctld.buildPaginatedMenu(_menuEntries) +function ctld.buildPaginatedMenu(_menuEntries) --[[ params table : + { text = command name menu + groupId = playerUnit groupId, + subMenuPath = complet MenuPath clicked, + menuFunction = function name to run on clicked menu, + menuArgsTable = table with arguments for the function to run, + }]] local nextSubMenuPath = {} local itemNbSubmenu = 0 for i, menu in ipairs(_menuEntries) do @@ -6094,20 +6089,20 @@ function ctld.updateRepackMenu(_playerUnitName) local repackableVehicles = ctld.getUnitsInRepackRadius(_playerUnitName, ctld.maximumDistanceRepackableUnitsSearch) if repackableVehicles then --ctld.logTrace("FG_ ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) - local RepackPreviousMenu = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) + local RepackPreviousMenu = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) local RepackCommandsPath = RepackPreviousMenu local repackSubMenuText = ctld.i18n_translate("Repack Vehicles") RepackCommandsPath[#RepackCommandsPath + 1] = repackSubMenuText -- add the submenu name to get the complet repack path --ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu - local RepackmenuPath = missionCommands.addSubMenuForGroup(_groupId, repackSubMenuText, RepackPreviousMenu) + local RepackMenuPath = missionCommands.addSubMenuForGroup(_groupId, repackSubMenuText, RepackPreviousMenu) local menuEntries = {} for i, _vehicle in ipairs(repackableVehicles) do if ctld.isUnitInMenuEntriesTable(menuEntries, _vehicle.desc) == false then _vehicle.playerUnitName = _playerUnitName table.insert(menuEntries, { text = ctld.i18n_translate("repack ") .. _vehicle.unit, groupId = _groupId, - subMenuPath = RepackmenuPath, + subMenuPath = RepackMenuPath, menuFunction = ctld.repackVehicleRequest, menuArgsTable = mist.utils.deepCopy(_vehicle) }) From 1826d28a869b1e599fbd90b473dfff1773cd7bfc Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Fri, 9 May 2025 14:33:54 +0200 Subject: [PATCH 159/166] commit --- CTLD.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 91565b4..432a3e2 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1957,6 +1957,7 @@ function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) end local unitsNamesList = ctld.getNearbyUnits(unit:getPoint(), _radius, unit:getCoalition()) + local repackableUnits = {} for i = 1, #unitsNamesList do local unitObject = Unit.getByName(unitsNamesList[i]) @@ -1984,13 +1985,14 @@ function ctld.getNearbyUnits(_point, _radius, _coalition) --local _dist = ctld.getDistance(u:getPoint(), _point) local _dist = mist.utils.get2DDist(u:getPoint(), _point) if _dist <= _radius then - unitsByDistance[cpt] = {id =cpt, dist = _dist, unit = _unitName} + unitsByDistance[cpt] = {id =cpt, dist = _dist, unit = _unitName, typeName = u:getTypeName()} cpt = cpt + 1 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 for i, v in ipairs(unitsByDistance) do table.insert(_units, v.unit) -- insert nearby unitName end From b5cd8929f3f590dff2e23803af1adf20b38f573f Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Fri, 9 May 2025 15:25:34 +0200 Subject: [PATCH 160/166] commit --- CTLD.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 432a3e2..73d6604 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2081,7 +2081,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' repackableUnit:destroy() -- destroy repacked unit end end - --ctld.updateRepackMenu(playerUnitName) -- update the repack menu to process destroyed units + ctld.updateRepackMenu(playerUnitName) -- update the repack menu ctld.repackRequestsStack[ii] = nil -- remove the processed request from the stacking table end if ctld.enableRepackingVehicles == true then @@ -5997,6 +5997,10 @@ function ctld.addTransportF10MenuOptions(_unitName) 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 @@ -6080,7 +6084,10 @@ function ctld.isUnitInMenuEntriesTable(_MenuEntriesTable, _typeUnitDesc) return false end --****************************************************************************************************** +ctld.updateCount = 0 function ctld.updateRepackMenu(_playerUnitName) + ctld.updateCount = ctld.updateCount + 1 + ctld.logTrace("FG_ ctld.updateRepackMenu(%s) - %s", _playerUnitName, ctld.updateCount) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then local _groupId = ctld.getGroupId(playerUnit) @@ -8341,7 +8348,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 1) + --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 1) -- initialize repack menu timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end if ctld.enableAutoOrbitingFlyingJtacOnTarget then From 782940265d17c4bcf13f3403f13ef377b5d15fe7 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Sat, 10 May 2025 01:33:15 +0200 Subject: [PATCH 161/166] commit --- CTLD.lua | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 73d6604..ea3ae28 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -2078,12 +2078,16 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, "dynamic") end end - repackableUnit:destroy() -- destroy repacked unit + repackableUnit:destroy() -- destroy repacked unit end + --ctld.updateRepackMenu(playerUnitName) -- update the repack menu + timer.scheduleFunction(ctld.updateRepackMenu, playerUnitName, timer.getTime() + 1) end - ctld.updateRepackMenu(playerUnitName) -- update the repack menu - 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 + + + if ctld.enableRepackingVehicles == true then return t + 3 -- reschedule the function in 3 seconds else @@ -3869,7 +3873,7 @@ function ctld.getClosestCrate(_heli, _crates, _type) local _shortestDistance = -1 local _distance = 0 local _minimumDistance = 5 -- prevents dynamic cargo crates from unpacking while in cargo hold - local _maxDistance = 10 -- prevents onboard dynamic cargo crates from unpacking requested by other helo + local _maxDistance = 15 -- prevents onboard dynamic cargo crates from unpacking requested by other helo for _, _crate in pairs(_crates) do if (_crate.details.unit == _type or _type == nil) then _distance = _crate.dist @@ -3929,9 +3933,10 @@ function ctld.getCrateObject(_name) end function ctld.unpackCrates(_arguments) + ctld.logTrace("FG_ ctld.unpackCrates._arguments = %s", ctld.p(_arguments)) local _status, _err = pcall(function(_args) local _heli = ctld.getTransportUnit(_args[1]) - + ctld.logTrace("FG_ ctld.unpackCrates._args = %s", ctld.p(_args)) if _heli ~= nil and ctld.inAir(_heli) == false then local _crates = ctld.getCratesAndDistance(_heli) local _crate = ctld.getClosestCrate(_heli, _crates) @@ -4008,9 +4013,9 @@ function ctld.unpackCrates(_arguments) trigger.action.outTextForCoalition(_heli:getCoalition(), - ctld.i18n_translate("%1 successfully deployed %2 to the field", ctld.getPlayerNameOrType(_heli), + ctld.i18n_translate("%1 successfully deployed %2 to the field", ctld.getPlayerNameOrType(_heli), _crate.details.desc), 10) - + timer.scheduleFunction(ctld.autoUpdateRepackMenu, { reschedule = false }, timer.getTime() + 1) if ctld.isJTACUnitType(_crate.details.unit) and ctld.JTAC_dropEnabled then local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) --put to the end @@ -6067,8 +6072,13 @@ function ctld.buildPaginatedMenu(_menuEntries) --[[ params table : end menu.menuArgsTable.subMenuPath = mist.utils.deepCopy(menu.subMenuPath) -- copy the table to avoid overwriting the same table in the next loop menu.menuArgsTable.subMenuLineIndex = itemNbSubmenu - missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, mist.utils.deepCopy(menu.menuArgsTable)) - --ctld.logTrace("FG_ boucle[%s].menu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) + ctld.logTrace("FG_ boucle[%s].groupId = %s", i, menu.groupId) + ctld.logTrace("FG_ boucle[%s].menu.text = %s", i, menu.text) + ctld.logTrace("FG_ boucle[%s].menu.subMenuPath = %s", i, menu.subMenuPath) + ctld.logTrace("FG_ boucle[%s].menu.menuFunction = %s", i, menu.menuFunction) + local r = missionCommands.addCommandForGroup(menu.groupId, menu.text, menu.subMenuPath, menu.menuFunction, mist.utils.deepCopy(menu.menuArgsTable)) + ctld.logTrace("FG_ boucle[%s].r = %s", i, r) + ctld.logTrace("FG_ boucle[%s].menu.menuArgsTable = %s", i, ctld.p(menu.menuArgsTable)) end end @@ -6099,11 +6109,16 @@ function ctld.updateRepackMenu(_playerUnitName) if repackableVehicles then --ctld.logTrace("FG_ ctld.vehicleCommandsPath[_playerUnitName] = %s", ctld.p(ctld.vehicleCommandsPath[_playerUnitName])) local RepackPreviousMenu = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) - local RepackCommandsPath = RepackPreviousMenu + local RepackCommandsPath = mist.utils.deepCopy(ctld.vehicleCommandsPath[_playerUnitName]) local repackSubMenuText = ctld.i18n_translate("Repack Vehicles") RepackCommandsPath[#RepackCommandsPath + 1] = repackSubMenuText -- add the submenu name to get the complet repack path --ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) missionCommands.removeItemForGroup(_groupId, RepackCommandsPath) -- remove existing "Repack Vehicles" menu + ctld.logTrace("FG_ RepackCommandsPath = %s", ctld.p(RepackCommandsPath)) + + ctld.logTrace("FG_ repackableVehicles = %s", ctld.p(repackableVehicles)) + ctld.logTrace("FG_ repackSubMenuText = %s", ctld.p(repackSubMenuText)) + ctld.logTrace("FG_ RepackPreviousMenu = %s", ctld.p(RepackPreviousMenu)) local RepackMenuPath = missionCommands.addSubMenuForGroup(_groupId, repackSubMenuText, RepackPreviousMenu) local menuEntries = {} for i, _vehicle in ipairs(repackableVehicles) do @@ -6127,6 +6142,8 @@ end --****************************************************************************************************** function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each transport unit if t == nil then t = timer.getTime() end + if p.reschedule == nil then p.reschedule = false end + ctld.logTrace("FG_ ctld.autoUpdateRepackMenu.p.reschedule = %s", p.reschedule) if ctld.enableRepackingVehicles then for _, _unitName in pairs(ctld.transportPilotNames) do if ctld.vehicleCommandsPath[_unitName] ~= nil then @@ -6152,7 +6169,9 @@ function ctld.autoUpdateRepackMenu(p, t) -- auto update repack menus for each tr end end end - return t + 1 -- reschedule every 1 seconds + if p.reschedule == true or p.reschedule == nil then + return t + 5 -- reschedule every 5 seconds + end end --****************************************************************************************************** From a1da0ea1c4ed551ccff6ffc5783009373a76d9f6 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 10 May 2025 12:04:18 +0200 Subject: [PATCH 162/166] Last fix commit !? --- CTLD.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index ea3ae28..6646f3b 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -4015,7 +4015,7 @@ function ctld.unpackCrates(_arguments) trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.i18n_translate("%1 successfully deployed %2 to the field", ctld.getPlayerNameOrType(_heli), _crate.details.desc), 10) - timer.scheduleFunction(ctld.autoUpdateRepackMenu, { reschedule = false }, timer.getTime() + 1) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, { reschedule = false }, timer.getTime() + 1) -- for add unpacked unit in repack menu if ctld.isJTACUnitType(_crate.details.unit) and ctld.JTAC_dropEnabled then local _code = table.remove(ctld.jtacGeneratedLaserCodes, 1) --put to the end @@ -4982,7 +4982,7 @@ function ctld.repairAASystem(_heli, _nearestCrate, _aaSystem) end function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) - ctld.logTrace("FG_ ctld.unpackMultiCrate, _nearestCrate = %s", ctld.p(_nearestCrate)) + --ctld.logTrace("FG_ ctld.unpackMultiCrate, _nearestCrate = %s", ctld.p(_nearestCrate)) -- unpack multi crate local _nearbyMultiCrates = {} @@ -5033,6 +5033,7 @@ function ctld.unpackMultiCrate(_heli, _nearestCrate, _nearbyCrates) if _spawnedGroup == nil then ctld.logError("ctld.unpackMultiCrate group was not spawned - skipping setGrpROE") else + timer.scheduleFunction(ctld.autoUpdateRepackMenu, { reschedule = false }, timer.getTime() + 1) -- for add unpacked unit in repack menu ctld.setGrpROE(_spawnedGroup) ctld.processCallback({ unit = _heli, crate = _nearestCrate, spawnedGroup = _spawnedGroup, action = "unpack" }) trigger.action.outTextForCoalition(_heli:getCoalition(), @@ -7488,7 +7489,7 @@ function ctld.setGrpROE(_grp, _ROE) local _controller = _grp:getController(); Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) Controller.setOption(_controller, AI.Option.Ground.id.ROE, _ROE) - _controller:setTask(_grp) + --_controller:setTask(_grp) end end From 61db3e9f93c9ebc72122dc674ab4dad01e7bcf25 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 10 May 2025 12:04:46 +0200 Subject: [PATCH 163/166] Commit --- CTLD.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CTLD.lua b/CTLD.lua index 6646f3b..0d0b607 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -7489,7 +7489,7 @@ function ctld.setGrpROE(_grp, _ROE) local _controller = _grp:getController(); Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) Controller.setOption(_controller, AI.Option.Ground.id.ROE, _ROE) - --_controller:setTask(_grp) + --_controller:setTask(_grp) -- FG 250510 this line seems to be a bug end end From 65336fdab8b15d5e0f776ff6afe5da76543aa925 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Sat, 10 May 2025 15:30:25 +0200 Subject: [PATCH 164/166] Final commit --- CTLD.lua | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 0d0b607..0bc5b45 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -1932,9 +1932,9 @@ function ctld.getSecureDistanceFromUnit(_unitName) -- return a distance between local unitUserBox = Unit.getByName(_unitName):getDesc().box local SecureDistanceFromUnit = 0 if math.abs(unitUserBox.max.x) >= math.abs(unitUserBox.min.x) then - SecureDistanceFromUnit = math.abs(unitUserBox.max.x) + rotorDiameter + SecureDistanceFromUnit = math.abs(unitUserBox.max.x) + (rotorDiameter/2) else - SecureDistanceFromUnit = math.abs(unitUserBox.min.x) + rotorDiameter + SecureDistanceFromUnit = math.abs(unitUserBox.min.x) + (rotorDiameter/2) end return SecureDistanceFromUnit end @@ -1945,7 +1945,29 @@ end -- Repack vehicules crates functions -- *************************************************************** ctld.repackRequestsStack = {} -- table to store the repack request +ctld.inAirMemorisation = {} -- last helico state of InAir() +function ctld.updateRepackMenuOnlanding(p, t) -- update helo repack menu when a helo landing is detected + if t == nil then t = timer.getTime() + 1; end + if ctld.transportPilotNames then + for _, _unitName in pairs(ctld.transportPilotNames) do + if Unit.getByName(_unitName) ~= nil and Unit.getByName(_unitName):isActive() == true then + if ctld.inAirMemorisation[_unitName] == nil then ctld.inAirMemorisation[_unitName] = false end -- init InAir() state + local _heli = Unit.getByName(_unitName) + if ctld.inAir(_heli) == false then + if ctld.inAirMemorisation[_unitName] == true then -- if transition from inAir to Landed => updateRepackMenu + ctld.updateRepackMenu(_unitName) + end + ctld.inAirMemorisation[_unitName] = false + else + ctld.inAirMemorisation[_unitName] = true + end + end + end + end + return t + 5 -- reschdule each 5 seconds +end +-- *************************************************************** function ctld.getUnitsInRepackRadius(_PlayerTransportUnitName, _radius) if _radius == nil then _radius = ctld.maximumDistanceRepackableUnitsSearch @@ -2065,6 +2087,7 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' if ctld.unitDynamicCargoCapable(PlayerTransportUnit) ~= false then randomHeading = ctld.RandomReal(playerHeading + math.pi - math.pi/4, playerHeading + math.pi + math.pi/4) end + repackableUnit:destroy() -- destroy repacked unit for i = 1, v.cratesRequired or 1 do -- see to spawn the crate at random position heading the transport unit local _unitId = ctld.getNextUnitId() @@ -2078,10 +2101,8 @@ function ctld.repackVehicle(_params, t) -- scan rrs table 'repackRequestsStack' ctld.spawnCrateStatic(refCountry, _unitId, relativePoint, _name, crateWeight, playerCoa, playerHeading, "dynamic") end end - repackableUnit:destroy() -- destroy repacked unit end - --ctld.updateRepackMenu(playerUnitName) -- update the repack menu - timer.scheduleFunction(ctld.updateRepackMenu, playerUnitName, timer.getTime() + 1) + timer.scheduleFunction(ctld.autoUpdateRepackMenu, { reschedule = false }, timer.getTime() + 1) -- for add unpacked unit in repack menu end ctld.repackRequestsStack[ii] = nil -- remove the processed request from the stacking table end @@ -2577,12 +2598,12 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) local _model_type = nil - local _point = ctld.getPointAt12Oclock(_heli, 30) + local _point = ctld.getPointAt12Oclock(_heli, 15) local _position = "12" if ctld.unitDynamicCargoCapable(_heli) then _model_type = "dynamic" - _point = ctld.getPointAt6Oclock(_heli, 30) + _point = ctld.getPointAt6Oclock(_heli, 15) _position = "6" end @@ -2611,7 +2632,7 @@ function ctld.spawnCrate(_arguments, bypassCrateWaitTime) end --*************************************************************** -ctld.randomCrateSpacing = 20 -- meters +ctld.randomCrateSpacing = 15 -- meters function ctld.getPointAt12Oclock(_unit, _offset) return ctld.getPointAtDirection(_unit, _offset, 0) end @@ -3873,7 +3894,7 @@ function ctld.getClosestCrate(_heli, _crates, _type) local _shortestDistance = -1 local _distance = 0 local _minimumDistance = 5 -- prevents dynamic cargo crates from unpacking while in cargo hold - local _maxDistance = 15 -- prevents onboard dynamic cargo crates from unpacking requested by other helo + local _maxDistance = 20 -- prevents onboard dynamic cargo crates from unpacking requested by other helo for _, _crate in pairs(_crates) do if (_crate.details.unit == _type or _type == nil) then _distance = _crate.dist @@ -4194,7 +4215,7 @@ function ctld.dropSlingCrate(_args) local _name = string.format("%s #%i", _crate.desc, _unitId) local _model_type = nil if ctld.inAir(_heli) == false or _heightDiff <= 7.5 then - _point = ctld.getPointAt12Oclock(_heli, 30) + _point = ctld.getPointAt12Oclock(_heli, 15) local _position = "12" if ctld.unitDynamicCargoCapable(_heli) then _model_type = "dynamic" @@ -6095,10 +6116,7 @@ function ctld.isUnitInMenuEntriesTable(_MenuEntriesTable, _typeUnitDesc) return false end --****************************************************************************************************** -ctld.updateCount = 0 function ctld.updateRepackMenu(_playerUnitName) - ctld.updateCount = ctld.updateCount + 1 - ctld.logTrace("FG_ ctld.updateRepackMenu(%s) - %s", _playerUnitName, ctld.updateCount) local playerUnit = ctld.getTransportUnit(_playerUnitName) if playerUnit then local _groupId = ctld.getGroupId(playerUnit) @@ -8368,7 +8386,7 @@ function ctld.initialize() timer.scheduleFunction(ctld.checkHoverStatus, nil, timer.getTime() + 1) end if ctld.enableRepackingVehicles == true then - --timer.scheduleFunction(ctld.autoUpdateRepackMenu, nil, timer.getTime() + 1) -- initialize repack menu + timer.scheduleFunction(ctld.updateRepackMenuOnlanding, nil, timer.getTime() + 1) -- update helo repack menu when a helo landing is detected timer.scheduleFunction(ctld.repackVehicle, nil, timer.getTime() + 1) end if ctld.enableAutoOrbitingFlyingJtacOnTarget then From 97e7c72144f054b50dd2437816c3f0070f08c593 Mon Sep 17 00:00:00 2001 From: FullGas1 Date: Fri, 16 May 2025 14:34:11 +0200 Subject: [PATCH 165/166] fix "es" dictionary --- CTLD-i18n.lua | 310 +++++++++++++++++++++++++------------------------- 1 file changed, 155 insertions(+), 155 deletions(-) diff --git a/CTLD-i18n.lua b/CTLD-i18n.lua index 050e6ce..651fe1f 100644 --- a/CTLD-i18n.lua +++ b/CTLD-i18n.lua @@ -323,98 +323,98 @@ ctld.i18n["es"]["5x - Mortar Squad"] = "5x - Grupos de morteros" ctld.i18n["es"]["Mortar Squad Red"] = "Grupo mortero rojo" --- crates names -ctld.i18n["es"]["Humvee - MG"] = "" -ctld.i18n["es"]["Humvee - TOW"] = "" -ctld.i18n["es"]["Light Tank - MRAP"] = "" -ctld.i18n["es"]["Med Tank - LAV-25"] = "" -ctld.i18n["es"]["Heavy Tank - Abrams"] = "" -ctld.i18n["es"]["BTR-D"] = "" -ctld.i18n["es"]["BRDM-2"] = "" -ctld.i18n["es"]["Hummer - JTAC"] = "" -ctld.i18n["es"]["M-818 Ammo Truck"] = "" -ctld.i18n["es"]["M-978 Tanker"] = "" -ctld.i18n["es"]["SKP-11 - JTAC"] = "" -ctld.i18n["es"]["Ural-375 Ammo Truck"] = "" -ctld.i18n["es"]["KAMAZ Ammo Truck"] = "" -ctld.i18n["es"]["EWR Radar"] = "" -ctld.i18n["es"]["FOB Crate - Small"] = "" -ctld.i18n["es"]["MQ-9 Repear - JTAC"] = "" -ctld.i18n["es"]["RQ-1A Predator - JTAC"] = "" -ctld.i18n["es"]["MLRS"] = "" -ctld.i18n["es"]["SpGH DANA"] = "" -ctld.i18n["es"]["T155 Firtina"] = "" -ctld.i18n["es"]["Howitzer"] = "" -ctld.i18n["es"]["SPH 2S19 Msta"] = "" -ctld.i18n["es"]["M1097 Avenger"] = "" -ctld.i18n["es"]["M48 Chaparral"] = "" -ctld.i18n["es"]["Roland ADS"] = "" -ctld.i18n["es"]["Gepard AAA"] = "" -ctld.i18n["es"]["LPWS C-RAM"] = "" -ctld.i18n["es"]["9K33 Osa"] = "" -ctld.i18n["es"]["9P31 Strela-1"] = "" -ctld.i18n["es"]["9K35M Strela-10"] = "" -ctld.i18n["es"]["9K331 Tor"] = "" -ctld.i18n["es"]["2K22 Tunguska"] = "" -ctld.i18n["es"]["HAWK Launcher"] = "" -ctld.i18n["es"]["HAWK Search Radar"] = "" -ctld.i18n["es"]["HAWK Track Radar"] = "" -ctld.i18n["es"]["HAWK PCP"] = "" -ctld.i18n["es"]["HAWK CWAR"] = "" -ctld.i18n["es"]["HAWK Repair"] = "" -ctld.i18n["es"]["NASAMS Launcher 120C"] = "" -ctld.i18n["es"]["NASAMS Search/Track Radar"] = "" -ctld.i18n["es"]["NASAMS Command Post"] = "" -ctld.i18n["es"]["NASAMS Repair"] = "" -ctld.i18n["es"]["KUB Launcher"] = "" -ctld.i18n["es"]["KUB Radar"] = "" -ctld.i18n["es"]["KUB Repair"] = "" -ctld.i18n["es"]["BUK Launcher"] = "" -ctld.i18n["es"]["BUK Search Radar"] = "" -ctld.i18n["es"]["BUK CC Radar"] = "" -ctld.i18n["es"]["BUK Repair"] = "" -ctld.i18n["es"]["Patriot Launcher"] = "" -ctld.i18n["es"]["Patriot Radar"] = "" -ctld.i18n["es"]["Patriot ECS"] = "" -ctld.i18n["es"]["Patriot ICC"] = "" -ctld.i18n["es"]["Patriot EPP"] = "" +ctld.i18n["es"]["Humvee - MG"] = "Humvee - Antipersonal .50 cal" +ctld.i18n["es"]["Humvee - TOW"] = "Humvee - Antitanque TOW" +ctld.i18n["es"]["Light Tank - MRAP"] = "Tanque ligero - MRAP" +ctld.i18n["es"]["Med Tank - LAV-25"] = "Tanque Med - LAV-25" +ctld.i18n["es"]["Heavy Tank - Abrams"] = "Tanque pesado - Abrams" +ctld.i18n["es"]["BTR-D"] = "BTR-D - Transporte de tropas" +ctld.i18n["es"]["BRDM-2"] = "BRDM-2 - Reconocimiento" +ctld.i18n["es"]["Hummer - JTAC"] = "JTAC Hummer" +ctld.i18n["es"]["M-818 Ammo Truck"] = "Camión M-818 de municiones" +ctld.i18n["es"]["M-978 Tanker"] = "Camión cisterna M-978" +ctld.i18n["es"]["SKP-11 - JTAC"] = "JTAC SKP-11" +ctld.i18n["es"]["Ural-375 Ammo Truck"] = "Camión Ural-375 de municiones" +ctld.i18n["es"]["KAMAZ Ammo Truck"] = "Camión KAMAZ de municiones" +ctld.i18n["es"]["EWR Radar"] = "Radar Alerta Temprana" +ctld.i18n["es"]["FOB Crate - Small"] = "Caja FOB - Pequeña" +ctld.i18n["es"]["MQ-9 Repear - JTAC"] = "JTAC MQ-9 Repear" +ctld.i18n["es"]["RQ-1A Predator - JTAC"] = "JTAC RQ-1A Predator" +ctld.i18n["es"]["MLRS"] = "MLRS - Artilleria de cohetes" +ctld.i18n["es"]["SpGH DANA"] = "Obus autopropulsado SpGH DANA" +ctld.i18n["es"]["T155 Firtina"] = "Obus autopropulsado T155 Firtina" +ctld.i18n["es"]["Howitzer"] = "Obus autopropulsado M109A6 Paladin" +ctld.i18n["es"]["SPH 2S19 Msta"] = "SPH 2S19 Msta - Obus Autopropulsado" +ctld.i18n["es"]["M1097 Avenger"] = "M1097 Avenger - SAM Corta Distancia" +ctld.i18n["es"]["M48 Chaparral"] = "M48 Chaparral - SAM Corta Distancia" +ctld.i18n["es"]["Roland ADS"] = "Roland ADS - Lanzador" +ctld.i18n["es"]["Gepard AAA"] = "Gepard AAA - AAA" +ctld.i18n["es"]["LPWS C-RAM"] = "LPWS C-RAM - AAA" +ctld.i18n["es"]["9K33 Osa"] = "9K33 Osa - SA-8 Gecko" +ctld.i18n["es"]["9P31 Strela-1"] = "9P31 Strela-1 - SA-9 Gaskin" +ctld.i18n["es"]["9K35M Strela-10"] = "9K35M Strela-10 - SA-13 Gopher" +ctld.i18n["es"]["9K331 Tor"] = "9K331 Tor - SA-15 Tor" +ctld.i18n["es"]["2K22 Tunguska"] = "2K22 Tunguska - SA-19 Tunguska" +ctld.i18n["es"]["HAWK Launcher"] = "HAWK - Lanzador" +ctld.i18n["es"]["HAWK Search Radar"] = "HAWK - Radar de Búsqueda" +ctld.i18n["es"]["HAWK Track Radar"] = "HAWK - Radar de Seguimiento" +ctld.i18n["es"]["HAWK PCP"] = "HAWK - Puesto de Comando" +ctld.i18n["es"]["HAWK CWAR"] = "HAWK - Sistema de Control de Guerra" +ctld.i18n["es"]["HAWK Repair"] = "Reparar HAWK" +ctld.i18n["es"]["NASAMS Launcher 120C"] = "NASAMS - Lanzador 120C" +ctld.i18n["es"]["NASAMS Search/Track Radar"] = "NASAMS - Radar de Búsqueda/Seguimiento" +ctld.i18n["es"]["NASAMS Command Post"] = "NASAMS - Puesto de Mando" +ctld.i18n["es"]["NASAMS Repair"] = "Reparar NASAMS" +ctld.i18n["es"]["KUB Launcher"] = "KUB - Lanzador" +ctld.i18n["es"]["KUB Radar"] = "KUB - Radar" +ctld.i18n["es"]["KUB Repair"] = "Reparar KUB" +ctld.i18n["es"]["BUK Launcher"] = "BUK - Lanzador" +ctld.i18n["es"]["BUK Search Radar"] = "BUK - Radar de Búsqueda" +ctld.i18n["es"]["BUK CC Radar"] = "BUK - Radar de Control de Combate" +ctld.i18n["es"]["BUK Repair"] = "Reparar BUK" +ctld.i18n["es"]["Patriot Launcher"] = "Patriot - Lanzador" +ctld.i18n["es"]["Patriot Radar"] = "Patriot - Radar de Búsqueda" +ctld.i18n["es"]["Patriot ECS"] = "Patriot - Puesto de Mando" +ctld.i18n["es"]["Patriot ICC"] = "Patriot - Sistema de Control de Fuego" +ctld.i18n["es"]["Patriot EPP"] = "Patriot - Generador" ctld.i18n["es"]["Patriot AMG (optional)"] = "" -ctld.i18n["es"]["Patriot Repair"] = "" -ctld.i18n["es"]["S-300 Grumble TEL C"] = "" -ctld.i18n["es"]["S-300 Grumble Flap Lid-A TR"] = "" -ctld.i18n["es"]["S-300 Grumble Clam Shell SR"] = "" -ctld.i18n["es"]["S-300 Grumble Big Bird SR"] = "" -ctld.i18n["es"]["S-300 Grumble C2"] = "" -ctld.i18n["es"]["S-300 Repair"] = "" -ctld.i18n["es"]["Humvee - TOW - All crates"] = "Humvee - TOW - Todas cajas" -ctld.i18n["es"]["Light Tank - MRAP - All crates"] = "Light Tank - MRAP - Todas cajas" -ctld.i18n["es"]["Med Tank - LAV-25 - All crates"] = "Med Tank - LAV-25 - Todas cajas" -ctld.i18n["es"]["Heavy Tank - Abrams - All crates"] = "Heavy Tank - Abrams - Todas cajas" -ctld.i18n["es"]["Hummer - JTAC - All crates"] = "Hummer - JTAC - Todas cajas" -ctld.i18n["es"]["M-818 Ammo Truck - All crates"] = "M-818 Ammo Truck - Todas cajas" -ctld.i18n["es"]["M-978 Tanker - All crates"] = "M-978 Tanker - Todas cajas" -ctld.i18n["es"]["Ural-375 Ammo Truck - All crates"] = "Ural-375 Ammo Truck - Todas cajas" -ctld.i18n["es"]["EWR Radar - All crates"] = "EWR Radar - Todas cajas" -ctld.i18n["es"]["MLRS - All crates"] = "MLRS - Todas cajas" -ctld.i18n["es"]["SpGH DANA - All crates"] = "SpGH DANA - Todas cajas" -ctld.i18n["es"]["T155 Firtina - All crates"] = "T155 Firtina - Todas cajas" -ctld.i18n["es"]["Howitzer - All crates"] = "Howitzer - Todas cajas" -ctld.i18n["es"]["SPH 2S19 Msta - All crates"] = "SPH 2S19 Msta - Todas cajas" -ctld.i18n["es"]["M1097 Avenger - All crates"] = "M1097 Avenger - Todas cajas" -ctld.i18n["es"]["M48 Chaparral - All crates"] = "M48 Chaparral - Todas cajas" -ctld.i18n["es"]["Roland ADS - All crates"] = "Roland ADS - Todas cajas" -ctld.i18n["es"]["Gepard AAA - All crates"] = "Gepard AAA - Todas cajas" -ctld.i18n["es"]["LPWS C-RAM - All crates"] = "LPWS C-RAM - Todas cajas" -ctld.i18n["es"]["9K33 Osa - All crates"] = "9K33 Osa - Todas cajas" -ctld.i18n["es"]["9P31 Strela-1 - All crates"] = "9P31 Strela-1 - Todas cajas" -ctld.i18n["es"]["9K35M Strela-10 - All crates"] = "9K35M Strela-10 - Todas cajas" -ctld.i18n["es"]["9K331 Tor - All crates"] = "9K331 Tor - Todas cajas" -ctld.i18n["es"]["2K22 Tunguska - All crates"] = "2K22 Tunguska - Todas cajas" -ctld.i18n["es"]["HAWK - All crates"] = "HAWK - Todas cajas" -ctld.i18n["es"]["NASAMS - All crates"] = "NASAMS - Todas cajas" -ctld.i18n["es"]["KUB - All crates"] = "KUB - Todas cajas" -ctld.i18n["es"]["BUK - All crates"] = "BUK - Todas cajas" -ctld.i18n["es"]["Patriot - All crates"] = "Patriot - Todas cajas" -ctld.i18n["es"]["Patriot - All crates"] = "Patriot - Todas cajas" +ctld.i18n["es"]["Patriot Repair"] = "Reparar Patriot" +ctld.i18n["es"]["S-300 Grumble TEL C"] = "S-300 Grumble TEL C - Lanzador" +ctld.i18n["es"]["S-300 Grumble Flap Lid-A TR"] = "S-300 Grumble Flap Lid-A TR - Radar de Seguimiento" +ctld.i18n["es"]["S-300 Grumble Clam Shell SR"] = "S-300 Grumble Clam Shell SR - Radar de Búsqueda" +ctld.i18n["es"]["S-300 Grumble Big Bird SR"] = "S-300 Grumble Big Bird SR - Radar de Búsqueda" +ctld.i18n["es"]["S-300 Grumble C2"] = "S-300 Grumble C2 - Puesto de Mando" +ctld.i18n["es"]["S-300 Repair"] = "Reparar S-300" +ctld.i18n["es"]["Humvee - TOW - All crates"] = "Humvee - TOW - Todas las cajas" +ctld.i18n["es"]["Light Tank - MRAP - All crates"] = "Light Tank - MRAP - Todas las cajas" +ctld.i18n["es"]["Med Tank - LAV-25 - All crates"] = "Med Tank - LAV-25 - Todas las cajas" +ctld.i18n["es"]["Heavy Tank - Abrams - All crates"] = "Heavy Tank - Abrams - Todas las cajas" +ctld.i18n["es"]["Hummer - JTAC - All crates"] = "Hummer - JTAC - Todas las cajas" +ctld.i18n["es"]["M-818 Ammo Truck - All crates"] = "M-818 Ammo Truck - Todas las cajas" +ctld.i18n["es"]["M-978 Tanker - All crates"] = "M-978 Tanker - Todas las cajas" +ctld.i18n["es"]["Ural-375 Ammo Truck - All crates"] = "Ural-375 Ammo Truck - Todas las cajas" +ctld.i18n["es"]["EWR Radar - All crates"] = "EWR Radar - Todas las cajas" +ctld.i18n["es"]["MLRS - All crates"] = "MLRS - Todas las cajas" +ctld.i18n["es"]["SpGH DANA - All crates"] = "SpGH DANA - Todas las cajas" +ctld.i18n["es"]["T155 Firtina - All crates"] = "T155 Firtina - Todas las cajas" +ctld.i18n["es"]["Howitzer - All crates"] = "Howitzer - Todas las cajas" +ctld.i18n["es"]["SPH 2S19 Msta - All crates"] = "SPH 2S19 Msta - Todas las cajas" +ctld.i18n["es"]["M1097 Avenger - All crates"] = "M1097 Avenger - Todas las cajas" +ctld.i18n["es"]["M48 Chaparral - All crates"] = "M48 Chaparral - Todas las cajas" +ctld.i18n["es"]["Roland ADS - All crates"] = "Roland ADS - Todas las cajas" +ctld.i18n["es"]["Gepard AAA - All crates"] = "Gepard AAA - Todas las cajas" +ctld.i18n["es"]["LPWS C-RAM - All crates"] = "LPWS C-RAM - Todas las cajas" +ctld.i18n["es"]["9K33 Osa - All crates"] = "9K33 Osa - Todas las cajas" +ctld.i18n["es"]["9P31 Strela-1 - All crates"] = "9P31 Strela-1 - Todas las cajas" +ctld.i18n["es"]["9K35M Strela-10 - All crates"] = "9K35M Strela-10 - Todas las cajas" +ctld.i18n["es"]["9K331 Tor - All crates"] = "9K331 Tor - Todas las cajas" +ctld.i18n["es"]["2K22 Tunguska - All crates"] = "2K22 Tunguska - Todas las cajas" +ctld.i18n["es"]["HAWK - All crates"] = "HAWK - Todas clas ajas" +ctld.i18n["es"]["NASAMS - All crates"] = "NASAMS - Todas las cajas" +ctld.i18n["es"]["KUB - All crates"] = "KUB - Todas las cajas" +ctld.i18n["es"]["BUK - All crates"] = "BUK - Todas las cajas" +ctld.i18n["es"]["Patriot - All crates"] = "Patriot - Todas las cajas" +ctld.i18n["es"]["Patriot - All crates"] = "Patriot - Todas las cajas" --- mission design error messages ctld.i18n["es"]["CTLD.lua ERROR: Can't find trigger called %1"] = "CTLD.lua ERROR : Imposible encontrar el activador llamado %1" @@ -423,33 +423,33 @@ ctld.i18n["es"]["CTLD.lua ERROR: Can't find zone or ship called %1"] = "CTLD.lua ctld.i18n["es"]["CTLD.lua ERROR: Can't find crate with weight %1"] = "CTLD.lua ERROR : Imposible encontrar una caja con un peso de %1" --- runtime messages -ctld.i18n["es"]["You are not close enough to friendly logistics to get a crate!"] = "¡No estás lo suficientemente cerca de la logística amigable para conseguir una caja!" +ctld.i18n["es"]["You are not close enough to friendly logistics to get a crate!"] = "¡No estás lo suficientemente cerca de la logística aliada para solicitar una caja!" ctld.i18n["es"]["No more JTAC Crates Left!"] = "¡No hay más cajas JTAC disponibles!" -ctld.i18n["es"]["Sorry you must wait %1 seconds before you can get another crate"] = "Lo sentimos, debes esperar %1 segundos antes de poder conseguir otra caja" -ctld.i18n["es"]["A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock "] = "Una caja %1 que pesa %2 kg ha sido sacada y está a tus %3 horas " -ctld.i18n["es"]["%1 fast-ropped troops from %2 into combat"] = "%1 lanzó rápidamente tropas de %2 al combate" -ctld.i18n["es"]["%1 dropped troops from %2 into combat"] = "%1 arrojó tropas de %2 al combate" -ctld.i18n["es"]["%1 fast-ropped troops from %2 into %3"] = "%1 lanzó tropas rápidamente de %2 a %3" +ctld.i18n["es"]["Sorry you must wait %1 seconds before you can get another crate"] = "Lo sentimos, debes esperar %1 segundos antes de poder solicitar otra caja" +ctld.i18n["es"]["A %1 crate weighing %2 kg has been brought out and is at your %3 o'clock "] = "Una caja %1 pesando %2 kg ha sido preparada y está a tus %3 en punto " +ctld.i18n["es"]["%1 fast-ropped troops from %2 into combat"] = "%1 descolgo tropas con cuerdas de %2 al combate" +ctld.i18n["es"]["%1 dropped troops from %2 into combat"] = "%1 descargo tropas de %2 al combate" +ctld.i18n["es"]["%1 fast-ropped troops from %2 into %3"] = "%1 descolgo tropas con cuerdas de %2 a %3" ctld.i18n["es"]["%1 dropped troops from %2 into %3"] = "%1 arrojó tropas de %2 a %3" -ctld.i18n["es"]["Too high or too fast to drop troops into combat! Hover below %1 feet or land."] = "¡Demasiado alto o demasiado rápido para lanzar tropas al combate! Coloca el cursor por debajo de % 1 pies o aterriza." -ctld.i18n["es"]["%1 dropped vehicles from %2 into combat"] = "%1 descargo vehículos de %2 en combate" +ctld.i18n["es"]["Too high or too fast to drop troops into combat! Hover below %1 feet or land."] = "¡Demasiado alto o rápido para lanzar tropas al combate! Manten estacionario por debajo de % 1 pies o aterriza." +ctld.i18n["es"]["%1 dropped vehicles from %2 into combat"] = "%1 descargo vehículos de %2 al combate" ctld.i18n["es"]["%1 loaded troops into %2"] = "%1 cargó tropas en %2" ctld.i18n["es"]["%1 loaded %2 vehicles into %3"] = "%1 cargó %2 vehículos en %3" ctld.i18n["es"]["%1 delivered a FOB Crate"] = "%1 entregó una caja FOB" -ctld.i18n["es"]["Delivered FOB Crate 60m at 6'oclock to you"] = "Se le entregó la caja FOB de 60 m a sus 6 horas" -ctld.i18n["es"]["FOB Crate dropped back to base"] = "La caja FOB volvió a la base" +ctld.i18n["es"]["Delivered FOB Crate 60m at 6 o'clock to you"] = "Se le entregó la caja FOB de 60 m a sus 6 en punto" +ctld.i18n["es"]["FOB Crate dropped back to base"] = "Caja FOB devuelta a la base" ctld.i18n["es"]["FOB Crate Loaded"] = "Caja FOB cargada" ctld.i18n["es"]["%1 loaded a FOB Crate ready for delivery!"] = "%1 cargó una caja FOB lista para su entrega!" ctld.i18n["es"]["There are no friendly logistic units nearby to load a FOB crate from!"] = "¡No hay unidades logísticas amigas cerca para cargar una caja FOB!" ctld.i18n["es"]["This area has no more reinforcements available!"] = "¡Esta área no tiene más refuerzos disponibles!" -ctld.i18n["es"]["You are not in a pickup zone and no one is nearby to extract"] = "No estás en una zona de recogida y no hay nadie cerca para extraerlo" -ctld.i18n["es"]["You are not in a pickup zone"] = "No estás en una zona de recogida" -ctld.i18n["es"]["No one to unload"] = "Nadie para descargar" -ctld.i18n["es"]["Dropped troops back to base"] = "Tropas arrojadas a la base" -ctld.i18n["es"]["Dropped vehicles back to base"] = "Vehículos arrojados a la base" +ctld.i18n["es"]["You are not in a pickup zone and no one is nearby to extract"] = "No estás en una zona de carga y/o no hay nadie cerca para extraccion" +ctld.i18n["es"]["You are not in a pickup zone"] = "No estás en una zona de carga" +ctld.i18n["es"]["No one to unload"] = "Nadie / Nada para descargar" +ctld.i18n["es"]["Dropped troops back to base"] = "Tropas descargados de vuelta a la base" +ctld.i18n["es"]["Dropped vehicles back to base"] = "Vehículos descargados de vuelta a la base" ctld.i18n["es"]["You already have troops onboard."] = "Ya tienes tropas a bordo." ctld.i18n["es"]["Count Infantries limit in the mission reached, you can't load more troops"] = "Se alcanzó el límite de infantería en la misión, no puedes cargar más tropas" -ctld.i18n["es"]["You already have vehicles onboard."] = "Ya tiene vehículos a bordo." +ctld.i18n["es"]["You already have vehicles onboard."] = "Ya tienes vehículos a bordo." ctld.i18n["es"]["Sorry - The group of %1 is too large to fit. \n\nLimit is %2 for %3"] = "Lo sentimos, el grupo de %1 es demasiado grande. \n \nEl límite es %2 para %3" ctld.i18n["es"]["%1 extracted troops in %2 from combat"] = "%1 tropas extraídas del combate en %2" ctld.i18n["es"]["No extractable troops nearby!"] = "¡No hay tropas extraíbles cerca!" @@ -460,69 +460,69 @@ ctld.i18n["es"]["%1 vehicles onboard (%2)\n"] = "%1 vehículos a bordo (%2)\n" ctld.i18n["es"]["1 FOB Crate oboard (%1 kg)\n"] = "1 caja FOB a bordo (%1 kg)\n" ctld.i18n["es"]["%1 crate onboard (%2 kg)\n"] = "%1 caja a bordo (%2 kg)\n" ctld.i18n["es"]["Total weight of cargo : %1 kg\n"] = "Peso total de la carga: %1 kg\n" -ctld.i18n["es"]["No cargo."] = "Aucune cargaison." -ctld.i18n["es"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "Flotando sobre %1 caja. \n\n¡Manténte flotando sobre ella durante %2 segundos! \n\n¡Si la cuenta regresiva se detiene, estás demasiado lejos!" +ctld.i18n["es"]["No cargo."] = "Sin carga." +ctld.i18n["es"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "En estacionario sobre la caja %1 \n\n¡Mantenlo durante %2 segundos! \n\n¡Si la cuenta atras se detiene, estás demasiado lejos!" ctld.i18n["es"]["Loaded %1 crate!"] = "Caja %1 cargada !" -ctld.i18n["es"]["Too low to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado bajo para enganchar la caja %1.\n\nManténte flotando sobre ella durante %2 segundos" -ctld.i18n["es"]["Too high to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado alto para enganchar la caja %1.\n\nManténte flotando sobre ella durante %2 segundos" +ctld.i18n["es"]["Too low to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado bajo para enganchar la caja %1.\n\nMantén el estacionario durante %2 segundos" +ctld.i18n["es"]["Too high to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado alto para enganchar la caja %1.\n\nMantén el estacionario durante %2 segundos" ctld.i18n["es"]["You must land before you can load a crate!"] = "¡Debes aterrizar antes de poder cargar una caja!" -ctld.i18n["es"]["No Crates within 50m to load!"] = "¡No hay cajas para cargar a 50 m a la redonda!" +ctld.i18n["es"]["No Crates within 50m to load!"] = "¡No hay cajas para cargar en un radio de 50 m!" ctld.i18n["es"]["Maximum number of crates are on board!"] = "¡El número máximo de cajas está a bordo!" -ctld.i18n["es"]["%1\n%2 crate - kg %3 - %4 m - %5 o'clock"] = "%1\n%2 caja - kg %3 - %4 m - %5 horas" -ctld.i18n["es"]["FOB Crate - %1 m - %2 o'clock\n"] = "Caja FOB - %1 m - %2 horas\n" +ctld.i18n["es"]["%1\n%2 crate - kg %3 - %4 m - %5 o'clock"] = "%1\n%2 caja - kg %3 - %4 m - a tus %5 en punto" +ctld.i18n["es"]["FOB Crate - %1 m - %2 o'clock\n"] = "Caja FOB - %1 m - a las %2 en punto\n" ctld.i18n["es"]["No Nearby Crates"] = "Ninguna caja de proximidad" ctld.i18n["es"]["Nearby Crates:\n%1"] = "Cajas cercanas:\n%1" -ctld.i18n["es"]["Nearby FOB Crates (Not Slingloadable):\n%1"] = "Cajas FOB cercanas (no transportable por cable):\n%1" +ctld.i18n["es"]["Nearby FOB Crates (Not Slingloadable):\n%1"] = "Cajas FOB cercanas (no se pueden cargar con eslinga):\n%1" ctld.i18n["es"]["FOB Positions:"] = "Posiciones FOB:" ctld.i18n["es"]["%1\nFOB @ %2"] = "%1\nFOB @ %2" ctld.i18n["es"]["Sorry, there are no active FOBs!"] = "¡Lo sentimos, no hay FOB activos!" ctld.i18n["es"]["No cargo."] = "Sin carga." -ctld.i18n["es"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "Estacionario sobre la caja %1 \n\n¡Mantenlo flotando durante %2 segundos! \n\n¡Si la cuenta regresiva se detiene, estás demasiado lejos!" +ctld.i18n["es"]["Hovering above %1 crate. \n\nHold hover for %2 seconds! \n\nIf the countdown stops you're too far away!"] = "En estacionario sobre la caja %1 \n\n¡Mantenlo durante %2 segundos! \n\n¡Si la cuenta atras se detiene, estás demasiado lejos!" ctld.i18n["es"]["Loaded %1 crate!"] = "¡Caja %1 cargada!" ctld.i18n["es"]["Too low to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado bajo para enganchar la caja %1.\n\nMantén el estacionario durante %2 segundos" ctld.i18n["es"]["Too high to hook %1 crate.\n\nHold hover for %2 seconds"] = "Demasiado alto para enganchar la caja %1.\n\nMantén el estacionario durante %2 segundos" ctld.i18n["es"]["You must land before you can load a crate!"] = "¡Debes aterrizar antes de poder cargar una caja!" ctld.i18n["es"]["No Crates within 50m to load!"] = "¡No hay cajas para cargar en un radio de 50 m!" ctld.i18n["es"]["Maximum number of crates are on board!"] = "¡Número máximo de cajas a bordo!" -ctld.i18n["es"]["%1\n%2 crate - kg %3 - %4 m - %5 o'clock"] = "%1\n%2 caja - kg %3 - %4 m - %5 horas" -ctld.i18n["es"]["FOB Crate - %1 m - %2 o'clock\n"] = "Caja FOB - %1 m - %2 horas\n" +ctld.i18n["es"]["%1\n%2 crate - kg %3 - %4 m - %5 o'clock"] = "%1\n%2 caja - kg %3 - %4 m - a tus %5 en punto" +ctld.i18n["es"]["FOB Crate - %1 m - %2 o'clock\n"] = "Caja FOB - %1 m - a tus %2 en punto\n" ctld.i18n["es"]["No Nearby Crates"] = "No hay cajas cerca" ctld.i18n["es"]["Nearby Crates:\n%1"] = "Cajas cercanas:\n%1" ctld.i18n["es"]["Nearby FOB Crates (Not Slingloadable):\n%1"] = "Cajas FOB cercanas (no se pueden cargar con eslinga):\n%1" ctld.i18n["es"]["FOB Positions:"] = "Posiciones FOB:" ctld.i18n["es"]["%1\nFOB @ %2"] = "%1\nFOB @ %2" ctld.i18n["es"]["Sorry, there are no active FOBs!"] = "¡Lo sentimos, no hay FOB activos!" -ctld.i18n["es"]["You can't unpack that here! Take it to where it's needed!"] = "¡No puedes desembalar eso aquí! ¡Llévalo a donde lo necesites!" -ctld.i18n["es"]["Sorry you must move this crate before you unpack it!"] = "¡Lo siento, debes mover esta caja antes de desempacarla!" -ctld.i18n["es"]["%1 successfully deployed %2 to the field"] = "%1 implementó exitosamente %2 en el campo." -ctld.i18n["es"]["No friendly crates close enough to unpack, or crate too close to aircraft."] = "No hay cajas amigas lo suficientemente cerca para desempacar, o la caja está demasiado cerca de un avión" +ctld.i18n["es"]["You can't unpack that here! Take it to where it's needed!"] = "¡No puedes desembalar eso aquí! ¡Llévalo a donde lo necesiten!" +ctld.i18n["es"]["Sorry you must move this crate before you unpack it!"] = "¡Lo siento, debes mover esta caja antes de desembalar!" +ctld.i18n["es"]["%1 successfully deployed %2 to the field"] = "%1 Desplego %2 con exito en el campo." +ctld.i18n["es"]["No friendly crates close enough to unpack, or crate too close to aircraft."] = "No hay cajas amigas lo suficientemente cerca por desembalar, o la caja está demasiado cerca de un avión" ctld.i18n["es"]["Finished building FOB! Crates and Troops can now be picked up."] = "¡Construcción FOB completada! Ahora se pueden recoger cajas y tropas" -ctld.i18n["es"]["Finished building FOB! Crates can now be picked up."] ="¡Construcción FOB completada! Las cajas ahora se pueden recoger." -ctld.i18n["es"]["%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke."] = "%1 comenzó a construir FOB usando %2 cajas FOB , estará terminado en %3 segundos.\nPosición marcada por la bomba de humo." +ctld.i18n["es"]["Finished building FOB! Crates can now be picked up."] ="¡Construcción FOB completada! Ahora se pueden recoger cajas." +ctld.i18n["es"]["%1 started building FOB using %2 FOB crates, it will be finished in %3 seconds.\nPosition marked with smoke."] = "%1 comenzó a construir FOB usando %2 cajas FOB , estará terminado en %3 segundos.\nPosición marcada con bomba de humo." ctld.i18n["es"]["Cannot build FOB!\n\nIt requires %1 Large FOB crates ( 3 small FOB crates equal 1 large FOB Crate) and there are the equivalent of %2 large FOB crates nearby\n\nOr the crates are not within 750m of each other"] = "¡No se puede construir el FOB!\n\nSe requiere %1 cajas FOB grandes (3 cajas FOB pequeñas equivalente a 1 caja FOB grande) y hay el equivalente a %2 cajas FOB grandes cerca\n\nO las cajas no están a menos de 750 m una de otra" -ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands."] = "Actualmente no estás transportando ninguna caja.\n\nPara cargar una caja, flota sobre la caja durante %1 segundos o aterrice y use los comandos de caja F10." -ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate."] = "Actualmente no estás transportando ninguna caja. \n\nPour recoge una caja, flota sobre la caja durante %1 segundos." +ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate or land and use F10 Crate Commands."] = "Actualmente no estás transportando ninguna caja.\n\nPara cargar una caja, realiza un estacionario sobre la caja durante %1 segundos o aterrice y use los comandos de caja F10." +ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, hover for %1 seconds above the crate."] = "Actualmente no estás transportando ninguna caja. \n\nPara cargar una caja, realiza un estacionario sobre la caja durante %1 segundos." ctld.i18n["es"]["You are not currently transporting any crates. \n\nTo Pickup a crate, land and use F10 Crate Commands to load one."] = "Actualmente no estás transportando ninguna caja. \n\nPara cargar una caja, aterriza y usa los controles de la caja F10." -ctld.i18n["es"]["%1 crate has been safely unhooked and is at your %2 o'clock"] = "%1 caja ha sido desenganchada de forma segura y está en tus %2 horas" -ctld.i18n["es"]["%1 crate has been safely dropped below you"] = "%1 caja se ha dejado caer de forma segura debajo de ti" -ctld.i18n["es"]["You were too high! The crate has been destroyed"] = "¡Estabas demasiado drogado! La caja ha sido destruida" +ctld.i18n["es"]["%1 crate has been safely unhooked and is at your %2 o'clock"] = "%1 caja desenganchada de forma segura y está en tus %2 en punto" +ctld.i18n["es"]["%1 crate has been safely dropped below you"] = "%1 caja ha soltado de forma segura debajo de ti" +ctld.i18n["es"]["You were too high! The crate has been destroyed"] = "¡Estabas demasiado alto! La caja ha sido destruida" ctld.i18n["es"]["Radio Beacons:\n%1"] = "Balizas de radio:\n%1" ctld.i18n["es"]["No Active Radio Beacons"] = "No hay radiobalizas activas" -ctld.i18n["es"]["%1 deployed a Radio Beacon.\n\n%2"] = "%1 implementó una radiobaliza.\n\n%2" +ctld.i18n["es"]["%1 deployed a Radio Beacon.\n\n%2"] = "%1 Despliega una radiobaliza.\n\n%2" ctld.i18n["es"]["You need to land before you can deploy a Radio Beacon!"] = "¡Debes aterrizar antes de poder desplegar una radiobaliza!" ctld.i18n["es"]["%1 removed a Radio Beacon.\n\n%2"] = "%1 eliminó una radiobaliza.\n\n%2" ctld.i18n["es"]["No Radio Beacons within 500m."] = "No hay radiobalizas a menos de 500 m." ctld.i18n["es"]["You need to land before remove a Radio Beacon"] = "Es necesario aterrizar antes de eliminar una radiobaliza" -ctld.i18n["es"]["%1 successfully rearmed a full %2 in the field"] = "%1 rearmó exitosamente un %2 completo en el campo" -ctld.i18n["es"]["Missing %1\n"] = "%1 falta\n" +ctld.i18n["es"]["%1 successfully rearmed a full %2 in the field"] = "%1 rearmó con exito un %2 completo en el campo" +ctld.i18n["es"]["Missing %1\n"] = "Faltan: %1\n" ctld.i18n["es"]["Out of parts for AA Systems. Current limit is %1\n"] = "Sin piezas para sistemas AA. El límite actual es %1\n" -ctld.i18n["es"]["Cannot build %1\n%2\n\nOr the crates are not close enough together"] = "Imposible construir %1\n%2\n\nO las cajas no están lo suficientemente cerca el uno del otro." -ctld.i18n["es"]["%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4"] = "%1 implementó exitosamente un % 2 completo en el campo \n\nEl límite AA del sistema activo es: %3\nActivo: %4" -ctld.i18n["es"]["%1 successfully repaired a full %2 in the field."] = "%1 reparó exitosamente un %2 completo en el campo." -ctld.i18n["es"]["Cannot repair %1. No damaged %2 within 300m"] = "Imposible de reparar %1. No hay daños en %2 dentro de 300 m" -ctld.i18n["es"]["%1 successfully deployed %2 to the field using %3 crates."] = "%1 implementó exitosamente %2 en el campo usando %3 cajas." -ctld.i18n["es"]["Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other"] = "Imposible construir %1 !\n\nNecesitamos %2 cajas y hay %3 \n\nO las cajas están a no menos de 300 m una de otra" -ctld.i18n["es"]["%1 dropped %2 smoke."] = "%1 arrojó un %2 humo." +ctld.i18n["es"]["Cannot build %1\n%2\n\nOr the crates are not close enough together"] = "Imposible construir %1\n%2\n\nO las cajas no están lo suficientemente cerca unas de otras." +ctld.i18n["es"]["%1 successfully deployed a full %2 in the field. \n\nAA Active System limit is: %3\nActive: %4"] = "%1 Despliegue con exito un % 2 completo en el campo \n\nEl límite AA del sistema activo es: %3\nActivo: %4" +ctld.i18n["es"]["%1 successfully repaired a full %2 in the field."] = "%1 reparó con exito un %2 completo en el campo." +ctld.i18n["es"]["Cannot repair %1. No damaged %2 within 300m"] = "Imposible reparar %1. No hay daños en %2 en 300 m al rededor" +ctld.i18n["es"]["%1 successfully deployed %2 to the field using %3 crates."] = "%1 Despliegue con exito de %2 en el campo usando %3 cajas." +ctld.i18n["es"]["Cannot build %1!\n\nIt requires %2 crates and there are %3 \n\nOr the crates are not within 300m of each other"] = "Imposible construir %1 !\n\nNecesita %2 cajas y hay %3 \n\nO las cajas están a no menos de 300 m una de otra" +ctld.i18n["es"]["%1 dropped %2 smoke."] = "%1 lanzo humo %2." --- JTAC messages ctld.i18n["es"]["JTAC Group %1 KIA!"] = "¡Grupo JTAC %1 KIA!" @@ -540,14 +540,14 @@ ctld.i18n["es"]["%1 %2 target destroyed."] = "%1 %2 objetivo destruido." ctld.i18n["es"]["JTAC STATUS: \n\n"] = "ESTADO JTAC: \n\n" ctld.i18n["es"][", available on %1 %2,"] = ", disponible en %1 %2," ctld.i18n["es"]["UNKNOWN"] = "DESCONOCIDO" -ctld.i18n["es"][" targeting "] = " apuntación " -ctld.i18n["es"][" targeting selected unit "] = " apuntando a la unidad seleccionada" -ctld.i18n["es"][" attempting to find selected unit, temporarily targeting "] = " intento de encontrar la unidad seleccionada, objetivo temporal " +ctld.i18n["es"][" targeting "] = " apuntando " +ctld.i18n["es"][" targeting selected unit "] = " apuntando a la unidad indicada" +ctld.i18n["es"][" attempting to find selected unit, temporarily targeting "] = " intentando encontrar la unidad indicada, laser activo " ctld.i18n["es"]["(Laser OFF) "] = "(Láser INACTIVO) " ctld.i18n["es"]["Visual On: "] = "Visual activado: " ctld.i18n["es"][" searching for targets %1\n"] = " buscando objetivos %1\n" -ctld.i18n["es"]["No Active JTACs"] = "No hay JTAC activos" -ctld.i18n["es"][", targeting selected unit, %1"] = ", apuntando a la unidad seleccionada, %1" +ctld.i18n["es"]["No Active JTACs"] = "Sin JTAC activos" +ctld.i18n["es"][", targeting selected unit, %1"] = ", apuntando a la unidad indicada, %1" ctld.i18n["es"][". CODE: %1. POSITION: %2"] = ". CÓDIGO: %1. POSICIÓN: %2" ctld.i18n["es"][", target selection reset."] = ", reinicio de selección de objetivo." ctld.i18n["es"]["%1, laser and smokes enabled"] = "%1, láser y humo habilitados" @@ -562,8 +562,8 @@ ctld.i18n["es"]["Troop Transport"] = "Transporte de tropas" ctld.i18n["es"]["Unload / Extract Troops"] = "Descargar/Extraer tropas" ctld.i18n["es"]["Next page"] = "Página siguiente" ctld.i18n["es"]["Load "] = "Cargar " -ctld.i18n["es"]["Vehicle / FOB Transport"] = "Transporte Vehículo / FOB" -ctld.i18n["es"]["Crates: Vehicle / FOB / Drone"] = "Cajas Vehículo / FOB / Dron" +ctld.i18n["es"]["Vehicle / FOB Transport"] = "Transporte de Vehículo / FOB" +ctld.i18n["es"]["Vehicle / FOB Crates / Drone"] = "Cajas de Vehículo / FOB / Dron" ctld.i18n["es"]["Unload Vehicles"] = "Descargar vehículos" ctld.i18n["es"]["Load / Extract Vehicles"] = "Cargar/Extraer vehículos" ctld.i18n["es"]["Load / Unload FOB Crate"] = "Cargar/Descargar caja FOB" @@ -572,25 +572,25 @@ ctld.i18n["es"]["CTLD Commands"] = "Comandos CTLD" ctld.i18n["es"]["CTLD"] = "CTLD" ctld.i18n["es"]["Check Cargo"] = "Verificar carga" ctld.i18n["es"]["Load Nearby Crate(s)"] = "Cargar caja(s) cercana(s)" -ctld.i18n["es"]["Unpack Any Crate"] = "Desempaquetar las cajas" -ctld.i18n["es"]["Drop Crate(s)"] = "Descargar caja(s)" +ctld.i18n["es"]["Unpack Any Crate"] = "Desempaquetar cajas" +ctld.i18n["es"]["Drop Crate(s)"] = "Soltar caja(s)" ctld.i18n["es"]["List Nearby Crates"] = "Enumerar cajas cercanas" ctld.i18n["es"]["List FOBs"] = "Enumerar FOBs" ctld.i18n["es"]["List Beacons"] = "Enumerar balizas" ctld.i18n["es"]["List Radio Beacons"] = "Enumerar radiobalizas" ctld.i18n["es"]["Smoke Markers"] = "Marcadores de humo" -ctld.i18n["es"]["Drop Red Smoke"] = "Soltar humo rojo" -ctld.i18n["es"]["Drop Blue Smoke"] = "Soltar humo azul" -ctld.i18n["es"]["Drop Orange Smoke"] = "Soltar humo naranja" -ctld.i18n["es"]["Drop Green Smoke"] = "Soltar humo verde" -ctld.i18n["es"]["Drop Beacon"] = "Soltar baliza" +ctld.i18n["es"]["Drop Red Smoke"] = "Lanzar humo rojo" +ctld.i18n["es"]["Drop Blue Smoke"] = "Lanzar humo azul" +ctld.i18n["es"]["Drop Orange Smoke"] = "Lanzar humo naranja" +ctld.i18n["es"]["Drop Green Smoke"] = "Lanzar humo verde" +ctld.i18n["es"]["Drop Beacon"] = "Desplegar baliza" ctld.i18n["es"]["Radio Beacons"] = "Balizas de radio" -ctld.i18n["es"]["Remove Closest Beacon"] = "Quitar baliza cercana" +ctld.i18n["es"]["Remove Closest Beacon"] = "Quitar la baliza mas cercana" ctld.i18n["es"]["JTAC Status"] = "Estado de JTAC" ctld.i18n["es"]["DISABLE "] = "DESHABILITAR " ctld.i18n["es"]["ENABLE "] = "HABILITAR " ctld.i18n["es"]["REQUEST "] = "SOLICITUD " -ctld.i18n["es"]["Reset TGT Selection"] = "Restablecer selección TGT" +ctld.i18n["es"]["Reset TGT Selection"] = "Restablecer selección de objetivo" -- F10 RECON menus ctld.i18n["es"]["RECON"] = "RECONOCIMIENTO" ctld.i18n["es"]["Show targets in LOS (refresh)"] = "Marcar objetivos visibles en el mapa F10" From af3e4fab1772a3bccf33beb489934e4ceaa60065 Mon Sep 17 00:00:00 2001 From: FullGas1 <51051389+FullGas1@users.noreply.github.com> Date: Tue, 20 May 2025 17:46:02 +0200 Subject: [PATCH 166/166] commit --- CTLD.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index 0bc5b45..184340a 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -890,6 +890,7 @@ ctld.internalCargoLimits = { -- Remove the -- below to turn on options ["Mi-8MT"] = 2, ["CH-47Fbl1"] = 8, + ["UH-1H"] = 3, -- to remove after debug } @@ -1179,8 +1180,8 @@ ctld.spawnableCrates = { ctld.spawnableCratesModels = { ["load"] = { - ["category"] = "Fortifications", - ["type"] = "Cargo04", + ["category"] = "Cargos", --"Fortifications" + ["type"] = "ammo_cargo", --"uh1h_cargo" --"Cargo04" ["canCargo"] = false, }, ["sling"] = {