Permalink
Corrrected Pick up time bug Added Function csar.spawnCsarAtZone( _zone, _coalition, _description, _randomPoint)
2156 lines (1564 sloc)
67 KB
| -- CSAR Script for DCS Ciribob - 2015 | |
| -- Version 1.9.2 - 23/04/2018 | |
| -- DCS 1.5 Compatible - Needs Mist 4.0.55 or higher! | |
| -- | |
| -- 4 Options: | |
| -- 0 - No Limit - NO Aircraft disabling or pilot lives | |
| -- 1 - Disable Aircraft when its down - Timeout to reenable aircraft | |
| -- 2 - Disable Aircraft for Pilot when he's shot down -- timeout to reenable pilot for aircraft | |
| -- 3 - Pilot Life Limit - No Aircraft Disabling | |
| csar = {} | |
| -- SETTINGS FOR MISSION DESIGNER vvvvvvvvvvvvvvvvvv | |
| --Enable CSar Options -HELICOPTERS | |
| --enableAllslots and Use prefix will work for Helicopter | |
| csar.enableAllslots = false -- Doesn't require to set the Unit name check Aircraft Type and Limit below | |
| -- All slot / Limit settings | |
| csar.aircraftType = {} -- Type and limit | |
| csar.aircraftType["SA342Mistral"] = 2 | |
| csar.aircraftType["SA342Minigun"] = 2 | |
| csar.aircraftType["SA342L"] = 2 | |
| csar.aircraftType["SA342M"] = 2 | |
| csar.aircraftType["UH-1H"] = 8 | |
| csar.aircraftType["Mi-8MT"] = 16 | |
| -- Prefix Settings - Only For helicopters | |
| csar.useprefix = true -- Use the Prefixed defined below, Requires Unit have the Prefix defined below | |
| csar.csarPrefix = { "helicargo", "MEDEVAC"} | |
| -- Fixed Unit Name. | |
| --Enable Csar Options for the units with the names in the list below | |
| csar.csarFixedUnits = { | |
| "helicargo1", | |
| "helicargo2", | |
| "helicargo3", | |
| "helicargo4", | |
| "helicargo5", | |
| "helicargo6", | |
| "helicargo7", | |
| "helicargo8", | |
| "helicargo9", | |
| "helicargo10", | |
| "helicargo11", | |
| "helicargo12", | |
| "helicargo13", | |
| "helicargo14", | |
| "helicargo15", | |
| "helicargo16", | |
| "helicargo17", | |
| "helicargo18", | |
| "helicargo19", | |
| "helicargo20", | |
| "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 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", | |
| } -- List of all the MEDEVAC _UNIT NAMES_ (the line where it says "Pilot" in the ME)! | |
| csar.autosmoke = false -- Automatically Smoke when CSAR helicopter is at 5 km | |
| csar.bluemash = { | |
| "BlueMASH #1", | |
| "BlueMASH #2", | |
| "BlueMASH #3", | |
| "BlueMASH #4", | |
| "BlueMASH #5", | |
| "BlueMASH #6", | |
| "BlueMASH #7", | |
| "BlueMASH #8", | |
| "BlueMASH #9", | |
| "BlueMASH #10" | |
| } -- The unit that serves as MASH for the blue side | |
| csar.redmash = { | |
| "RedMASH #1", | |
| "RedMASH #2", | |
| "RedMASH #3", | |
| "RedMASH #4", | |
| "RedMASH #5", | |
| "RedMASH #6", | |
| "RedMASH #7", | |
| "RedMASH #8", | |
| "RedMASH #9", | |
| "RedMASH #10" | |
| } -- The unit that serves as MASH for the red side | |
| csar.csarMode = 0 | |
| -- 0 - No Limit - NO Aircraft disabling | |
| -- 1 - Disable Aircraft when its down - Timeout to reenable aircraft | |
| -- 2 - Disable Aircraft for Pilot when he's shot down -- timeout to reenable pilot for aircraft | |
| -- 3 - Pilot Life Limit - No Aircraft Disabling -- timeout to reset lives? | |
| csar.maxLives = 8 -- Maximum pilot lives | |
| csar.countCSARCrash = false -- If you set to true, pilot lives count for CSAR and CSAR aircraft will count. | |
| csar.csarOncrash = true -- If set to true, will generate a csar when crash as well. | |
| csar.allowDownedPilotCAcontrol = true -- Set to false if you don't want to allow control by Combined arms | |
| csar.reenableIfCSARCrashes = true -- If a CSAR heli crashes, the pilots are counted as rescued anyway. Set to false to Stop this | |
| -- - I recommend you leave the option on below IF USING MODE 1 otherwise the | |
| -- aircraft will be disabled for the duration of the mission | |
| csar.disableAircraftTimeout = true -- Allow aircraft to be used after 20 minutes if the pilot isnt rescued | |
| csar.disableTimeoutTime = 20 -- Time in minutes for TIMEOUT | |
| csar.destructionHeight = 150 -- height in meters an aircraft will be destroyed at if the aircraft is disabled | |
| csar.enableForAI = true -- set to false to disable AI units from being rescued. | |
| csar.enableForRED = true -- enable for red side | |
| csar.enableForBLUE = true -- enable for blue side | |
| csar.enableSlotBlocking = true -- if set to true, you need to put the csarSlotBlockGameGUI.lua | |
| -- in C:/Users/<YOUR USERNAME>/DCS/Scripts for 1.5 or C:/Users/<YOUR USERNAME>/DCS.openalpha/Scripts for 2.0 | |
| -- For missions using FLAGS and this script, the CSAR flags will NOT interfere with your mission :) | |
| csar.bluesmokecolor = 4 -- Color of smokemarker for blue side, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue | |
| csar.redsmokecolor = 1 -- Color of smokemarker for red side, 0 is green, 1 is red, 2 is white, 3 is orange and 4 is blue | |
| csar.requestdelay = 2 -- Time in seconds before the survivors will request Medevac | |
| csar.coordtype = 3 -- Use Lat/Long DDM (0), Lat/Long DMS (1), MGRS (2), Bullseye imperial (3) or Bullseye metric (4) for coordinates. | |
| csar.coordaccuracy = 1 -- Precision of the reported coordinates, see MIST-docs at http://wiki.hoggit.us/view/GetMGRSString | |
| -- only applies to _non_ bullseye coords | |
| csar.immortalcrew = true -- Set to true to make wounded crew immortal | |
| csar.invisiblecrew = true -- Set to true to make wounded crew insvisible | |
| csar.messageTime = 30 -- Time to show the intial wounded message for in seconds | |
| csar.weight = 100 | |
| csar.pilotRuntoExtractPoint = true -- Downed Pilot will run to the rescue helicopter up to csar.extractDistance METERS | |
| csar.loadDistance = 60 -- configure distance for pilot to get in helicopter in meters. | |
| csar.extractDistance = 500 -- Distance the Downed pilot will run to the rescue helicopter | |
| csar.loadtimemax = 135 | |
| csar.radioSound = "beacon.ogg" -- the name of the sound file to use for the Pilot radio beacons. If this isnt added to the mission BEACONS WONT WORK! | |
| csar.allowFARPRescue = true --allows pilot to be rescued by landing at a FARP or Airbase | |
| csar.landedStatus = {} -- tracks status of a helis hover above a downed pilot | |
| csar.csarUnits = {} | |
| -- SETTINGS FOR MISSION DESIGNER ^^^^^^^^^^^^^^^^^^^* | |
| -- *************************************************************** | |
| -- **************** Mission Editor Functions ********************* | |
| -- *************************************************************** | |
| ----------------------------------------------------------------- | |
| -- Resets all life limits so everyone can spawn again. Usage: | |
| -- csar.resetAllPilotLives() | |
| -- | |
| function csar.resetAllPilotLives() | |
| for x, _pilot in pairs(csar.pilotLives) do | |
| trigger.action.setUserFlag("CSAR_PILOT" .. _pilot:gsub('%W', ''), csar.maxLives + 1) | |
| end | |
| csar.pilotLives = {} | |
| env.info("Pilot Lives Reset!") | |
| end | |
| ----------------------------------------------------------------- | |
| -- Resets all life limits so everyone can spawn again. Usage: | |
| -- csar.resetAllPilotLives() | |
| -- | |
| function csar.resetPilotLife(_playerName) | |
| csar.pilotLives[_playerName] = nil | |
| trigger.action.setUserFlag("CSAR_PILOT" .. _playerName:gsub('%W', ''), csar.maxLives + 1) | |
| env.info("Pilot life Reset!") | |
| end | |
| -- *************************************************************** | |
| -- **************** BE CAREFUL BELOW HERE ************************ | |
| -- *************************************************************** | |
| -- Sanity checks of mission designer | |
| assert(mist ~= nil, "\n\n** HEY MISSION-DESIGNER! **\n\nMiST has not been loaded!\n\nMake sure MiST 4.0.57 or higher is running\n*before* running this script!\n") | |
| csar.addedTo = {} | |
| csar.downedPilotCounterRed = 0 | |
| csar.downedPilotCounterBlue = 0 | |
| csar.woundedGroups = {} -- contains the new group of units | |
| csar.inTransitGroups = {} -- contain a table for each SAR with all units he has with the | |
| -- original name of the killed group | |
| csar.radioBeacons = {} | |
| csar.smokeMarkers = {} -- tracks smoke markers for groups | |
| csar.heliVisibleMessage = {} -- tracks if the first message has been sent of the heli being visible | |
| csar.heliCloseMessage = {} -- tracks heli close message ie heli < 500m distance | |
| csar.radioBeacons = {} -- all current beacons | |
| csar.max_units = 6 --number of pilots that can be carried | |
| csar.currentlyDisabled = {} --stored disabled aircraft | |
| csar.hoverStatus = {} -- tracks status of a helis hover above a downed pilot | |
| csar.pilotDisabled = {} -- tracks what aircraft a pilot is disabled for | |
| csar.pilotLives = {} -- tracks how many lives a pilot has | |
| csar.takenOff = {} | |
| function csar.tableLength(T) | |
| if T == nil then | |
| return 0 | |
| end | |
| local count = 0 | |
| for _ in pairs(T) do count = count + 1 end | |
| return count | |
| end | |
| function csar.pilotsOnboard(_heliName) | |
| local count = 0 | |
| if csar.inTransitGroups[_heliName] then | |
| for _, _group in pairs(csar.inTransitGroups[_heliName]) do | |
| count = count + 1 | |
| end | |
| end | |
| return count | |
| end | |
| function csar.addCsar(_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description ) | |
| local _spawnedGroup = csar.spawnGroup( _coalition, _country, _point, _typeName ) | |
| csar.addSpecialParametersToGroup(_spawnedGroup) | |
| if noMessage ~= true then | |
| trigger.action.outTextForCoalition(_spawnedGroup:getCoalition(), "MAYDAY MAYDAY! " .. _typeName .. " is down. ", 10) | |
| end | |
| if _freq == nil then | |
| _freq = csar.generateADFFrequency() | |
| end | |
| if _freq ~= nil then | |
| csar.addBeaconToGroup(_spawnedGroup:getName(), _freq) | |
| end | |
| csar.handleEjectOrCrash(_playerName, false) | |
| -- Generate DESCRIPTION text | |
| local _text = " " | |
| if _playerName ~= nil then | |
| _text = "Pilot " .. _playerName .. " of " .. _unitName .. " - " .. _typeName | |
| elseif _typeName ~= nil then | |
| _text = "AI Pilot of " .. _unitName .. " - " .. _typeName | |
| else | |
| _text = _description | |
| end | |
| csar.woundedGroups[_spawnedGroup:getName()] = { side = _spawnedGroup:getCoalition(), originalUnit = _unitName, desc = _text, typename = _typeName, frequency = _freq, player = _playerName } | |
| csar.initSARForPilot(_spawnedGroup, _freq, noMessage) | |
| if _spawnedGroup ~= nil then | |
| local _controller = _spawnedGroup:getController(); | |
| Controller.setOption(_controller, AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.GREEN) | |
| Controller.setOption(_controller, AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD) | |
| end | |
| end | |
| function csar.spawnCsarAtZone( _zone, _coalition, _description, _randomPoint) | |
| local _country | |
| local freq = csar.generateADFFrequency() | |
| local _triggerZone = trigger.misc.getZone(_zone) -- trigger to use as reference position | |
| if _triggerZone == nil then | |
| trigger.action.outText("Csar.lua ERROR: Cant find zone called " .. _zone, 10) | |
| return | |
| end | |
| local pos | |
| if _randomPoint == true then | |
| pos = mist.getRandomPointInZone(_zone) | |
| pos.z = pos.y | |
| pos.y = 0 | |
| else | |
| pos = mist.utils.zoneToVec3(_zone) | |
| end | |
| if _coalition == coalition.side.BLUE then | |
| _country = country.id.USA | |
| else | |
| _country = country.id.RUSSIA | |
| end | |
| csar.addCsar(_coalition, _country, pos, nil, nil, nil, freq, true, _description) | |
| end | |
| -- Handles all world events | |
| csar.eventHandler = {} | |
| function csar.eventHandler:onEvent(_event) | |
| local status, err = pcall(function(_event) | |
| if _event == nil or _event.initiator == nil then | |
| return false | |
| elseif _event.id == 3 then -- taken offf | |
| if _event.initiator:getName() then | |
| csar.takenOff[_event.initiator:getName()] = true | |
| end | |
| return true | |
| elseif _event.id == 15 then --player entered unit | |
| if _event.initiator:getName() then | |
| csar.takenOff[_event.initiator:getName()] = nil | |
| end | |
| -- if its a sar heli, re-add check status script | |
| for _, _heliName in pairs(csar.csarUnits) do | |
| if _heliName == _event.initiator:getName() then | |
| -- add back the status script | |
| for _woundedName, _groupInfo in pairs(csar.woundedGroups) do | |
| if _groupInfo.side == _event.initiator:getCoalition() then | |
| --env.info(string.format("Schedule Respawn %s %s",_heliName,_woundedName)) | |
| -- queue up script | |
| -- Schedule timer to check when to pop smoke | |
| timer.scheduleFunction(csar.checkWoundedGroupStatus, { _heliName, _woundedName }, timer.getTime() + 5) | |
| end | |
| end | |
| end | |
| end | |
| if _event.initiator:getName() and _event.initiator:getPlayerName() then | |
| env.info("Checking Unit - " .. _event.initiator:getName()) | |
| csar.checkDisabledAircraftStatus({ _event.initiator:getName(), _event.initiator:getPlayerName() }) | |
| end | |
| return true | |
| elseif (_event.id == 9 and csar.csarOncrash == false) then | |
| -- Pilot dead | |
| env.info("Event unit - Pilot Dead") | |
| local _unit = _event.initiator | |
| if _unit == nil then | |
| return -- error! | |
| end | |
| local _coalition = _unit:getCoalition() | |
| if _coalition == 1 and not csar.enableForRED then | |
| return --ignore! | |
| end | |
| if _coalition == 2 and not csar.enableForBLUE then | |
| return --ignore! | |
| end | |
| -- Catch multiple events here? | |
| if csar.takenOff[_event.initiator:getName()] == true or _unit:inAir() then | |
| if csar.doubleEjection(_unit) then | |
| return | |
| end | |
| trigger.action.outTextForCoalition(_unit:getCoalition(), "MAYDAY MAYDAY! " .. _unit:getTypeName() .. " shot down. No Chute!", 10) | |
| csar.handleEjectOrCrash(_unit, true) | |
| else | |
| env.info("Pilot Hasnt taken off, ignore") | |
| end | |
| return | |
| elseif _event.id == 9 or world.event.S_EVENT_EJECTION == _event.id then | |
| if _event.id == 9 and csar.csarOncrash == false then | |
| return | |
| end | |
| env.info("Event unit - Pilot Ejected") | |
| local _unit = _event.initiator | |
| if _unit == nil then | |
| return -- error! | |
| end | |
| local _coalition = _unit:getCoalition() | |
| if _coalition == 1 and not csar.enableForRED then | |
| return --ignore! | |
| end | |
| if _coalition == 2 and not csar.enableForBLUE then | |
| return --ignore! | |
| end | |
| -- TODO catch ejection on runway? | |
| if csar.enableForAI == false and _unit:getPlayerName() == nil then | |
| return | |
| end | |
| if csar.takenOff[_event.initiator:getName()] ~= true and not _unit:inAir() then | |
| env.info("Pilot Hasnt taken off, ignore") | |
| return -- give up, pilot hasnt taken off | |
| end | |
| if csar.doubleEjection(_unit) then | |
| return | |
| end | |
| local _freq = csar.generateADFFrequency() | |
| csar.addCsar(_coalition, _unit:getCountry(), _unit:getPoint() , _unit:getTypeName(), _unit:getName(), _unit:getPlayerName(), _freq, false, 0) | |
| return true | |
| elseif world.event.S_EVENT_LAND == _event.id then | |
| if _event.initiator:getName() then | |
| csar.takenOff[_event.initiator:getName()] = nil | |
| end | |
| if csar.allowFARPRescue then | |
| --env.info("Landing") | |
| local _unit = _event.initiator | |
| if _unit == nil then | |
| env.info("Unit Nil on Landing") | |
| return -- error! | |
| end | |
| csar.takenOff[_event.initiator:getName()] = nil | |
| local _place = _event.place | |
| if _place == nil then | |
| env.info("Landing Place Nil") | |
| return -- error! | |
| end | |
| -- Coalition == 3 seems to be a bug... unless it means contested?! | |
| if _place:getCoalition() == _unit:getCoalition() or _place:getCoalition() == 0 or _place:getCoalition() == 3 then | |
| csar.rescuePilots(_unit) | |
| --env.info("Rescued") | |
| -- env.info("Rescued by Landing") | |
| else | |
| -- env.info("Cant Rescue ") | |
| env.info(string.format("airfield %d, unit %d", _place:getCoalition(), _unit:getCoalition())) | |
| end | |
| end | |
| return true | |
| end | |
| end, _event) | |
| if (not status) then | |
| env.error(string.format("Error while handling event %s", err), false) | |
| end | |
| end | |
| csar.lastCrash = {} | |
| function csar.doubleEjection(_unit) | |
| if csar.lastCrash[_unit:getName()] then | |
| local _time = csar.lastCrash[_unit:getName()] | |
| if timer.getTime() - _time < 10 then | |
| env.info("Caught double ejection!") | |
| return true | |
| end | |
| end | |
| csar.lastCrash[_unit:getName()] = timer.getTime() | |
| return false | |
| end | |
| function csar.handleEjectOrCrash(_unit, _crashed) | |
| -- disable aircraft for ALL pilots | |
| if csar.csarMode == 1 then | |
| if csar.currentlyDisabled[_unit:getName()] ~= nil then | |
| return --already ejected once! | |
| end | |
| -- --mark plane as broken and unflyable | |
| if _unit:getPlayerName() ~= nil and csar.currentlyDisabled[_unit:getName()] == nil then | |
| if csar.countCSARCrash == false then | |
| for _, _heliName in pairs(csar.csarUnits) do | |
| if _unit:getName() == _heliName then | |
| -- IGNORE Crashed CSAR | |
| return | |
| end | |
| end | |
| end | |
| csar.currentlyDisabled[_unit:getName()] = { timeout = (csar.disableTimeoutTime * 60) + timer.getTime(), desc = "", noPilot = _crashed, unitId = _unit:getID(), name = _unit:getName() } | |
| -- disable aircraft | |
| trigger.action.setUserFlag("CSAR_AIRCRAFT" .. _unit:getID(), 100) | |
| env.info("Unit Disabled: " .. _unit:getName() .. " ID:" .. _unit:getID()) | |
| end | |
| elseif csar.csarMode == 2 then -- disable aircraft for pilot | |
| --csar.pilotDisabled | |
| if _unit:getPlayerName() ~= nil and csar.pilotDisabled[_unit:getPlayerName() .. "_" .. _unit:getName()] == nil then | |
| if csar.countCSARCrash == false then | |
| for _, _heliName in pairs(csar.csarUnits) do | |
| if _unit:getName() == _heliName then | |
| -- IGNORE Crashed CSAR | |
| return | |
| end | |
| end | |
| end | |
| csar.pilotDisabled[_unit:getPlayerName() .. "_" .. _unit:getName()] = { timeout = (csar.disableTimeoutTime * 60) + timer.getTime(), desc = "", noPilot = true, unitId = _unit:getID(), player = _unit:getPlayerName(), name = _unit:getName() } | |
| -- disable aircraft | |
| -- strip special characters from name gsub('%W','') | |
| trigger.action.setUserFlag("CSAR_AIRCRAFT" .. _unit:getPlayerName():gsub('%W', '') .. "_" .. _unit:getID(), 100) | |
| env.info("Unit Disabled for player : " .. _unit:getName()) | |
| end | |
| elseif csar.csarMode == 3 then -- No Disable - Just reduce player lives | |
| --csar.pilotDisabled | |
| if _unit:getPlayerName() ~= nil then | |
| if csar.countCSARCrash == false then | |
| for _, _heliName in pairs(csar.csarUnits) do | |
| if _unit:getName() == _heliName then | |
| -- IGNORE Crashed CSAR | |
| return | |
| end | |
| end | |
| end | |
| local _lives = csar.pilotLives[_unit:getPlayerName()] | |
| if _lives == nil then | |
| _lives = csar.maxLives + 1 --plus 1 because we'll use flag set to 1 to indicate NO MORE LIVES | |
| end | |
| csar.pilotLives[_unit:getPlayerName()] = _lives - 1 | |
| trigger.action.setUserFlag("CSAR_PILOT" .. _unit:getPlayerName():gsub('%W', ''), _lives - 1) | |
| end | |
| end | |
| end | |
| function csar.enableAircraft(_name, _playerName) | |
| -- enable aircraft for ALL pilots | |
| if csar.csarMode == 1 then | |
| local _details = csar.currentlyDisabled[_name] | |
| if _details ~= nil then | |
| csar.currentlyDisabled[_name] = nil -- {timeout = (csar.disableTimeoutTime*60) + timer.getTime(),desc="",noPilot = _crashed,unitId=_unit:getID() } | |
| --use flag to reenable | |
| trigger.action.setUserFlag("CSAR_AIRCRAFT" .. _details.unitId, 0) | |
| end | |
| elseif csar.csarMode == 2 and _playerName ~= nil then -- enable aircraft for pilot | |
| local _details = csar.pilotDisabled[_playerName .. "_" .. _name] | |
| if _details ~= nil then | |
| csar.pilotDisabled[_playerName .. "_" .. _name] = nil | |
| trigger.action.setUserFlag("CSAR_AIRCRAFT" .. _playerName:gsub('%W', '') .. "_" .. _details.unitId, 0) | |
| end | |
| elseif csar.csarMode == 3 and _playerName ~= nil then -- No Disable - Just reduce player lives | |
| -- give back life | |
| local _lives = csar.pilotLives[_playerName] | |
| if _lives == nil then | |
| _lives = csar.maxLives + 1 --plus 1 because we'll use flag set to 1 to indicate NO MORE LIVES | |
| else | |
| _lives = _lives + 1 -- give back live! | |
| if csar.maxLives + 1 <= _lives then | |
| _lives = csar.maxLives + 1 --plus 1 because we'll use flag set to 1 to indicate NO MORE LIVES | |
| end | |
| end | |
| csar.pilotLives[_playerName] = _lives | |
| trigger.action.setUserFlag("CSAR_PILOT" .. _playerName:gsub('%W', ''), _lives) | |
| end | |
| end | |
| function csar.reactivateAircraft() | |
| timer.scheduleFunction(csar.reactivateAircraft, nil, timer.getTime() + 5) | |
| -- disable aircraft for ALL pilots | |
| if csar.csarMode == 1 then | |
| for _unitName, _details in pairs(csar.currentlyDisabled) do | |
| if timer.getTime() >= _details.timeout then | |
| csar.enableAircraft(_unitName) | |
| end | |
| end | |
| elseif csar.csarMode == 2 then -- disable aircraft for pilot | |
| for _key, _details in pairs(csar.pilotDisabled) do | |
| if timer.getTime() >= _details.timeout then | |
| csar.enableAircraft(_details.name, _details.player) | |
| end | |
| end | |
| elseif csar.csarMode == 3 then -- No Disable - Just reduce player lives | |
| end | |
| end | |
| function csar.checkDisabledAircraftStatus(_args) | |
| local _name = _args[1] | |
| local _playerName = _args[2] | |
| local _unit = Unit.getByName(_name) | |
| --if its not the same user anymore, stop checking | |
| if _unit ~= nil and _unit:getPlayerName() ~= nil and _playerName == _unit:getPlayerName() then | |
| -- disable aircraft for ALL pilots | |
| if csar.csarMode == 1 then | |
| local _details = csar.currentlyDisabled[_unit:getName()] | |
| if _details ~= nil then | |
| local _time = _details.timeout - timer.getTime() | |
| if _details.noPilot then | |
| if csar.disableAircraftTimeout then | |
| local _text = string.format("This aircraft cannot be flow as the pilot was killed in a crash. Reinforcements in %.2dM,%.2dS\n\nIt will be DESTROYED on takeoff!", (_time / 60), _time % 60) | |
| --display message, | |
| csar.displayMessageToSAR(_unit, _text, 10, true) | |
| else | |
| --display message, | |
| csar.displayMessageToSAR(_unit, "This aircraft cannot be flown again as the pilot was killed in a crash\n\nIt will be DESTROYED on takeoff!", 10, true) | |
| end | |
| else | |
| if csar.disableAircraftTimeout then | |
| --display message, | |
| csar.displayMessageToSAR(_unit, _details.desc .. " needs to be rescued or reinforcements arrive before this aircraft can be flown again! Reinforcements in " .. string.format("%.2dM,%.2d", (_time / 60), _time % 60) .. "\n\nIt will be DESTROYED on takeoff!", 10, true) | |
| else | |
| --display message, | |
| csar.displayMessageToSAR(_unit, _details.desc .. " needs to be rescued before this aircraft can be flown again!\n\nIt will be DESTROYED on takeoff!", 10, true) | |
| end | |
| end | |
| if csar.destroyUnit(_unit) then | |
| return --plane destroyed | |
| else | |
| --check again in 10 seconds | |
| timer.scheduleFunction(csar.checkDisabledAircraftStatus, _args, timer.getTime() + 10) | |
| end | |
| end | |
| elseif csar.csarMode == 2 then -- disable aircraft for pilot | |
| local _details = csar.pilotDisabled[_unit:getPlayerName() .. "_" .. _unit:getName()] | |
| if _details ~= nil then | |
| local _time = _details.timeout - timer.getTime() | |
| if _details.noPilot then | |
| if csar.disableAircraftTimeout then | |
| local _text = string.format("This aircraft cannot be flow as the pilot was killed in a crash. Reinforcements in %.2dM,%.2dS\n\nIt will be DESTROYED on takeoff!", (_time / 60), _time % 60) | |
| --display message, | |
| csar.displayMessageToSAR(_unit, _text, 10, true) | |
| else | |
| --display message, | |
| csar.displayMessageToSAR(_unit, "This aircraft cannot be flown again as the pilot was killed in a crash\n\nIt will be DESTROYED on takeoff!", 10, true) | |
| end | |
| else | |
| if csar.disableAircraftTimeout then | |
| --display message, | |
| csar.displayMessageToSAR(_unit, _details.desc .. " needs to be rescued or reinforcements arrive before this aircraft can be flown again! Reinforcements in " .. string.format("%.2dM,%.2d", (_time / 60), _time % 60) .. "\n\nIt will be DESTROYED on takeoff!", 10, true) | |
| else | |
| --display message, | |
| csar.displayMessageToSAR(_unit, _details.desc .. " needs to be rescued before this aircraft can be flown again!\n\nIt will be DESTROYED on takeoff!", 10, true) | |
| end | |
| end | |
| if csar.destroyUnit(_unit) then | |
| return --plane destroyed | |
| else | |
| --check again in 10 seconds | |
| timer.scheduleFunction(csar.checkDisabledAircraftStatus, _args, timer.getTime() + 10) | |
| end | |
| end | |
| elseif csar.csarMode == 3 then -- No Disable - Just reduce player lives | |
| local _lives = csar.pilotLives[_unit:getPlayerName()] | |
| if _lives == nil or _lives > 1 then | |
| if _lives == nil then | |
| _lives = csar.maxLives + 1 | |
| end | |
| -- -1 for lives as we use 1 to indicate out of lives! | |
| local _text = string.format("CSAR ACTIVE! \n\nYou have " .. (_lives - 1) .. " lives remaining. Make sure you eject!") | |
| csar.displayMessageToSAR(_unit, _text, 20, true) | |
| return | |
| else | |
| local _text = string.format("You have run out of LIVES! Lives will be reset on mission restart or when your pilot is rescued.\n\nThis aircraft will be DESTROYED on takeoff!") | |
| --display message, | |
| csar.displayMessageToSAR(_unit, _text, 10, true) | |
| if csar.destroyUnit(_unit) then | |
| return --plane destroyed | |
| else | |
| --check again in 10 seconds | |
| timer.scheduleFunction(csar.checkDisabledAircraftStatus, _args, timer.getTime() + 10) | |
| end | |
| end | |
| end | |
| end | |
| end | |
| function csar.destroyUnit(_unit) | |
| --destroy if the SAME player is still in the aircraft | |
| -- if a new player got in it'll be destroyed in a bit anyways | |
| if _unit ~= nil and _unit:getPlayerName() ~= nil then | |
| if csar.heightDiff(_unit) > csar.destructionHeight then | |
| csar.displayMessageToSAR(_unit, "**** Aircraft Destroyed as the pilot needs to be rescued or you have no lives! ****", 10, true) | |
| --if we're off the ground then explode | |
| trigger.action.explosion(_unit:getPoint(), 100); | |
| return true | |
| end | |
| --_unit:destroy() destroy doesnt work for playes who arent the host in multiplayer | |
| end | |
| return false | |
| end | |
| function csar.heightDiff(_unit) | |
| local _point = _unit:getPoint() | |
| return _point.y - land.getHeight({ x = _point.x, y = _point.z }) | |
| end | |
| csar.addBeaconToGroup = function(_woundedGroupName, _freq) | |
| local _group = Group.getByName(_woundedGroupName) | |
| if _group == nil then | |
| --return frequency to pool of available | |
| for _i, _current in ipairs(csar.usedVHFFrequencies) do | |
| if _current == _freq then | |
| table.insert(csar.freeVHFFrequencies, _freq) | |
| table.remove(csar.usedVHFFrequencies, _i) | |
| end | |
| end | |
| return | |
| end | |
| local _sound = "l10n/DEFAULT/" .. csar.radioSound | |
| trigger.action.radioTransmission(_sound, _group:getUnit(1):getPoint(), 0, false, _freq, 1000) | |
| timer.scheduleFunction(csar.refreshRadioBeacon, { _woundedGroupName, _freq }, timer.getTime() + 30) | |
| end | |
| csar.refreshRadioBeacon = function(_args) | |
| csar.addBeaconToGroup(_args[1], _args[2]) | |
| end | |
| csar.addSpecialParametersToGroup = function(_spawnedGroup) | |
| -- Immortal code for alexej21 | |
| local _setImmortal = { | |
| id = 'SetImmortal', | |
| params = { | |
| value = true | |
| } | |
| } | |
| -- invisible to AI, Shagrat | |
| local _setInvisible = { | |
| id = 'SetInvisible', | |
| params = { | |
| value = true | |
| } | |
| } | |
| local _controller = _spawnedGroup:getController() | |
| if (csar.immortalcrew) then | |
| Controller.setCommand(_controller, _setImmortal) | |
| end | |
| if (csar.invisiblecrew) then | |
| Controller.setCommand(_controller, _setInvisible) | |
| end | |
| end | |
| function csar.spawnGroup( _coalition, _country, _point, _typeName ) | |
| local _id = mist.getNextGroupId() | |
| local _groupName = "Downed Pilot #" .. _id | |
| local _side = _coalition | |
| local _pos = _point | |
| local _group = { | |
| ["visible"] = false, | |
| ["groupId"] = _id, | |
| ["hidden"] = false, | |
| ["units"] = {}, | |
| ["name"] = _groupName, | |
| ["task"] = {}, | |
| } | |
| if _side == 2 then | |
| _group.units[1] = csar.createUnit(_pos.x + 50, _pos.z + 50, 120, "Soldier M4") | |
| else | |
| _group.units[1] = csar.createUnit(_pos.x + 50, _pos.z + 50, 120, "Infantry AK") | |
| end | |
| _group.category = Group.Category.GROUND; | |
| _group.country = _country; | |
| local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name) | |
| -- Turn off AI | |
| if csar.allowDownedPilotCAcontrol == false then | |
| trigger.action.setGroupAIOff(_spawnedGroup) | |
| end | |
| return _spawnedGroup | |
| end | |
| function csar.createUnit(_x, _y, _heading, _type) | |
| local _id = mist.getNextUnitId(); | |
| local _name = string.format("Wounded Pilot #%s", _id) | |
| local _newUnit = { | |
| ["y"] = _y, | |
| ["type"] = _type, | |
| ["name"] = _name, | |
| ["unitId"] = _id, | |
| ["heading"] = _heading, | |
| ["playerCanDrive"] = false, | |
| ["skill"] = "Excellent", | |
| ["x"] = _x, | |
| } | |
| return _newUnit | |
| end | |
| function csar.initSARForPilot(_downedGroup, _freq) | |
| local _leader = _downedGroup:getUnit(1) | |
| local _coordinatesText = csar.getPositionOfWounded(_downedGroup) | |
| local | |
| _text = string.format("%s requests SAR at %s, beacon at %.2f KHz", | |
| _leader:getName(), _coordinatesText, _freq / 1000) | |
| local _randPercent = math.random(1, 100) | |
| -- Loop through all the medevac units | |
| for x, _heliName in pairs(csar.csarUnits) do | |
| local _status, _err = pcall(function(_args) | |
| local _unitName = _args[1] | |
| local _woundedSide = _args[2] | |
| local _medevacText = _args[3] | |
| local _leaderPos = _args[4] | |
| local _groupName = _args[5] | |
| local _group = _args[6] | |
| local _heli = csar.getSARHeli(_unitName) | |
| -- queue up for all SAR, alive or dead, we dont know the side if they're dead or not spawned so check | |
| --coalition in scheduled smoke | |
| if _heli ~= nil then | |
| -- Check coalition side | |
| if (_woundedSide == _heli:getCoalition()) then | |
| -- Display a delayed message | |
| timer.scheduleFunction(csar.delayedHelpMessage, { _unitName, _medevacText, _groupName }, timer.getTime() + csar.requestdelay) | |
| -- Schedule timer to check when to pop smoke | |
| timer.scheduleFunction(csar.checkWoundedGroupStatus, { _unitName, _groupName }, timer.getTime() + 1) | |
| end | |
| else | |
| --env.warning(string.format("Medevac unit %s not active", _heliName), false) | |
| -- Schedule timer for Dead unit so when the unit respawns he can still pickup units | |
| --timer.scheduleFunction(medevac.checkStatus, {_unitName,_groupName}, timer.getTime() + 5) | |
| end | |
| end, { _heliName, _leader:getCoalition(), _text, _leader:getPoint(), _downedGroup:getName(), _downedGroup }) | |
| if (not _status) then | |
| env.warning(string.format("Error while checking with medevac-units %s", _err)) | |
| end | |
| end | |
| end | |
| function csar.checkWoundedGroupStatus(_argument) | |
| local _status, _err = pcall(function(_args) | |
| local _heliName = _args[1] | |
| local _woundedGroupName = _args[2] | |
| local _woundedGroup = csar.getWoundedGroup(_woundedGroupName) | |
| local _heliUnit = csar.getSARHeli(_heliName) | |
| -- if wounded group is not here then message alread been sent to SARs | |
| -- stop processing any further | |
| if csar.woundedGroups[_woundedGroupName] == nil then | |
| return | |
| end | |
| local _woundedLeader = _woundedGroup[1] | |
| local _lookupKeyHeli = _heliName .. "_" .. _woundedLeader:getID() --lookup key for message state tracking | |
| if _heliUnit == nil then | |
| -- stop wounded moving, head back to smoke as target heli is DEAD | |
| -- in transit cleanup | |
| -- csar.inTransitGroups[_heliName] = nil | |
| csar.heliVisibleMessage[_lookupKeyHeli] = nil | |
| csar.heliCloseMessage[_lookupKeyHeli] = nil | |
| csar.landedStatus[_lookupKeyHeli] = nil | |
| return | |
| end | |
| -- double check that this function hasnt been queued for the wrong side | |
| if csar.woundedGroups[_woundedGroupName].side ~= _heliUnit:getCoalition() then | |
| return --wrong side! | |
| end | |
| if csar.checkGroupNotKIA(_woundedGroup, _woundedGroupName, _heliUnit, _heliName) then | |
| local _woundedLeader = _woundedGroup[1] | |
| local _lookupKeyHeli = _heliUnit:getName() .. "_" .. _woundedLeader:getID() --lookup key for message state tracking | |
| local _distance = csar.getDistance(_heliUnit:getPoint(), _woundedLeader:getPoint()) | |
| if _distance < 3000 then | |
| if csar.checkCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedGroup, _woundedGroupName) == true then | |
| -- we're close, reschedule | |
| timer.scheduleFunction(csar.checkWoundedGroupStatus, _args, timer.getTime() + 1) | |
| end | |
| else | |
| csar.heliVisibleMessage[_lookupKeyHeli] = nil | |
| --reschedule as units arent dead yet , schedule for a bit slower though as we're far away | |
| timer.scheduleFunction(csar.checkWoundedGroupStatus, _args, timer.getTime() + 5) | |
| end | |
| end | |
| end, _argument) | |
| if not _status then | |
| env.error(string.format("error checkWoundedGroupStatus %s", _err)) | |
| end | |
| end | |
| function csar.popSmokeForGroup(_woundedGroupName, _woundedLeader) | |
| -- have we popped smoke already in the last 5 mins | |
| local _lastSmoke = csar.smokeMarkers[_woundedGroupName] | |
| if _lastSmoke == nil or timer.getTime() > _lastSmoke then | |
| local _smokecolor | |
| if (_woundedLeader:getCoalition() == 2) then | |
| _smokecolor = csar.bluesmokecolor | |
| else | |
| _smokecolor = csar.redsmokecolor | |
| end | |
| trigger.action.smoke(_woundedLeader:getPoint(), _smokecolor) | |
| csar.smokeMarkers[_woundedGroupName] = timer.getTime() + 300 -- next smoke time | |
| end | |
| end | |
| function csar.pickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) | |
| local _woundedLeader = _woundedGroup[1] | |
| -- GET IN! | |
| local _heliName = _heliUnit:getName() | |
| local _groups = csar.inTransitGroups[_heliName] | |
| local _unitsInHelicopter = csar.pilotsOnboard(_heliName) | |
| -- init table if there is none for this helicopter | |
| if not _groups then | |
| csar.inTransitGroups[_heliName] = {} | |
| _groups = csar.inTransitGroups[_heliName] | |
| end | |
| -- if the heli can't pick them up, show a message and return | |
| local _maxUnits = csar.aircraftType[_heliUnit:getTypeName()] | |
| if _maxUnits == nil then | |
| _maxUnits = csar.max_units | |
| end | |
| if _unitsInHelicopter + 1 > _maxUnits then | |
| csar.displayMessageToSAR(_heliUnit, string.format("%s, %s. We're already crammed with %d guys! Sorry!", | |
| _pilotName, _heliName, _unitsInHelicopter, _unitsInHelicopter), 10) | |
| return true | |
| end | |
| csar.inTransitGroups[_heliName][_woundedGroupName] = | |
| { | |
| originalUnit = csar.woundedGroups[_woundedGroupName].originalUnit, | |
| woundedGroup = _woundedGroupName, | |
| side = _heliUnit:getCoalition(), | |
| desc = csar.woundedGroups[_woundedGroupName].desc, | |
| player = csar.woundedGroups[_woundedGroupName].player, | |
| } | |
| Group.destroy(_woundedLeader:getGroup()) | |
| csar.displayMessageToSAR(_heliUnit, string.format("%s: %s I'm in! Get to the MASH ASAP! ", _heliName, _pilotName), 10) | |
| timer.scheduleFunction(csar.scheduledSARFlight, | |
| { | |
| heliName = _heliUnit:getName(), | |
| groupName = _woundedGroupName | |
| }, | |
| timer.getTime() + 1) | |
| return true | |
| end | |
| function csar.getAliveGroup(_groupName) | |
| local _group = Group.getByName(_groupName) | |
| if _group and _group:isExist() == true and #_group:getUnits() > 0 then | |
| return _group | |
| end | |
| return nil | |
| end | |
| function csar.orderGroupToMoveToPoint(_leader, _destination) | |
| 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 _mission = { | |
| id = 'Mission', | |
| params = { | |
| route = { | |
| points =_path | |
| }, | |
| }, | |
| } | |
| -- delayed 2 second to work around bug | |
| timer.scheduleFunction(function(_arg) | |
| local _grp = csar.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.GREEN) | |
| Controller.setOption(_controller, AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD) | |
| _controller:setTask(_arg[2]) | |
| end | |
| end | |
| , {_group:getName(), _mission}, timer.getTime() + 2) | |
| end | |
| -- Helicopter is within 3km | |
| function csar.checkCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedGroup, _woundedGroupName) | |
| local _woundedLeader = _woundedGroup[1] | |
| local _lookupKeyHeli = _heliUnit:getName() .. "_" .. _woundedLeader:getID() --lookup key for message state tracking | |
| local _pilotName = csar.woundedGroups[_woundedGroupName].desc | |
| local _woundedCount = 1 | |
| local _reset = true | |
| if csar.autosmoke == true then | |
| csar.popSmokeForGroup(_woundedGroupName, _woundedLeader) | |
| end | |
| if csar.heliVisibleMessage[_lookupKeyHeli] == nil then | |
| if csar.autosmoke == true then | |
| csar.displayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn that thing is loud! Land or hover by the smoke.", _heliName, _pilotName), 30) | |
| else | |
| csar.displayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn that thing is loud! Request a Flare or Smoke if you need", _heliName, _pilotName), 30) | |
| end | |
| --mark as shown for THIS heli and THIS group | |
| csar.heliVisibleMessage[_lookupKeyHeli] = true | |
| end | |
| if (_distance < 500) then | |
| if csar.heliCloseMessage[_lookupKeyHeli] == nil then | |
| if csar.autosmoke == true then | |
| csar.displayMessageToSAR(_heliUnit, string.format("%s: %s. You're close now! Land or hover at the smoke.", _heliName, _pilotName), 10) | |
| else | |
| csar.displayMessageToSAR(_heliUnit, string.format("%s: %s. You're close now! Land in a safe place, i will go there ", _heliName, _pilotName), 10) | |
| end | |
| --mark as shown for THIS heli and THIS group | |
| csar.heliCloseMessage[_lookupKeyHeli] = true | |
| end | |
| -- have we landed close enough? | |
| if csar.inAir(_heliUnit) == false then | |
| -- if you land on them, doesnt matter if they were heading to someone else as you're closer, you win! :) | |
| if csar.pilotRuntoExtractPoint == true then | |
| if (_distance < csar.extractDistance) then | |
| local _time = csar.landedStatus[_lookupKeyHeli] | |
| if _time == nil then | |
| --csar.displayMessageToSAR(_heliUnit, "Landed at " .. _distance, 10, true) | |
| csar.landedStatus[_lookupKeyHeli] = math.floor( (_distance * csar.loadtimemax ) / csar.extractDistance ) | |
| _time = csar.landedStatus[_lookupKeyHeli] | |
| csar.orderGroupToMoveToPoint(_woundedLeader, _heliUnit:getPoint()) | |
| csar.displayMessageToSAR(_heliUnit, "Wait till " .. _pilotName .. ". Gets in \n" .. _time .. " more seconds.", 10, true) | |
| else | |
| _time = csar.landedStatus[_lookupKeyHeli] - 1 | |
| csar.landedStatus[_lookupKeyHeli] = _time | |
| end | |
| if _time <= 0 then | |
| csar.landedStatus[_lookupKeyHeli] = nil | |
| return csar.pickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) | |
| end | |
| end | |
| else | |
| if (_distance < csar.loadDistance) then | |
| return csar.pickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) | |
| end | |
| end | |
| else | |
| local _unitsInHelicopter = csar.pilotsOnboard(_heliName) | |
| local _maxUnits = csar.aircraftType[_heliUnit:getTypeName()] | |
| if _maxUnits == nil then | |
| _maxUnits = csar.max_units | |
| end | |
| if csar.inAir(_heliUnit) and _unitsInHelicopter + 1 <= _maxUnits then | |
| if _distance < 8.0 then | |
| --check height! | |
| local _height = _heliUnit:getPoint().y - _woundedLeader:getPoint().y | |
| if _height <= 20.0 then | |
| local _time = csar.hoverStatus[_lookupKeyHeli] | |
| if _time == nil then | |
| csar.hoverStatus[_lookupKeyHeli] = 10 | |
| _time = 10 | |
| else | |
| _time = csar.hoverStatus[_lookupKeyHeli] - 1 | |
| csar.hoverStatus[_lookupKeyHeli] = _time | |
| end | |
| if _time > 0 then | |
| csar.displayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you're too far away!", 10, true) | |
| else | |
| csar.hoverStatus[_lookupKeyHeli] = nil | |
| return csar.pickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) | |
| end | |
| _reset = false | |
| else | |
| csar.displayMessageToSAR(_heliUnit, "Too high to winch " .. _pilotName .. " \nReduce height and hover for 10 seconds!", 5, true) | |
| end | |
| end | |
| end | |
| end | |
| end | |
| if _reset then | |
| csar.hoverStatus[_lookupKeyHeli] = nil | |
| end | |
| return true | |
| end | |
| function csar.checkGroupNotKIA(_woundedGroup, _woundedGroupName, _heliUnit, _heliName) | |
| -- check if unit has died or been picked up | |
| if #_woundedGroup == 0 and _heliUnit ~= nil then | |
| local inTransit = false | |
| for _currentHeli, _groups in pairs(csar.inTransitGroups) do | |
| if _groups[_woundedGroupName] then | |
| local _group = _groups[_woundedGroupName] | |
| if _group.side == _heliUnit:getCoalition() then | |
| inTransit = true | |
| csar.displayToAllSAR(string.format("%s has been picked up by %s", _woundedGroupName, _currentHeli), _heliUnit:getCoalition(), _heliName) | |
| break | |
| end | |
| end | |
| end | |
| --display to all sar | |
| if inTransit == false then | |
| --DEAD | |
| csar.displayToAllSAR(string.format("%s is KIA ", _woundedGroupName), _heliUnit:getCoalition(), _heliName) | |
| end | |
| -- medevac.displayMessageToSAR(_heliUnit, string.format("%s: %s is dead", _heliName,_woundedGroupName ),10) | |
| --stops the message being displayed again | |
| csar.woundedGroups[_woundedGroupName] = nil | |
| return false | |
| end | |
| --continue | |
| return true | |
| end | |
| function csar.scheduledSARFlight(_args) | |
| local _status, _err = pcall(function(_args) | |
| local _heliUnit = csar.getSARHeli(_args.heliName) | |
| local _woundedGroupName = _args.groupName | |
| if (_heliUnit == nil) then | |
| --helicopter crashed? | |
| -- Put intransit pilots back | |
| --TODO possibly respawn the guys | |
| if csar.reenableIfCSARCrashes then | |
| local _rescuedGroups = csar.inTransitGroups[_args.heliName] | |
| if _rescuedGroups ~= nil then | |
| -- enable pilots again | |
| for _, _rescueGroup in pairs(_rescuedGroups) do | |
| csar.enableAircraft(_rescueGroup.originalUnit, _rescuedGroups.player) | |
| end | |
| end | |
| end | |
| csar.inTransitGroups[_args.heliName] = nil | |
| return | |
| end | |
| if csar.inTransitGroups[_heliUnit:getName()] == nil or csar.inTransitGroups[_heliUnit:getName()][_woundedGroupName] == nil then | |
| -- Groups already rescued | |
| return | |
| end | |
| local _dist = csar.getClosetMASH(_heliUnit) | |
| if _dist == -1 then | |
| -- Can now rescue to FARP | |
| -- Mash Dead | |
| -- csar.inTransitGroups[_heliUnit:getName()][_woundedGroupName] = nil | |
| -- csar.displayMessageToSAR(_heliUnit, string.format("%s: NO MASH! The pilot died of despair!", _heliUnit:getName()), 10) | |
| return | |
| end | |
| if _dist < 200 and _heliUnit:inAir() == false then | |
| csar.rescuePilots(_heliUnit) | |
| return | |
| end | |
| -- end | |
| --queue up | |
| timer.scheduleFunction(csar.scheduledSARFlight, | |
| { | |
| heliName = _heliUnit:getName(), | |
| groupName = _woundedGroupName | |
| }, | |
| timer.getTime() + 1) | |
| end, _args) | |
| if (not _status) then | |
| env.error(string.format("Error in scheduledSARFlight\n\n%s", _err)) | |
| end | |
| end | |
| function csar.rescuePilots(_heliUnit) | |
| local _rescuedGroups = csar.inTransitGroups[_heliUnit:getName()] | |
| if _rescuedGroups == nil then | |
| -- Groups already rescued | |
| return | |
| end | |
| csar.inTransitGroups[_heliUnit:getName()] = nil | |
| local _txt = string.format("%s: The pilots have been taken to the\nmedical clinic. Good job!", _heliUnit:getName()) | |
| -- enable pilots again | |
| for _, _rescueGroup in pairs(_rescuedGroups) do | |
| csar.enableAircraft(_rescueGroup.originalUnit, _rescueGroup.player) | |
| end | |
| csar.displayMessageToSAR(_heliUnit, _txt, 10) | |
| -- env.info("Rescued") | |
| end | |
| function csar.getSARHeli(_unitName) | |
| local _heli = Unit.getByName(_unitName) | |
| if _heli ~= nil and _heli:isActive() and _heli:getLife() > 0 then | |
| return _heli | |
| end | |
| return nil | |
| end | |
| -- Displays a request for medivac | |
| function csar.delayedHelpMessage(_args) | |
| local status, err = pcall(function(_args) | |
| local _heliName = _args[1] | |
| local _text = _args[2] | |
| local _injuredGroupName = _args[3] | |
| local _heli = csar.getSARHeli(_heliName) | |
| if _heli ~= nil and #csar.getWoundedGroup(_injuredGroupName) > 0 then | |
| csar.displayMessageToSAR(_heli, _text, csar.messageTime) | |
| local _groupId = csar.getGroupId(_heli) | |
| if _groupId then | |
| trigger.action.outSoundForGroup(_groupId, "l10n/DEFAULT/CSAR.ogg") | |
| end | |
| else | |
| env.info("No Active Heli or Group DEAD") | |
| end | |
| end, _args) | |
| if (not status) then | |
| env.error(string.format("Error in delayedHelpMessage ")) | |
| end | |
| return nil | |
| end | |
| function csar.displayMessageToSAR(_unit, _text, _time, _clear) | |
| local _groupId = csar.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 csar.getWoundedGroup(_groupName) | |
| local _status, _result = pcall(function(_groupName) | |
| local _woundedGroup = {} | |
| local _units = Group.getByName(_groupName):getUnits() | |
| for _, _unit in pairs(_units) do | |
| if _unit ~= nil and _unit:isActive() and _unit:getLife() > 0 then | |
| table.insert(_woundedGroup, _unit) | |
| end | |
| end | |
| return _woundedGroup | |
| end, _groupName) | |
| if (_status) then | |
| return _result | |
| else | |
| --env.warning(string.format("getWoundedGroup failed! Returning 0.%s",_result), false) | |
| return {} --return empty table | |
| end | |
| end | |
| function csar.convertGroupToTable(_group) | |
| local _unitTable = {} | |
| for _, _unit in pairs(_group:getUnits()) do | |
| if _unit ~= nil and _unit:getLife() > 0 then | |
| table.insert(_unitTable, _unit:getName()) | |
| end | |
| end | |
| return _unitTable | |
| end | |
| function csar.getPositionOfWounded(_woundedGroup) | |
| local _woundedTable = csar.convertGroupToTable(_woundedGroup) | |
| local _coordinatesText = "" | |
| if csar.coordtype == 0 then -- Lat/Long DMTM | |
| _coordinatesText = string.format("%s", mist.getLLString({ units = _woundedTable, acc = csar.coordaccuracy, DMS = 0 })) | |
| elseif csar.coordtype == 1 then -- Lat/Long DMS | |
| _coordinatesText = string.format("%s", mist.getLLString({ units = _woundedTable, acc = csar.coordaccuracy, DMS = 1 })) | |
| elseif csar.coordtype == 2 then -- MGRS | |
| _coordinatesText = string.format("%s", mist.getMGRSString({ units = _woundedTable, acc = csar.coordaccuracy })) | |
| elseif csar.coordtype == 3 then -- Bullseye Imperial | |
| _coordinatesText = string.format("bullseye %s", mist.getBRString({ units = _woundedTable, ref = coalition.getMainRefPoint(_woundedGroup:getCoalition()), alt = 0 })) | |
| else -- Bullseye Metric --(medevac.coordtype == 4) | |
| _coordinatesText = string.format("bullseye %s", mist.getBRString({ units = _woundedTable, ref = coalition.getMainRefPoint(_woundedGroup:getCoalition()), alt = 0, metric = 1 })) | |
| end | |
| return _coordinatesText | |
| end | |
| -- Displays all active MEDEVACS/SAR | |
| function csar.displayActiveSAR(_unitName) | |
| local _msg = "Active MEDEVAC/SAR:" | |
| local _heli = csar.getSARHeli(_unitName) | |
| if _heli == nil then | |
| return | |
| end | |
| local _heliSide = _heli:getCoalition() | |
| local _csarList = {} | |
| for _groupName, _value in pairs(csar.woundedGroups) do | |
| local _woundedGroup = csar.getWoundedGroup(_groupName) | |
| if #_woundedGroup > 0 and (_woundedGroup[1]:getCoalition() == _heliSide) then | |
| local _coordinatesText = csar.getPositionOfWounded(_woundedGroup[1]:getGroup()) | |
| local _distance = csar.getDistance(_heli:getPoint(), _woundedGroup[1]:getPoint()) | |
| table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %.2f KHz ADF - %.3fKM ", _value.desc, _coordinatesText, _value.frequency / 1000, _distance / 1000.0) }) | |
| end | |
| end | |
| local function sortDistance(a, b) | |
| return a.dist < b.dist | |
| end | |
| table.sort(_csarList, sortDistance) | |
| for _, _line in pairs(_csarList) do | |
| _msg = _msg .. "\n" .. _line.msg | |
| end | |
| csar.displayMessageToSAR(_heli, _msg, 20) | |
| end | |
| function csar.getClosetDownedPilot(_heli) | |
| local _side = _heli:getCoalition() | |
| local _closetGroup = nil | |
| local _shortestDistance = -1 | |
| local _distance = 0 | |
| local _closetGroupInfo = nil | |
| for _woundedName, _groupInfo in pairs(csar.woundedGroups) do | |
| local _tempWounded = csar.getWoundedGroup(_woundedName) | |
| -- check group exists and not moving to someone else | |
| if #_tempWounded > 0 and (_tempWounded[1]:getCoalition() == _side) then | |
| _distance = csar.getDistance(_heli:getPoint(), _tempWounded[1]:getPoint()) | |
| if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then | |
| _shortestDistance = _distance | |
| _closetGroup = _tempWounded[1] | |
| _closetGroupInfo = _groupInfo | |
| end | |
| end | |
| end | |
| return { pilot = _closetGroup, distance = _shortestDistance, groupInfo = _closetGroupInfo } | |
| end | |
| function csar.signalFlare(_unitName) | |
| local _heli = csar.getSARHeli(_unitName) | |
| if _heli == nil then | |
| return | |
| end | |
| local _closet = csar.getClosetDownedPilot(_heli) | |
| if _closet ~= nil and _closet.pilot ~= nil and _closet.distance < 8000.0 then | |
| local _clockDir = csar.getClockDirection(_heli, _closet.pilot) | |
| local _msg = string.format("%s - %.2f KHz ADF - %.3fM - Popping Signal Flare at your %s ", _closet.groupInfo.desc, _closet.groupInfo.frequency / 1000, _closet.distance, _clockDir) | |
| csar.displayMessageToSAR(_heli, _msg, 20) | |
| trigger.action.signalFlare(_closet.pilot:getPoint(), 1, 0) | |
| else | |
| csar.displayMessageToSAR(_heli, "No Pilots within 8KM", 20) | |
| end | |
| end | |
| function csar.displayToAllSAR(_message, _side, _ignore) | |
| for _, _unitName in pairs(csar.csarUnits) do | |
| local _unit = csar.getSARHeli(_unitName) | |
| if _unit ~= nil and _unit:getCoalition() == _side then | |
| if _ignore == nil or _ignore ~= _unitName then | |
| csar.displayMessageToSAR(_unit, _message, 10) | |
| end | |
| else | |
| -- env.info(string.format("unit nil %s",_unitName)) | |
| end | |
| end | |
| end | |
| function csar.reqsmoke( _unitName ) | |
| local _heli = csar.getSARHeli(_unitName) | |
| if _heli == nil then | |
| return | |
| end | |
| local _closet = csar.getClosetDownedPilot(_heli) | |
| if _closet ~= nil and _closet.pilot ~= nil and _closet.distance < 8000.0 then | |
| local _clockDir = csar.getClockDirection(_heli, _closet.pilot) | |
| local _msg = string.format("%s - %.2f KHz ADF - %.3fM - Popping Blue smoke at your %s ", _closet.groupInfo.desc, _closet.groupInfo.frequency / 1000, _closet.distance, _clockDir) | |
| csar.displayMessageToSAR(_heli, _msg, 20) | |
| local _smokecolor | |
| if (_closet.pilot:getCoalition() == 2) then | |
| _smokecolor = csar.bluesmokecolor | |
| else | |
| _smokecolor = csar.redsmokecolor | |
| end | |
| trigger.action.smoke(_closet.pilot:getPoint(), _smokecolor) | |
| else | |
| csar.displayMessageToSAR(_heli, "No Pilots within 8KM", 20) | |
| end | |
| end | |
| function csar.getClosetMASH(_heli) | |
| local _mashes = csar.bluemash | |
| if (_heli:getCoalition() == 1) then | |
| _mashes = csar.redmash | |
| end | |
| local _shortestDistance = -1 | |
| local _distance = 0 | |
| for _, _mashName in pairs(_mashes) do | |
| local _mashUnit = Unit.getByName(_mashName) | |
| if _mashUnit ~= nil and _mashUnit:isActive() and _mashUnit:getLife() > 0 then | |
| _distance = csar.getDistance(_heli:getPoint(), _mashUnit:getPoint()) | |
| if _distance ~= nil and (_shortestDistance == -1 or _distance < _shortestDistance) then | |
| _shortestDistance = _distance | |
| end | |
| end | |
| end | |
| if _shortestDistance ~= -1 then | |
| return _shortestDistance | |
| else | |
| return -1 | |
| end | |
| end | |
| function csar.checkOnboard(_unitName) | |
| local _unit = csar.getSARHeli(_unitName) | |
| if _unit == nil then | |
| return | |
| end | |
| --list onboard pilots | |
| local _inTransit = csar.inTransitGroups[_unitName] | |
| if _inTransit == nil or csar.tableLength(_inTransit) == 0 then | |
| csar.displayMessageToSAR(_unit, "No Rescued Pilots onboard", 30) | |
| else | |
| local _text = "Onboard - RTB to FARP/Airfield or MASH: " | |
| for _, _onboard in pairs(csar.inTransitGroups[_unitName]) do | |
| _text = _text .. "\n" .. _onboard.desc | |
| end | |
| csar.displayMessageToSAR(_unit, _text, 30) | |
| end | |
| end | |
| function csar.addweight( _heli ) | |
| local cargoWeight = 0 | |
| local _heliName = _heli:getName() | |
| if ctld ~= nil and ctld.troopWeight ~= nil then | |
| -- TODO Count CTLD troops | |
| end | |
| ctld.troopWeight = 100 | |
| if csar.inTransitGroups[_heliName] then | |
| local csarcount = 0 | |
| for _, _group in pairs(csar.inTransitGroups[_heliName]) do | |
| csarcount = csarcount + 1 | |
| end | |
| cargoWeight = cargoWeight + csar.weight * csarcount | |
| end | |
| trigger.action.setUnitInternalCargo(_heli:getName(),0 ) -- Set To to recalculate | |
| trigger.action.setUnitInternalCargo(_heli:getName(), cargoWeight) | |
| end | |
| -- Adds menuitem to all medevac units that are active | |
| function csar.addMedevacMenuItem() | |
| -- Loop through all Medevac units | |
| timer.scheduleFunction(csar.addMedevacMenuItem, nil, timer.getTime() + 5) | |
| local _allHeliGroups = coalition.getGroups(coalition.side.BLUE, Group.Category.HELICOPTER) | |
| for key, val in pairs (coalition.getGroups(coalition.side.RED, Group.Category.HELICOPTER)) do | |
| table.insert(_allHeliGroups, val) | |
| end | |
| for _key, _group in pairs (_allHeliGroups) do | |
| local _unit = _group:getUnit(1) -- Asume that there is only one unit in the flight for players | |
| if _unit ~= nil then | |
| if _unit:isExist() == true then | |
| local unitName = _unit:getName() | |
| if csar.enableAllslots == true then | |
| -- Enable all helicopters | |
| local _type = _unit:getTypeName() | |
| if csar.aircraftType[_type] ~= nil then | |
| if csar.csarUnits[_unit:getName()] == nil then | |
| csar.csarUnits[_unit:getName()] = _unit:getName() | |
| for _woundedName, _groupInfo in pairs(csar.woundedGroups) do | |
| if _groupInfo.side == _group:getCoalition() then | |
| -- Schedule timer to check when to pop smoke | |
| timer.scheduleFunction(csar.checkWoundedGroupStatus, { _unit:getName() , _woundedName }, timer.getTime() + 5) | |
| end | |
| end | |
| end | |
| end | |
| elseif csar.useprefix == true then | |
| --use prefix | |
| local upperCaseUnitname = string.upper(unitName) | |
| for key, prefix in pairs (csar.csarPrefix) do | |
| local upperCasePrefix = string.upper(prefix) | |
| if string.match(upperCaseUnitname, upperCasePrefix) then | |
| if csar.csarUnits[_unit:getName()] == nil then | |
| csar.csarUnits[_unit:getName()] = _unit:getName() | |
| for _woundedName, _groupInfo in pairs(csar.woundedGroups) do | |
| if _groupInfo.side == _group:getCoalition() then | |
| -- Schedule timer to check when to pop smoke | |
| timer.scheduleFunction(csar.checkWoundedGroupStatus, { _unit:getName() , _woundedName }, timer.getTime() + 5) | |
| end | |
| end | |
| end | |
| break | |
| end | |
| end | |
| end | |
| end | |
| end | |
| end | |
| for key, unitName in pairs(csar.csarFixedUnits) do | |
| if csar.csarUnits[unitName] == nil then | |
| csar.csarUnits[unitName] = unitName | |
| for _woundedName, _groupInfo in pairs(csar.woundedGroups) do | |
| if _groupInfo.side == _group:getCoalition() then | |
| -- Schedule timer to check when to pop smoke | |
| timer.scheduleFunction(csar.checkWoundedGroupStatus, { unitName , _woundedName }, timer.getTime() + 5) | |
| end | |
| end | |
| end | |
| end | |
| for _, _unitName in pairs(csar.csarUnits) do | |
| local _unit = csar.getSARHeli(_unitName) | |
| if _unit ~= nil then | |
| local _groupId = csar.getGroupId(_unit) | |
| if _groupId then | |
| if csar.addedTo[tostring(_groupId)] == nil then | |
| csar.addedTo[tostring(_groupId)] = true | |
| local _rootPath = missionCommands.addSubMenuForGroup(_groupId, "CSAR") | |
| missionCommands.addCommandForGroup(_groupId, "List Active CSAR", _rootPath, csar.displayActiveSAR, | |
| _unitName) | |
| missionCommands.addCommandForGroup(_groupId, "Check Onboard", _rootPath, csar.checkOnboard, _unitName) | |
| missionCommands.addCommandForGroup(_groupId, "Request Signal Flare", _rootPath, csar.signalFlare, _unitName) | |
| missionCommands.addCommandForGroup(_groupId, "Request Smoke", _rootPath, csar.reqsmoke, _unitName) | |
| end | |
| end | |
| else | |
| -- env.info(string.format("unit nil %s",_unitName)) | |
| end | |
| end | |
| return | |
| end | |
| --get distance in meters assuming a Flat world | |
| function csar.getDistance(_point1, _point2) | |
| local xUnit = _point1.x | |
| local yUnit = _point1.z | |
| local xZone = _point2.x | |
| local yZone = _point2.z | |
| local xDiff = xUnit - xZone | |
| local yDiff = yUnit - yZone | |
| return math.sqrt(xDiff * xDiff + yDiff * yDiff) | |
| end | |
| -- 200 - 400 in 10KHz | |
| -- 400 - 850 in 10 KHz | |
| -- 850 - 1250 in 50 KHz | |
| function csar.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 | |
| } | |
| csar.freeVHFFrequencies = {} | |
| csar.usedVHFFrequencies = {} | |
| 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(csar.freeVHFFrequencies, _start) | |
| end | |
| _start = _start + 10000 | |
| 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(csar.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(csar.freeVHFFrequencies, _start) | |
| end | |
| _start = _start + 50000 | |
| end | |
| end | |
| function csar.generateADFFrequency() | |
| if #csar.freeVHFFrequencies <= 3 then | |
| csar.freeVHFFrequencies = csar.usedVHFFrequencies | |
| csar.usedVHFFrequencies = {} | |
| end | |
| local _vhf = table.remove(csar.freeVHFFrequencies, math.random(#csar.freeVHFFrequencies)) | |
| return _vhf | |
| --- return {uhf=_uhf,vhf=_vhf} | |
| end | |
| function csar.inAir(_heli) | |
| 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 | |
| end | |
| function csar.getClockDirection(_heli, _crate) | |
| -- 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 _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 _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 _rightDistance = mist.vec.dp(_relativePosition, _headingVectorPerpendicular) | |
| 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 | |
| return _angle | |
| end | |
| function csar.getGroupId(_unit) | |
| local _unitDB = mist.DBs.unitsById[tonumber(_unit:getID())] | |
| if _unitDB ~= nil and _unitDB.groupId then | |
| return _unitDB.groupId | |
| end | |
| return nil | |
| end | |
| csar.generateVHFrequencies() | |
| -- Schedule timer to add radio item | |
| timer.scheduleFunction(csar.addMedevacMenuItem, nil, timer.getTime() + 5) | |
| if csar.disableAircraftTimeout then | |
| -- Schedule timer to reactivate things | |
| timer.scheduleFunction(csar.reactivateAircraft, nil, timer.getTime() + 5) | |
| end | |
| world.addEventHandler(csar.eventHandler) | |
| env.info("CSAR event handler added") | |
| --save CSAR MODE | |
| trigger.action.setUserFlag("CSAR_MODE", csar.csarMode) | |
| -- disable aircraft | |
| if csar.enableSlotBlocking then | |
| trigger.action.setUserFlag("CSAR_SLOTBLOCK", 100) | |
| env.info("CSAR Slot block enabled") | |
| end |