From 2f78b0288847588bab73ced6d1223ef5e9d4ab08 Mon Sep 17 00:00:00 2001 From: Ciaran Fisher Date: Sat, 9 May 2015 16:26:29 +0100 Subject: [PATCH] Feature complete Added ability to spawn groups at triggers AI will load and auto unload troops Added ability to rearm HAWK launcher --- CTLD.lua | 565 ++++++++++++++++++++++++++++++++++++++--------- README.md | 10 + test-mission.miz | Bin 54994 -> 58515 bytes 3 files changed, 469 insertions(+), 106 deletions(-) diff --git a/CTLD.lua b/CTLD.lua index d694895..9f6ee87 100644 --- a/CTLD.lua +++ b/CTLD.lua @@ -13,23 +13,12 @@ Supports usual CTTS functions such as spawn group Huey max carry weight = 4000lb / 1814.37 kg - Mi-8 Max carry weight = 6614 / 3000 kg - C-130 Max Carry Weight - 26,634 + Mi-8 Max carry weight = 6614lb / 3000 kg + C-130 Max Carry Weight - 26,634 lb - Two HMMWV (ATGM + MG ) and 10 troops - It can actually hold 92 ground troops, 64 fully equipped paratroopers, or 74 litter patients - Source http://fas.org/man/dod-101/sys/ac/c-130.htm - -- Possible ideas for huey + mi8 - -- Make it so the huey cant carry all the bits needed, maybe need two trips? - - - -- use events to catch cargo destroyed events - - How to get crate damage? If damaged by a certain amount then destroy the jeep? - - -- Make ALL troop groups extractable? - -- iterate over nearby groups, - -- Destroy the units and add to new group? - worth the effort? - ]] ctld = {} @@ -39,6 +28,20 @@ ctld = {} -- ************************************************************************ ctld.disableAllSmoke = false -- if true, all smoke is diabled regardless of settings below. Leave false to respect settings below +ctld.enableCrates = true -- if false, Helis will not be able to spawn or unpack crates so will be normal CTTS +ctld.enableSmokeDrop = true -- if false, helis and c-130 will not be able to drop smoke + +ctld.maxExtractDistance = 50 -- 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.maximumSearchDistance = 4000 -- max distance for troops to search for enemy +ctld.maximumMoveDistance = 1000 -- max distance for troops to move from drop point if no enemy is nearby + +ctld.numberOfTroops = 10 -- default number of troops to load on a transport heli or C-130 + +ctld.vehiclesForTransport = { "M1045 HMMWV TOW", "M1043 HMMWV Armament" } -- vehicles to load onto c130 or hercules + +ctld.spawnRPGWithCoalition = true --spawns a friendly RPG unit with Coalition forces + -- ***************** Pickup and dropoff zones ***************** @@ -59,6 +62,19 @@ ctld.pickupZones = { { "pickzone10", "none" }, } +ctld.dropOffZones = { + { "dropzone1", "red" }, + { "dropzone2", "blue" }, + { "dropzone3", "none" }, + { "dropzone4", "none" }, + { "dropzone5", "none" }, + { "dropzone6", "none" }, + { "dropzone7", "none" }, + { "dropzone8", "none" }, + { "dropzone9", "none" }, + { "dropzone10", "none" }, +} + -- ******************** Transports names ********************** -- Use any of the predefined names or set your own ones @@ -91,6 +107,39 @@ ctld.transportPilotNames = { "helicargo23", "helicargo24", "helicargo25", + + -- *** 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", + + "transport11", + "transport12", + "transport13", + "transport14", + "transport15", + "transport16", + "transport17", + "transport18", + "transport19", + "transport20", + + "transport21", + "transport22", + "transport23", + "transport24", + "transport25", + } -- *************** Optional Extractable GROUPS ***************** @@ -127,7 +176,7 @@ ctld.extractableGroups = { "extract25", } --- ************** Optional Logistics CRATE UNITS ****************** +-- ************** Logistics UNITS FOR CRATE SPAWNING ****************** -- Use any of the predefined names or set your own ones @@ -144,17 +193,12 @@ ctld.logisticUnits = { "logistic10", } --- ****************** GENERAL SCRIPT CONFIG ******************** +-- ************** UNITS ABLE TO TRANSPORT VEHICLES ****************** -ctld.maxExtractDistance = 50 -- 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.maximumSearchDistance = 4000 -- max distance for troops to search for enemy -ctld.maximumMoveDistance = 1000 -- max distance for troops to move from drop point if no enemy is nearby +ctld.vehicleTransportEnabled = { + "C-130", +} -ctld.numberOfTroops = 10 -- default number of troops to load on a transport -ctld.vehiclesForTransport = { "M1045 HMMWV TOW", "M1043 HMMWV Armament" } -- vehicles to load onto c130 or hercules - -ctld.spawnRPGWithCoalition = true --spawns a friendly RPG unit with Coalition forces -- *************************************************************** -- **************** BE CAREFUL BELOW HERE ************************ @@ -174,6 +218,8 @@ ctld.droppedVehiclesBLUE = {} -- stores vehicle groups for c-130 / hercules ctld.inTransitTroops = {} +ctld.completeHawkSystems = {} -- stores complete spawned groups from multiple crates + -- Weights must be unique as we use the weight to change the cargo to the correct unit -- when we unpack ctld.spawnableCrates = { @@ -238,7 +284,84 @@ for _, _groupName in pairs(ctld.extractableGroups) do end ----------------- FUNCTIONS ---------------- +------------ EXTERNAL FUNCTIONS FOR MISSION EDITOR ----------- + + + +----------------------------------------------------------------- +-- Spawn group at a trigger and sets them as extractable. Usage: +-- ctld.spawnGroupAtTrigger("groupside", number, "triggerName", radius) +-- Variables: +-- "groupSide" = "red" for Russia "blue" for USA +-- _number = number of groups to spawn +-- "triggerName" = trigger name in mission editor between commas +-- _searchRadius = random distance for units to move from spawn zone (0 will leave troops at the spawn position - no search for enemy) +-- +-- Example: ctld.spawnGroupAtTrigger("red", 2, "spawn1", 1000) +-- +-- This example will spawn 2 groups of russians at trigger "spawn1" +-- and they will search for enemy or move randomly withing 1000m +function ctld.spawnGroupAtTrigger(_groupSide, _number, _triggerName, _searchRadius) + local _spawnTrigger = trigger.misc.getZone(_triggerName) -- trigger to use as reference position + + if _spawnTrigger == nil then + trigger.action.outText("CTLD.lua ERROR: Cant find trigger called " .. _triggerName, 10) + return + end + + if _groupSide == "red" then + _groupSide = 1 + else + _groupSide = 2 + end + + if _number < 1 then + _number = 1 + 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 _types = ctld.generateTroopTypes(_groupSide,_number) + + local _droppedTroops = ctld.spawnDroppedGroup(_groupSide,_pos3, _types, false,_searchRadius); + + 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) + + 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 + + + + +---------------- INTERNAL FUNCTIONS ---------------- -- Remove intransit troops when heli / cargo plane dies ctld.eventHandler = {} @@ -350,10 +473,6 @@ function ctld.spawnCrate(_args) -- _spawnedCrate = coalition.addStaticObject(_side, _spawnedCrate) ctld.spawnedCratesBLUE[_name] = _crateType end --- for key, value in pairs(getmetatable(_spawnedCrate)) do --- env.info(tostring(key)) --- env.info(tostring(value)) --- end ctld.displayMessageToGroup(_heli, string.format("A %s crate weighing %s kg has been brought out and is at your 12 o'clock ", _crateType.desc, _crateType.weight), 20) @@ -390,6 +509,18 @@ function ctld.troopsOnboard(_heli,_troops) 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 +end + + function ctld.deployTroops(_heli,_troops) local _onboard = ctld.inTransitTroops[_heli:getName()] @@ -399,7 +530,7 @@ function ctld.deployTroops(_heli,_troops) if _onboard.troops ~= nil and #_onboard.troops > 0 then - local _droppedTroops = ctld.spawnDroppedGroup(_heli, _onboard.troops, false) + local _droppedTroops = ctld.spawnDroppedGroup(_heli:getCoalition(),_heli:getPoint(), _onboard.troops, false) if _heli:getCoalition() == 1 then @@ -410,13 +541,13 @@ function ctld.deployTroops(_heli,_troops) end ctld.inTransitTroops[_heli:getName()].troops = {} - trigger.action.outTextForCoalition(_heli:getCoalition(), _heli:getPlayerName() .. " dropped troops from " .. _heli:getTypeName() .. " into combat", 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.getPlayerNameOrType(_heli) .. " dropped troops from " .. _heli:getTypeName() .. " into combat", 10) end else if _onboard.vehicles ~= nil and #_onboard.vehicles > 0 then - local _droppedVehicles = ctld.spawnDroppedGroup(_heli, _onboard.vehicles, true) + local _droppedVehicles = ctld.spawnDroppedGroup(_heli:getCoalition(),_heli:getPoint(), _onboard.vehicles, true) if _heli:getCoalition() == 1 then @@ -428,51 +559,69 @@ function ctld.deployTroops(_heli,_troops) ctld.inTransitTroops[_heli:getName()].vehicles = {} - trigger.action.outTextForCoalition(_heli:getCoalition(), _heli:getPlayerName() .. " dropped vehicles from " .. _heli:getTypeName() .. " into combat", 10) + trigger.action.outTextForCoalition(_heli:getCoalition(),ctld.getPlayerNameOrType(_heli) .. " dropped vehicles from " .. _heli:getTypeName() .. " into combat", 10) end end end -function ctld.loadTroops(_heli,_troops) + + +function ctld.generateTroopTypes(_side,_count) + + local _troops = {} + + for _i = 1,_count do + + local _unitType = "Soldier AK" + + if _side == 2 then + _unitType = "Soldier M4" + if _i <= 4 and ctld.spawnRPGWithCoalition then + _unitType = "Paratrooper RPG-16" + end + if _i <= 2 then + _unitType = "Soldier M249" + end + else + _unitType = "Infantry AK" + if _i <= 4 then + _unitType = "Paratrooper RPG-16" + end + if _i <= 2 then + _unitType = "Paratrooper AKS-74" + end + end + + _troops[_i] = _unitType + end + + return _troops +end + +-- load troops onto vehicle +function ctld.loadTroops(_heli,_troops, _number) -- 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 _number == nil then + _number = ctld.numberOfTroops + end + if _onboard == nil then _onboard = { troops = {}, vehicles = {} } end if _troops then - for _i = 1, ctld.numberOfTroops do - local _unitType = "Soldier AK" + _onboard.troops = ctld.generateTroopTypes(_heli:getCoalition(),_number) - if _heli:getCoalition() == 2 then - _unitType = "Soldier M4" - if _i <= 4 and ctld.spawnRPGWithCoalition then - _unitType = "Paratrooper RPG-16" - end - if _i <= 2 then - _unitType = "Soldier M249" - end - else - _unitType = "Infantry AK" - if _i <= 4 then - _unitType = "Paratrooper RPG-16" - end - if _i <= 2 then - _unitType = "Paratrooper AKS-74" - end - end - - _onboard.troops[_i] = _unitType - end - - trigger.action.outTextForCoalition(_heli:getCoalition(), _heli:getPlayerName() .. " loaded "..ctld.numberOfTroops.." troops into " .. _heli:getTypeName(), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.getPlayerNameOrType(_heli) .. " loaded ".._number.." troops into " .. _heli:getTypeName(), 10) else @@ -482,7 +631,7 @@ function ctld.loadTroops(_heli,_troops) local _count = #ctld.vehiclesForTransport - trigger.action.outTextForCoalition(_heli:getCoalition(), _heli:getPlayerName() .. " loaded ".._count.." vehicles into " .. _heli:getTypeName(), 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.getPlayerNameOrType(_heli) .. " loaded ".._count.." vehicles into " .. _heli:getTypeName(), 10) end @@ -551,7 +700,7 @@ function ctld.extractTroops(_heli,_troops) _onboard.troops = _extractTroops.types - trigger.action.outTextForCoalition(_heli:getCoalition(), _heli:getPlayerName() .. " extracted troops in " .. _heli:getTypeName() .. " from combat", 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.getPlayerNameOrType(_heli) .. " extracted troops in " .. _heli:getTypeName() .. " from combat", 10) if _heli:getCoalition() == 1 then ctld.droppedTroopsRED[_extractTroops.group:getName()] = nil @@ -593,7 +742,7 @@ function ctld.extractTroops(_heli,_troops) ctld.droppedVehiclesBLUE[_extractVehicles.group:getName()] = nil end - trigger.action.outTextForCoalition(_heli:getCoalition(), _heli:getPlayerName() .. " extracted vehicles in " .. _heli:getTypeName() .. " from combat", 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.getPlayerNameOrType(_heli) .. " extracted vehicles in " .. _heli:getTypeName() .. " from combat", 10) --remove _extractVehicles.group:destroy() @@ -708,7 +857,9 @@ function ctld.getCratesAndDistance(_heli) --get crate local _crate = StaticObject.getByName(_crateName) - if _crate ~= nil and _crate:getLife() > 0 and _crate:inAir() == false then + --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 (_crate:inAir() == false or (land.getHeight(_crate:getPoint()) < 200 and mist.vec.mag(_crate:getVelocity()) < 1.0 )) then local _dist = ctld.getDistance(_crate:getPoint(), _heli:getPoint()) @@ -740,6 +891,42 @@ function ctld.getClosestCrate(_heli, _crates) return _closetCrate end +function ctld.findNearestHawk(_heli) + + local _closestHawkGroup = nil + local _shortestDistance = -1 + local _distance = 0 + + for _, _groupName in pairs(ctld.completeHawkSystems) do + + local _hawkGroup = Group.getByName(_groupName) + + if _hawkGroup ~= nil and _hawkGroup:getCoalition() == _heli:getCoalition() then + + local _leader = _hawkGroup:getUnit(1) + + 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 + end + + end + end + + if _closestHawkGroup ~= nil then + return {group = _closestHawkGroup, dist = _distance} + end + return nil + + +end + + function ctld.unpackCrates(_args) -- trigger.action.outText("Unpack Crates".._args[1],10) @@ -753,7 +940,7 @@ function ctld.unpackCrates(_args) if _crate ~= nil and _crate.dist < 200 then - if ctld.inPickupZone(_heli) == true then + if ctld.inLogisticsZone(_heli) == true then ctld.displayMessageToGroup(_heli, "You can't unpack that here! Take it to where it's needed!", 20) @@ -764,12 +951,66 @@ function ctld.unpackCrates(_args) if string.match(_crate.details.desc, "HAWK") then -- multicrate + -- are we adding to existing hawk system? + if _crate.details.unit == "Hawk ln" then + + -- find nearest COMPLETE hawk system + local _nearestHawk = ctld.findNearestHawk(_heli) + + if _nearestHawk ~=nil and _nearestHawk.dist < 300 then + + if _heli:getCoalition() == 1 then + + ctld.spawnedCratesRED[_crate.crateUnit:getName()] = nil + else + + ctld.spawnedCratesBLUE[_crate.crateUnit:getName()] = nil + end + + local _types = {} + local _points = {} + + local _units = _nearestHawk.group:getUnits() + + if _units ~= nil and #_units > 0 then + + for x = 1, #_units do + if _units[x]:getLife() > 0 then + table.insert(_types,_units[x]:getTypeName()) + table.insert(_points,_units[x]:getPoint()) + end + end + end + + if #_types == 3 and #_points == 3 then + + -- rearm hawk + -- destroy old group + ctld.completeHawkSystems[_nearestHawk.group:getName()] = nil + + _nearestHawk.group:destroy() + + local _spawnedGroup = ctld.spawnCrateGroup(_heli, _points, _types) + + ctld.completeHawkSystems[_spawnedGroup:getName()] = _spawnedGroup:getName() + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.getPlayerNameOrType(_heli) .. " successfully rearmed a full HAWK AA System in the field", 10) + + return -- all done so quit + end + + + end + + end + + -- are there all the pieces close enough together local _hawkParts = { ["Hawk ln"] = false, ["Hawk tr"] = false, ["Hawk sr"] = false } for _, _nearbyCrate in pairs(_crates) do - if _nearbyCrate.dist < 200 then + if _nearbyCrate.dist < 300 then if _nearbyCrate.details.unit == "Hawk ln" or _nearbyCrate.details.unit == "Hawk sr" or _nearbyCrate.details.unit == "Hawk tr" then @@ -821,18 +1062,16 @@ function ctld.unpackCrates(_args) ctld.spawnedCratesBLUE[_hawkPart.crateUnit:getName()] = nil end - - -- local _spawnedCrate = ctld.spawnCrateStatic( _heli:getCoalition(),_hawkPart.crateUnit:getID(),{x=100,z=100},_name,100) - - --destroy - hawkPart.crateUnit:destroy() + _hawkPart.crateUnit:destroy() end -- HAWK READY! - ctld.spawnCrateGroup(_heli, _posArray, _typeArray) + local _spawnedGroup = ctld.spawnCrateGroup(_heli, _posArray, _typeArray) - trigger.action.outTextForCoalition(_heli:getCoalition(), _heli:getPlayerName() .. " successfully deployed a full HAWK AA System to the field", 10) + ctld.completeHawkSystems[_spawnedGroup:getName()] = _spawnedGroup:getName() + + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.getPlayerNameOrType(_heli) .. " successfully deployed a full HAWK AA System to the field", 10) end else @@ -845,7 +1084,7 @@ function ctld.unpackCrates(_args) --remove crate _crate.crateUnit:destroy() - ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }) + ctld.spawnCrateGroup(_heli, { _cratePoint }, { _crate.details.unit }) if _heli:getCoalition() == 1 then @@ -855,7 +1094,7 @@ function ctld.unpackCrates(_args) ctld.spawnedCratesBLUE[_crateName] = nil end - trigger.action.outTextForCoalition(_heli:getCoalition(), _heli:getPlayerName() .. " successfully deployed " .. _crate.details.desc .. " to the field", 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.getPlayerNameOrType(_heli) .. " successfully deployed " .. _crate.details.desc .. " to the field", 10) end else @@ -905,19 +1144,19 @@ function ctld.spawnCrateGroup(_heli, _positions, _types) _dest = { x = _dest.x + 5, _y = _dest.y + 5, z = _dest.z + 5 } ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _dest) + + return _spawnedGroup end -- spawn normal group -function ctld.spawnDroppedGroup(_heli, _types, _spawnBehind) +function ctld.spawnDroppedGroup(_side,_point, _types, _spawnBehind,_maxSearch) local _id = mist.getNextGroupId() local _groupName = "Dropped Group #" .. _id - local _side = _heli:getCoalition() - local _group = { ["visible"] = false, ["groupId"] = _id, @@ -934,7 +1173,7 @@ function ctld.spawnDroppedGroup(_heli, _types, _spawnBehind) -- spawn in circle around heli - local _pos = _heli:getPoint() + local _pos = _point for _i, _type in ipairs(_types) do @@ -947,7 +1186,7 @@ function ctld.spawnDroppedGroup(_heli, _types, _spawnBehind) else - local _pos = _heli:getPoint() + local _pos = _point --try to spawn at 6 oclock to us local _angle = math.atan2(_pos.z, _pos.x) @@ -964,24 +1203,28 @@ function ctld.spawnDroppedGroup(_heli, _types, _spawnBehind) -- find nearest enemy and head there - local _enemyPos = ctld.findNearestEnemy(_heli) + if _maxSearch == nil then + _maxSearch = ctld.maximumSearchDistance + end + + local _enemyPos = ctld.findNearestEnemy(_side,_point,_maxSearch) ctld.orderGroupToMoveToPoint(_spawnedGroup:getUnit(1), _enemyPos) return _spawnedGroup end -function ctld.findNearestEnemy(_heli) +function ctld.findNearestEnemy(_side,_point,_searchDistance) local _closestEnemy = nil local _groups - local _closestEnemyDist = ctld.maximumSearchDistance + local _closestEnemyDist = _searchDistance - local _heliPoint = _heli:getPoint() + local _heliPoint = _point - if _heli:getCoalition() == 2 then + if _side == 2 then _groups = coalition.getGroups(1, Group.Category.GROUND) else _groups = coalition.getGroups(2, Group.Category.GROUND) @@ -1150,6 +1393,10 @@ end -- are we in pickup zone function ctld.inPickupZone(_heli) + if _heli:inAir() then + return false + end + local _heliPoint = _heli:getPoint() for _, _zoneDetails in pairs(ctld.pickupZones) do @@ -1171,9 +1418,41 @@ function ctld.inPickupZone(_heli) return false end +-- are we in a dropoff zone +function ctld.inDropoffZone(_heli) + + if _heli:inAir() 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 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 near friendly logistics zone function ctld.inLogisticsZone(_heli) + if _heli:inAir() then + return false + end + local _heliPoint = _heli:getPoint() for _, _name in pairs(ctld.logisticUnits) do @@ -1251,10 +1530,66 @@ function ctld.dropSmoke(_args) trigger.action.smoke(_pos3, _args[2]) - trigger.action.outTextForCoalition(_heli:getCoalition(), _heli:getPlayerName() .. " dropped " .._colour .." smoke ", 10) + trigger.action.outTextForCoalition(_heli:getCoalition(), ctld.getPlayerNameOrType(_heli) .. " dropped " .._colour .." smoke ", 10) end end +function ctld.unitCanCarryVehicles(_unit) + + local _type = _unit:getTypeName() + + for _,_name in pairs(ctld.vehicleTransportEnabled) do + + if string.match(_type, _name) then + return true + end + end + + return false + +end + + +-- checks the status of all AI troop carriers and auto loads and unloads troops +function ctld.checkAIStatus() + + timer.scheduleFunction(ctld.checkAIStatus,nil,timer.getTime()+5) + + for _, _unitName in pairs(ctld.transportPilotNames) do + + local _unit = ctld.getTransportUnit(_unitName) + + if _unit ~= nil and _unit:getPlayerName() == nil then + + -- no player name means AI! + + if ctld.inPickupZone(_unit) and not ctld.troopsOnboard(_unit,true) then + + ctld.loadTroops(_unit,true) + + elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit,true) then + + ctld.deployTroops(_unit,true) + end + + if ctld.unitCanCarryVehicles(_unit) then + + if ctld.inPickupZone(_unit) and not ctld.troopsOnboard(_unit,false) then + + ctld.loadTroops(_unit,false) + + elseif ctld.inDropoffZone(_unit) and ctld.troopsOnboard(_unit,false) then + + ctld.deployTroops(_unit,false) + end + + end + + end + end + +end + -- Adds menuitem to all heli units that are active function addTransportMenuItem() @@ -1268,47 +1603,59 @@ function addTransportMenuItem() if _unit ~= nil then - if ctld.addedTo[_unitName] == nil then + local _groupId = _unit:getGroup():getID() - local _groupId = _unit:getGroup():getID() + if ctld.addedTo[_groupId] == nil and _unit:getPlayerName() ~= nil then missionCommands.addSubMenuForGroup(_groupId, "Troop Transport") missionCommands.addCommandForGroup(_groupId, "Load / Unload Troops", { "Troop Transport" }, ctld.loadUnloadTroops, { _unitName,true }) - - if string.match(_unit:getTypeName(), "C-130") then + if ctld.unitCanCarryVehicles(_unit) then missionCommands.addCommandForGroup(_groupId, "Load / Unload Vehicles", { "Troop Transport" }, ctld.loadUnloadTroops, { _unitName,false }) end missionCommands.addCommandForGroup(_groupId, "Check Status", { "Troop Transport" }, ctld.checkTroopStatus, { _unitName }) - if not string.match(_unit:getTypeName(), "C-130") then + if ctld.enableCrates then - missionCommands.addSubMenuForGroup(_groupId, "Ground Forces") - missionCommands.addCommandForGroup(_groupId, "HMMWV - TOW", { "Ground Forces" }, ctld.spawnCrate, { _unitName, "M1045 HMMWV TOW" }) - missionCommands.addCommandForGroup(_groupId, "HMMWV - MG", { "Ground Forces" }, ctld.spawnCrate, { _unitName, "M1043 HMMWV Armament" }) + if ctld.unitCanCarryVehicles(_unit) == false then - missionCommands.addCommandForGroup(_groupId, "2B11 Mortar", { "Ground Forces" }, ctld.spawnCrate, { _unitName, "2B11 mortar" }) + missionCommands.addSubMenuForGroup(_groupId, "Ground Forces") + missionCommands.addCommandForGroup(_groupId, "HMMWV - TOW", { "Ground Forces" }, ctld.spawnCrate, { _unitName, "M1045 HMMWV TOW" }) + missionCommands.addCommandForGroup(_groupId, "HMMWV - MG", { "Ground Forces" }, ctld.spawnCrate, { _unitName, "M1043 HMMWV Armament" }) - missionCommands.addSubMenuForGroup(_groupId, "AA Crates") - missionCommands.addCommandForGroup(_groupId, "MANPAD", { "AA Crates" }, ctld.spawnCrate, { _unitName, "Stinger manpad" }) + missionCommands.addCommandForGroup(_groupId, "2B11 Mortar", { "Ground Forces" }, ctld.spawnCrate, { _unitName, "2B11 mortar" }) - missionCommands.addCommandForGroup(_groupId, "HAWK Launcher", { "AA Crates" }, ctld.spawnCrate, { _unitName, "Hawk ln" }) - missionCommands.addCommandForGroup(_groupId, "HAWK Search Radar", { "AA Crates" }, ctld.spawnCrate, { _unitName, "Hawk sr" }) - missionCommands.addCommandForGroup(_groupId, "HAWK Track Radar", { "AA Crates" }, ctld.spawnCrate, { _unitName, "Hawk tr" }) + missionCommands.addSubMenuForGroup(_groupId, "AA Crates") + missionCommands.addCommandForGroup(_groupId, "MANPAD", { "AA Crates" }, ctld.spawnCrate, { _unitName, "Stinger manpad" }) - end + missionCommands.addCommandForGroup(_groupId, "HAWK Launcher", { "AA Crates" }, ctld.spawnCrate, { _unitName, "Hawk ln" }) + missionCommands.addCommandForGroup(_groupId, "HAWK Search Radar", { "AA Crates" }, ctld.spawnCrate, { _unitName, "Hawk sr" }) + missionCommands.addCommandForGroup(_groupId, "HAWK Track Radar", { "AA Crates" }, ctld.spawnCrate, { _unitName, "Hawk tr" }) - missionCommands.addSubMenuForGroup(_groupId, "Crate Commands") - missionCommands.addCommandForGroup(_groupId, "List Nearby Crates", { "Crate Commands" }, ctld.listNearbyCrates, { _unitName }) - missionCommands.addCommandForGroup(_groupId, "Unpack Crate", { "Crate Commands" }, ctld.unpackCrates, { _unitName }) - missionCommands.addCommandForGroup(_groupId, "Drop Red Smoke", { "Crate Commands" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Red }) - missionCommands.addCommandForGroup(_groupId, "Drop Blue Smoke", { "Crate Commands" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Blue }) - -- missionCommands.addCommandForGroup(_groupId, "Drop Orange Smoke", { "Crate Commands" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Orange }) - missionCommands.addCommandForGroup(_groupId, "Drop Green Smoke", { "Crate Commands" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Green }) + end + missionCommands.addSubMenuForGroup(_groupId, "Crate Commands") + missionCommands.addCommandForGroup(_groupId, "List Nearby Crates", { "Crate Commands" }, ctld.listNearbyCrates, { _unitName }) + missionCommands.addCommandForGroup(_groupId, "Unpack Crate", { "Crate Commands" }, ctld.unpackCrates, { _unitName }) - ctld.addedTo[_unitName] = true + if ctld.enableSmokeDrop then + missionCommands.addCommandForGroup(_groupId, "Drop Red Smoke", { "Crate Commands" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Red }) + missionCommands.addCommandForGroup(_groupId, "Drop Blue Smoke", { "Crate Commands" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Blue }) + -- missionCommands.addCommandForGroup(_groupId, "Drop Orange Smoke", { "Crate Commands" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Orange }) + missionCommands.addCommandForGroup(_groupId, "Drop Green Smoke", { "Crate Commands" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Green }) + end + else + if ctld.enableSmokeDrop then + missionCommands.addSubMenuForGroup(_groupId, "Smoke Markers") + missionCommands.addCommandForGroup(_groupId, "Drop Red Smoke", { "Smoke Markers" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Red }) + missionCommands.addCommandForGroup(_groupId, "Drop Blue Smoke", { "Smoke Markers" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Blue }) + missionCommands.addCommandForGroup(_groupId, "Drop Orange Smoke", { "Smoke Markers"}, ctld.dropSmoke, { _unitName, trigger.smokeColor.Orange }) + missionCommands.addCommandForGroup(_groupId, "Drop Green Smoke", { "Smoke Markers" }, ctld.dropSmoke, { _unitName, trigger.smokeColor.Green }) + end + end + + ctld.addedTo[_groupId] = true end else -- env.info(string.format("unit nil %s",_unitName)) @@ -1339,9 +1686,15 @@ end timer.scheduleFunction(ctld.refreshSmoke, nil, timer.getTime() + 5) timer.scheduleFunction(addTransportMenuItem, nil, timer.getTime() + 5) +timer.scheduleFunction(ctld.checkAIStatus,nil,timer.getTime() + 5) --event handler for deaths world.addEventHandler(ctld.eventHandler) env.info("CTLD event handler added") +--DEBUG FUNCTION +-- for key, value in pairs(getmetatable(_spawnedCrate)) do +-- env.info(tostring(key)) +-- env.info(tostring(value)) +-- end diff --git a/README.md b/README.md index 1c0169e..cc3d7a6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,12 @@ # DCS-CTLD Complete Troops and Logistics Deployment for DCS World + +The goal is to replace the current CTTS script by extending its functionality to support: + +Cargo Sling-loading and unpacking +Large transport aircraft and Vehicle delivery + + + + + diff --git a/test-mission.miz b/test-mission.miz index 469f704e218a94f310a3e541baf391df423645f1..fb4172dffa2846b926fbc76943c3152790c4ee3b 100644 GIT binary patch delta 19546 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 NL{Jcr>cxLw{|j_i{-OW?