DCS-CSAR/CSAR.lua
Ciaran Fisher 4947887a78 Fixed Pilot Naming
Fixed Pilot Naming
Added distance to list CSAR list
2015-08-06 19:23:34 +01:00

1100 lines
34 KiB
Lua

-- CSAR Script for DCS Ciribob 2015
-- Version 1.1 - 6/8/2015
csar = {}
-- SETTINGS FOR MISSION DESIGNER vvvvvvvvvvvvvvvvvv
csar.csarUnits = { "MEDEVAC #1", "MEDEVAC #2", "MEDEVAC #3", "MEDEVAC #4", "MEDEVAC #5", "MEDEVAC RED #1" } -- List of all the MEDEVAC _UNIT NAMES_ (the line where it says "Pilot" in the ME)!
csar.bluemash = { "BlueMASH #1", "BlueMASH #2" } -- The unit that serves as MASH for the blue side
csar.redmash = { "RedMASH #1", "RedMASH #2" } -- The unit that serves as MASH for the red side
csar.disableAircraft = true -- DISABLE player aircraft until the pilot is rescued?
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.enableForAI = false -- set to false to disable AI units from being rescued.
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
-- If you set it less than 25 the troops might not move close enough
csar.loadDistance = 50 -- configure distance for troops to get in helicopter in meters.
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!
-- SETTINGS FOR MISSION DESIGNER ^^^^^^^^^^^^^^^^^^^*
-- Sanity checks of mission designer
assert(mist ~= nil, "\n\n** HEY MISSION-DESIGNER! **\n\nMiST has not been loaded!\n\nMake sure MiST 3.7 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 = 5 --number of pilots that can be carried
csar.currentlyDisabled = {} --stored disabled aircraft
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
-- 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 == 15 then
-- 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
return true
elseif (_event.id == 9) then
-- Pilot dead
trigger.action.outTextForCoalition(_event.initiator:getCoalition(), "MAYDAY MAYDAY! " .. _event.initiator:getTypeName() .. " shot down. No Chute!", 10)
--remove status messages for each Heli?
return
elseif world.event.S_EVENT_EJECTION == _event.id then
env.info("Event unit - Pilot Ejected")
local _unit = _event.initiator
if csar.enableForAI == false and _unit:getPlayerName() == nil then
return
end
local _spawnedGroup = csar.spawnGroup(_unit)
csar.addSpecialParametersToGroup(_spawnedGroup)
trigger.action.outTextForCoalition(_unit:getCoalition(), "MAYDAY MAYDAY! " .. _unit:getTypeName() .. " shot down. Chute Spotted!", 10)
local _freq = csar.generateADFFrequency()
csar.addBeaconToGroup(_spawnedGroup:getName(),_freq)
-- Generate DESCRIPTION text
local _text = " "
if _unit:getPlayerName() ~= nil then
_text = "Pilot ".._unit:getPlayerName().." of ".._unit:getName().." - ".._unit:getTypeName()
else
_text = "AI Pilot of ".._unit:getName().." - ".._unit:getTypeName()
end
--mark plane as broken and unflyable
if _unit:getPlayerName() ~= nil and csar.disableAircraft == true then
csar.currentlyDisabled[_unit:getName()] = {timeout = csar.disableTimeoutTime*60 + timer.getTime(),desc=_text}
timer.scheduleFunction(csar.checkDisabledAircraftStatus, _unit:getName(), timer.getTime() + 1)
end
--store the old group under the new group name
csar.woundedGroups[_spawnedGroup:getName()] = { originalGroup = _unit:getGroup():getName(), side = _spawnedGroup:getCoalition(), originalUnit = _unit:getName(), frequency= _freq, desc = _text }
csar.initSARForPilot(_spawnedGroup,_freq)
--dont add until we're done processing...
--table.insert(medevac.deadUnits, _event.initiator)
end
end, _event)
if (not status) then
env.error(string.format("Error while handling event %s", err), csar.displayerrordialog)
end
end
function csar.checkDisabledAircraftStatus(_name)
local _details = csar.currentlyDisabled[_name]
if _details ~= nil then
if csar.disableAircraftTimeout and timer.getTime() > _details.timeout then
--remove from disabled
csar.currentlyDisabled[_name] = nil
return
end
local _unit = Unit.getByName(_name)
if _unit ~= nil then
--display message,
csar.displayMessageToSAR(_unit, _details.desc .. " needs to be rescued before this aircraft can be flown again!", 10)
--destroy in 10 seconds
timer.scheduleFunction(csar.destroyUnit, _name, timer.getTime() + 5)
--queue up in 12 seconds
timer.scheduleFunction(csar.checkDisabledAircraftStatus, _name, timer.getTime() + 6)
return
end
else
return -- stop checking
end
timer.scheduleFunction(csar.checkDisabledAircraftStatus, _name, timer.getTime() + 1)
end
function csar.destroyUnit(_unitName)
local _unit = Unit.getByName(_unitName)
if _unit ~= nil then
_unit:destroy()
end
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(ctld.freeVHFFrequencies, _freq)
table.remove(ctld.usedVHFFrequencies, _i)
end
end
return
end
-- local _coordinatesText = string.format("%s at %s - %.2f KHz ADF ", _woundedGroupName, csar.getPositionOfWounded(_group), _freq/1000)
--
-- local _setFrequency = {
-- ["enabled"] = true,
-- ["auto"] = false,
-- ["id"] = "WrappedAction",
-- ["number"] = 1, -- first task
-- ["params"] = {
-- ["action"] = {
-- ["id"] = "SetFrequency",
-- ["params"] = {
-- ["modulation"] = 0, -- 0 is AM 1 is FM --if FM you cant read the message... might be the only fix to stop FC3 aircraft hearing it... :(
-- ["frequency"] =_freq,
-- },
-- },
-- },
-- }
--
-- local _setupDetails = {
-- ["enabled"] = true,
-- ["auto"] = false,
-- ["id"] = "WrappedAction",
-- ["number"] = 2, -- second task
-- ["params"] = {
-- ["action"] = {
-- ["id"] = "TransmitMessage",
-- ["params"] = {
-- ["loop"] = true, --false works too
-- ["subtitle"] = _coordinatesText, --_text
-- ["duration"] = 60, -- reset every 60 seconds --used to have timer.getTime() +60
-- ["file"] = csar.radioSound,
-- },
-- },
-- }
-- }
--
-- local _groupController = _group:getController()
--
-- --reset!
-- _groupController:resetTask()
--
-- _groupController:setTask(_setFrequency)
-- _groupController:setTask(_setupDetails)
--
-- --Make the unit NOT engage
-- _groupController:setOption(AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD)
trigger.action.radioTransmission(csar.radioSound, _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(_deadUnit)
local _id = mist.getNextGroupId()
local _groupName = "Downed Pilot #" .. _id
local _side = _deadUnit:getCoalition()
local _pos = _deadUnit:getPoint()
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 = _deadUnit:getCountry();
local _spawnedGroup = Group.getByName(mist.dynAdd(_group).name)
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
if _heliUnit == nil then
-- stop wounded moving, head back to smoke as target heli is DEAD
-- in transit cleanup
-- csar.inTransitGroups[_heliName] = 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:getID() .. "_" .. _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
-- Helicopter is within 3km
function csar.checkCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedGroup, _woundedGroupName)
local _woundedLeader = _woundedGroup[1]
local _lookupKeyHeli = _heliUnit:getID() .. "_" .. _woundedLeader:getID() --lookup key for message state tracking
local _pilotName = csar.woundedGroups[_woundedGroupName].desc
local _woundedCount = 1
csar.popSmokeForGroup(_woundedGroupName, _woundedLeader)
if csar.heliVisibleMessage[_lookupKeyHeli] == nil then
csar.displayMessageToSAR(_heliUnit, string.format("%s: %s. I hear you! Damn that thing is loud! Land by the smoke.", _heliName,_pilotName), 30)
--mark as shown for THIS heli and THIS group
csar.heliVisibleMessage[_lookupKeyHeli] = true
end
if (_distance < 500) then
if csar.heliCloseMessage[_lookupKeyHeli] == nil then
csar.displayMessageToSAR(_heliUnit, string.format("%s: %s. You're close now! Land at the smoke.", _heliName, _pilotName), 10)
--mark as shown for THIS heli and THIS group
csar.heliCloseMessage[_lookupKeyHeli] = true
end
-- have we landed close enough?
if _heliUnit:inAir() == false then
-- if you land on them, doesnt matter if they were heading to someone else as you're closer, you win! :)
if (_distance < csar.loadDistance) then
-- 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
if _unitsInHelicopter + 1 > csar.max_units then
csar.displayMessageToSAR(_heliUnit, string.format("%s, %s. We're already crammed with %d guys! Sorry!",
_pilotName, _heliName, _unitsInHelicopter, _woundedCount), 10)
return true
end
csar.inTransitGroups[_heliName][_woundedGroupName] =
{
originalGroup = csar.woundedGroups[_woundedGroupName].originalGroup,
originalUnit = csar.woundedGroups[_woundedGroupName].originalUnit,
woundedGroup = _woundedGroupName,
side = _heliUnit:getCoalition(),
desc = csar.woundedGroups[_woundedGroupName].desc
}
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 false
end
else
end
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
-- Put intransit pilots back
--TODO possibly respawn the guys
local _rescuedGroups = csar.inTransitGroups[_args.heliName]
if _rescuedGroups ~= nil then
-- enable pilots again
for _, _rescueGroup in pairs(_rescuedGroups) do
csar.currentlyDisabled[_rescueGroup.originalUnit] = nil
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
-- 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
local _rescuedGroups = csar.inTransitGroups[_heliUnit:getName()]
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.currentlyDisabled[_rescueGroup.originalUnit] = nil
end
csar.displayMessageToSAR(_heliUnit, _txt, 10)
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.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)
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)
trigger.action.outTextForGroup(_unit:getGroup():getID(), _text, _time)
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()
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())
_msg = string.format("%s\n%s at %s - %.2f KHz ADF - %.3fKM ", _msg, _value.desc, _coordinatesText, _value.frequency/1000,_distance/1000.0)
end
end
csar.displayMessageToSAR(_heli, _msg, 20)
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.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: "
for _,_onboard in pairs(csar.inTransitGroups[_unitName]) do
_text = _text .."\n".._onboard.desc
end
csar.displayMessageToSAR(_unit,_text , 30)
end
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)
for _, _unitName in pairs(csar.csarUnits) do
local _unit = csar.getSARHeli(_unitName)
if _unit ~= nil then
local _groupId = _unit:getGroup():getID()
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)
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
csar.generateVHFrequencies()
-- Schedule timer to add radio item
timer.scheduleFunction(csar.addMedevacMenuItem, nil, timer.getTime() + 5)
world.addEventHandler(csar.eventHandler)
env.info("Medevac event handler added")