diff --git a/changelog.md b/changelog.md index 3e93a0ce..414ae15a 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,7 @@ * **[Campaigns]** Reworked Syrian Civil War campaign (Added FOB and preset locations for SAMS) * **[Factions]** Updated Nato Desert Storm to include F-14A * **[Factions]** Updated Iraq 1991 factions to include Zsu-57 and Mig-29A +* **[Plugins]** [The EWRS plugin](https://github.com/Bob7heBuilder/EWRS) is now included. ## Fixes: * **[Factions]** AI would never buy artillery units for the frontline - fixed diff --git a/resources/plugins/ewrs/LICENSE b/resources/plugins/ewrs/LICENSE new file mode 100644 index 00000000..76590432 --- /dev/null +++ b/resources/plugins/ewrs/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Bob7heBuilder + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/resources/plugins/ewrs/ewrs.lua b/resources/plugins/ewrs/ewrs.lua new file mode 100644 index 00000000..b66b728a --- /dev/null +++ b/resources/plugins/ewrs/ewrs.lua @@ -0,0 +1,830 @@ +--[[ + Early Warning Radar Script - 1.5.3 - 07/11/2016 + + Allows use of units with radars to provide Bearing Range and Altitude information via text display to player aircraft + + Features: + - Uses in-game radar information to detect targets so terrain masking, beaming, low altitude flying, etc is effective for avoiding detection + - Dynamic. If valid units with radar are created during a mission (eg. via chopper with CTLD), they will be added to the EWRS radar network + - Can allow / disable BRA messages to fighters or sides + - Uses player aircraft or mission bullseye for BRA reference, can be changed via F10 radio menu or restricted to one reference in the script settings + - Can switch between imperial (feet, knots, NM) or metric (meters, km/h, km) measurements using F10 radio menu + - Ability to change the message display time and automated update interval + - Can choose to disable automated messages and allow players to request BRA from F10 menu + - Can allow players to request Bogey Dope at any time through F10 radio menu + + Built and Tested in DCS 1.5 - See https://github.com/Bob7heBuilder/EWRS for the latest version + + This script uses MIST 4.0.57 or later - https://github.com/mrSkortch/MissionScriptingTools + + At the moment, because of limitations within DCS to not show messages to individual units, the reference, measurements, and messages + are done per group. So a group of 4 fighters will each receive 4 BRA messages. Each message however, will have the player's name + in it, that its refering to. Its unfortunate, but nothing I can do about it. + + Changes: + - 1.3 - Added Option to allow picture report to be requested thru F10 menu instead of an automated display + - Fixed bug where a known unit type would sometimes still display as ??? + - 1.4 - Added setting to be able to limit the amount of threats displayed in a picture report + - Added option to enable Bogey Dopes + * Mission designer can turn on / off in script settings + * Pilots can request thru the F10 menu and it will show the BRA to the nearest hostile aircraft that has + been detected. It will always reference the requesting pilot's own aircraft. + - Finally implemented a cleaner workaround for some ground units being detected and listed in picture report + - 1.4.1 - Added some ships to search radar list, you will need to remove the comment markers (--) at the start of the line to activate + - 1.5 - Added ability to request picture of friendly aircraft positions referencing your own aircraft - Mission designer chooses if this feature is active or not + - 1.5.1 - Added Gazelle to acCategories + - 1.5.2 - Added F5E to acCategories + - 1.5.3 - Fixed bug with maxThreatDisplay set at 0 not displaying any threats + - Added Mistral Gazelle + - Added C-101CC +]] + +ewrs = {} --DO NOT REMOVE +ewrs.HELO = 1 +ewrs.ATTACK = 2 +ewrs.FIGHTER = 3 + +----SCRIPT OPTIONS---- + +ewrs.messageUpdateInterval = 30 --How often EWRS will update automated BRA messages (seconds) +ewrs.messageDisplayTime = 25 --How long EWRS BRA messages will show for (seconds) +ewrs.restrictToOneReference = false -- Disables the ability to change the BRA calls from pilot's own aircraft or bullseye. If this is true, set ewrs.defaultReference to the option you want to restrict to. +ewrs.defaultReference = "self" --The default reference for BRA calls - can be changed via f10 radio menu if ewrs.restrictToOneReference is false (self or bulls) +ewrs.defaultMeasurements = "imperial" --Default measurement units - can be changed via f10 radio menu (imperial or metric) +ewrs.disableFightersBRA = false -- disables BRA messages to fighters when true +ewrs.enableRedTeam = true -- enables / disables EWRS for the red team +ewrs.enableBlueTeam = true -- enables / disables EWRS for the blue team +ewrs.disableMessageWhenNoThreats = true -- disables message when no threats are detected - Thanks Rivvern - NOTE: If using ewrs.onDemand = true, this has no effect +ewrs.useImprovedDetectionLogic = true --this makes the messages more realistic. If the radar doesn't know the type or distance to the detected threat, it will be reflected in the picture report / BRA message +ewrs.onDemand = false --Setting to true will disable the automated messages to everyone and will add an F10 menu to get picture / BRA message. +ewrs.maxThreatDisplay = 5 -- Max amounts of threats to display on picture report (0 will display all) +ewrs.allowBogeyDope = true -- Allows pilots to request a bogey dope even with the automated messages running. It will display only the cloest threat, and will always reference the players own aircraft. +ewrs.allowFriendlyPicture = true -- Allows pilots to request picture of friendly aircraft +ewrs.maxFriendlyDisplay = 5 -- Limits the amount of friendly aircraft shown on friendly picture + +--[[ +Units with radar to use as part of the EWRS radar network +If you want to shorten the range of SAM radar detection, use their track radars instead of their search radars +NOTE that track radars require a search radar to detect targets (but the search radars do not need to be included in the list) +I haven't tested detection with ships (that have radar), but should work. +]] +ewrs.validSearchRadars = { +"p-10 s125 sr", --SA-3 Search Radar +"Kub 1S91 str", --SA-6 Search and Track Radar +"S-300PS 64H6E sr", --SA-10 Search Radar +"S-300 PS 40B6MD sr", --SA-10 Search Radar +"SA-11 Buk SR 9518M1", --SA-11 Search Radar +"55G6 EWR", --Early Warning Radar +"1L13 EWR", --Early Warning Radar +"A-50", --AWACS +"E-2D", --AWACS +"E-3A", --AWACS +"Roland Radar", --Roland Search Radar +"Hawk sr", --Hawk SAM Search Radar +"Patriot str", --Patriot SAM Search and Track Radar + +--Ships - I've left these commented out because I don't know which ones have radar +--and which ones don't. Just remove the comment to activate them (the '--' at the start of the line) + +--"MOSCOW", -- CG 1164 Moskva +--"TICONDEROG", -- CG-60 Normandy +--"PIOTR", -- CGN 1144.2 Pyotr Velikiy +--"KUZNECOW", -- CV 1143.5 Admiral Kuzentsov +--"VINSON", -- CVN-70 Carl Vinson +--"REZKY", -- FF 1135M Rezky +--"NEUSTRASH", -- FFG 11540 Neustrashimy +--"PERRY", -- FFG-7CL Oliver Hazzard Perry +--"ALBATROS", -- FFL 1124.4 Grisha +--"MOLNIYA", -- FSG 1241.1MP Molniya +} + +--[[ +Aircraft Type ENUMs +This is used to restrict BRA messages to fighters +Change the ewrs.TYPE for each aircraft (in ewrs.acCategories) to suit your needs +For now, BRA will be displayed to everyone unless ewrs.disableFightersBRA is true. +When this is true, anything in the list below that == ewrs.FIGHTER will NOT receive BRA messages +]] +ewrs.acCategories = { --Have I left anything out? Please let me know if I have +[ "A-10A" ] = ewrs.ATTACK , +[ "A-10C" ] = ewrs.ATTACK , +[ "Bf-109K-4" ] = ewrs.ATTACK , +[ "C-101EB" ] = ewrs.ATTACK , +[ "C-101CC" ] = ewrs.ATTACK , +[ "F-15C" ] = ewrs.FIGHTER , +[ "F-5E-3" ] = ewrs.ATTACK , +[ "FW-190D9" ] = ewrs.ATTACK , +[ "F-86F Sabre" ] = ewrs.FIGHTER , +[ "Hawk" ] = ewrs.ATTACK , +[ "Ka-50" ] = ewrs.HELO , +[ "L-39C" ] = ewrs.ATTACK , +[ "L-39ZA" ] = ewrs.ATTACK , +[ "Mi-8MT" ] = ewrs.HELO , +[ "MiG-15bis" ] = ewrs.ATTACK , +[ "MiG-21Bis" ] = ewrs.ATTACK , +[ "MiG-29A" ] = ewrs.FIGHTER , +[ "MiG-29S" ] = ewrs.FIGHTER , +[ "M-2000C" ] = ewrs.FIGHTER , +[ "P-51D" ] = ewrs.ATTACK , +[ "SA342M" ] = ewrs.HELO , +[ "SA342L" ] = ewrs.HELO , +[ "SA342Mistral" ] = ewrs.HELO , +[ "Su-25" ] = ewrs.ATTACK , +[ "Su-25T" ] = ewrs.ATTACK , +[ "Su-27" ] = ewrs.FIGHTER , +[ "Su-33" ] = ewrs.FIGHTER , +[ "TF-51D" ] = ewrs.ATTACK , +[ "UH-1H" ] = ewrs.HELO , +} + +----END OF SCRIPT OPTIONS---- + + +----INTERNAL FUNCTIONS ***** Be Careful changing things below here ***** ---- + + +function ewrs.getDistance(obj1PosX, obj1PosZ, obj2PosX, obj2PosZ) + local xDiff = obj1PosX - obj2PosX + local yDiff = obj1PosZ - obj2PosZ + return math.sqrt(xDiff * xDiff + yDiff * yDiff) -- meters +end + +function ewrs.getBearing(obj1PosX, obj1PosZ, obj2PosX, obj2PosZ) + local bearing = math.atan2(obj2PosZ - obj1PosZ, obj2PosX - obj1PosX) + if bearing < 0 then + bearing = bearing + 2 * math.pi + end + bearing = bearing * 180 / math.pi + return bearing -- degrees +end + +function ewrs.getHeading(vec) + local heading = math.atan2(vec.z,vec.x) + if heading < 0 then + heading = heading + 2 * math.pi + end + heading = heading * 180 / math.pi + return heading -- degrees +end + +function ewrs.getSpeed(velocity) + local speed = math.sqrt(velocity.x^2 + velocity.y^2 + velocity.z^2) --m/s + return speed -- m/s +end + +function ewrs.update() + timer.scheduleFunction(ewrs.update, nil, timer.getTime() + 5) + ewrs.buildActivePlayers() + ewrs.buildF10Menu() +end + +function ewrs.buildThreatTable(activePlayer, bogeyDope) + local function sortRanges(v1,v2) + return v1.range < v2.range + end + + local targets = {} + if activePlayer.side == 2 then + targets = ewrs.currentlyDetectedRedUnits + else + targets = ewrs.currentlyDetectedBlueUnits + end + + local bogeyDope = bogeyDope or false + local referenceX + local referenceZ + if ewrs.groupSettings[tostring(activePlayer.groupID)].reference == "self" or bogeyDope then + local _self = Unit.getByName(activePlayer.unitname) + local selfpos = _self:getPosition() + referenceX = selfpos.p.x + referenceZ = selfpos.p.z + else + local bullseye = coalition.getMainRefPoint(activePlayer.side) + referenceX = bullseye.x + referenceZ = bullseye.z + end + + local threatTable = {} + + for k,v in pairs(targets) do + local velocity = v["object"]:getVelocity() + local bogeypos = v["object"]:getPosition() + local bogeyType = nil + if ewrs.useImprovedDetectionLogic then + if v["type"] then + bogeyType = v["object"]:getTypeName() + else + bogeyType = " ??? " + end + else + bogeyType = v["object"]:getTypeName() + end + + local bearing = ewrs.getBearing(referenceX,referenceZ,bogeypos.p.x,bogeypos.p.z) + local heading = ewrs.getHeading(velocity) + local range = ewrs.getDistance(referenceX,referenceZ,bogeypos.p.x,bogeypos.p.z) -- meters + local altitude = bogeypos.p.y --meters + local speed = ewrs.getSpeed(velocity) --m/s + + if ewrs.groupSettings[tostring(activePlayer.groupID)].measurements == "metric" then + range = range / 1000 --change to KM + speed = mist.utils.mpsToKmph(speed) + --altitude already in meters + else + range = mist.utils.metersToNM(range) + speed = mist.utils.mpsToKnots(speed) + altitude = mist.utils.metersToFeet(altitude) + end + + if ewrs.useImprovedDetectionLogic then + if not v["distance"] then + range = ewrs.notAvailable + end + end + + local j = #threatTable + 1 + threatTable[j] = {} + threatTable[j].unitType = bogeyType + threatTable[j].bearing = bearing + threatTable[j].range = range + threatTable[j].altitude = altitude + threatTable[j].speed = speed + threatTable[j].heading = heading + end + + table.sort(threatTable,sortRanges) + + return threatTable +end + +function ewrs.outText(activePlayer, threatTable, bogeyDope, greeting) + local status, result = pcall(function() + + local message = {} + local altUnits + local speedUnits + local rangeUnits + local bogeyDope = bogeyDope or false + if ewrs.groupSettings[tostring(activePlayer.groupID)].measurements == "metric" then + altUnits = "m" + speedUnits = "Km/h" + rangeUnits = "Km" + else + altUnits = "ft" + speedUnits = "Knts" + rangeUnits = "NM" + end + + if #threatTable >= 1 then + local maxThreats = nil + local messageGreeting = nil + if greeting == nil then + if bogeyDope then + maxThreats = 1 + messageGreeting = "EWRS Bogey Dope for: " .. activePlayer.player + else + if ewrs.maxThreatDisplay == 0 then + maxThreats = 999 + else + maxThreats = ewrs.maxThreatDisplay + end + messageGreeting = "EWRS Picture Report for: " .. activePlayer.player .. " -- Reference: " .. ewrs.groupSettings[tostring(activePlayer.groupID)].reference + end + else + messageGreeting = greeting + maxThreats = ewrs.maxFriendlyDisplay + end + + --Display table + table.insert(message, "\n") + table.insert(message, messageGreeting) + table.insert(message, "\n\n") + table.insert(message, string.format( "%-16s", "TYPE")) + table.insert(message, string.format( "%-12s", "BRG")) + table.insert(message, string.format( "%-12s", "RNG")) + table.insert(message, string.format( "%-21s", "ALT")) + table.insert(message, string.format( "%-15s", "SPD")) + table.insert(message, string.format( "%-3s", "HDG")) + table.insert(message, "\n") + + for k = 1, maxThreats do + if threatTable[k] == nil then break end + table.insert(message, "\n") + table.insert(message, string.format( "%-16s", threatTable[k].unitType)) + if threatTable[k].range == ewrs.notAvailable then + table.insert(message, string.format( "%-12s", " ")) + table.insert(message, string.format( "%-12s", "POSITION")) + table.insert(message, string.format( "%-21s", " ")) + table.insert(message, string.format( "%-15s", "UNKNOWN")) + table.insert(message, string.format( "%-3s", " ")) + else + table.insert(message, string.format( "%03d", threatTable[k].bearing)) + table.insert(message, string.format( "%8.1f %s", threatTable[k].range, rangeUnits)) + table.insert(message, string.format( "%9d %s", threatTable[k].altitude, altUnits)) + table.insert(message, string.format( "%9d %s", threatTable[k].speed, speedUnits)) + table.insert(message, string.format( " %03d", threatTable[k].heading)) + end + table.insert(message, "\n") + end + trigger.action.outTextForGroup(activePlayer.groupID, table.concat(message), ewrs.messageDisplayTime) + else + if (not ewrs.disableMessageWhenNoThreats) or (ewrs.onDemand) and greeting == nil then + trigger.action.outTextForGroup(activePlayer.groupID, "\nEWRS Picture Report for: " .. activePlayer.player .. "\n\nNo targets detected", ewrs.messageDisplayTime) + end + if greeting ~= nil then + trigger.action.outTextForGroup(activePlayer.groupID, "\nEWRS Friendly Picture for: " .. activePlayer.player .. "\n\nNo friendlies detected", ewrs.messageDisplayTime) + end + end + end) + if not status then + env.error(string.format("EWRS outText Error: %s", result)) + end +end + +function ewrs.displayMessageToAll() + local status, result = pcall(function() + timer.scheduleFunction(ewrs.displayMessageToAll, nil, timer.getTime() + ewrs.messageUpdateInterval) + ewrs.findRadarUnits() + ewrs.getDetectedTargets() + for i = 1, #ewrs.activePlayers do + if ewrs.groupSettings[tostring(ewrs.activePlayers[i].groupID)].messages then + if ewrs.activePlayers[i].side == 1 and #ewrs.redEwrUnits > 0 or ewrs.activePlayers[i].side == 2 and #ewrs.blueEwrUnits > 0 then + ewrs.outText(ewrs.activePlayers[i], ewrs.buildThreatTable(ewrs.activePlayers[i])) + end -- if ewrs.activePlayers[i].side == 1 and #ewrs.redEwrUnits > 0 or ewrs.activePlayers[i].side == 2 and #ewrs.blueEwrUnits > 0 then + end -- if ewrs.groupSettings[tostring(ewrs.activePlayers[i].groupID)].messages then + end -- for i = 1, #ewrs.activePlayers do + end) + if not status then + env.error(string.format("EWRS displayMessageToAll Error: %s", result)) + end +end + +function ewrs.onDemandMessage(args) + local status, result = pcall(function() + ewrs.findRadarUnits() + ewrs.getDetectedTargets() + for i = 1, #ewrs.activePlayers do + if ewrs.activePlayers[i].groupID == args[1] then + ewrs.outText(ewrs.activePlayers[i], ewrs.buildThreatTable(ewrs.activePlayers[i],args[2]),args[2]) + end + end + end) + if not status then + env.error(string.format("EWRS onDemandMessage Error: %s", result)) + end +end + +function ewrs.buildFriendlyTable(friendlyNames,activePlayer) + local function sortRanges(v1,v2) + return v1.range < v2.range + end + + local units = {} + for i =1, #friendlyNames do + local unit = Unit.getByName(friendlyNames[i]) + if unit ~= nil and unit:isActive() then + + table.insert(units,unit) + else + --env.error("Friendly Picture - Unit not found: "..friendlyNames[i]) -- Client Planes that are not active will fall into here. + end + end + + local _self = Unit.getByName(activePlayer.unitname) + local selfpos = _self:getPosition() + local referenceX = selfpos.p.x + local referenceZ = selfpos.p.z + + local friendlyTable = {} + + for k,v in pairs(units) do + local velocity = v:getVelocity() + local pos = v:getPosition() + local bogeyType = v:getTypeName() + if pos.p.x ~= selfpos.p.x and pos.p.z ~= selfpos.p.z then --same position as self, means its you! + + local bearing = ewrs.getBearing(referenceX,referenceZ,pos.p.x,pos.p.z) + local heading = ewrs.getHeading(velocity) + local range = ewrs.getDistance(referenceX,referenceZ,pos.p.x,pos.p.z) -- meters + local altitude = pos.p.y --meters + local speed = ewrs.getSpeed(velocity) --m/s + + if ewrs.groupSettings[tostring(activePlayer.groupID)].measurements == "metric" then + range = range / 1000 --change to KM + speed = mist.utils.mpsToKmph(speed) + --altitude already in meters + else + range = mist.utils.metersToNM(range) + speed = mist.utils.mpsToKnots(speed) + altitude = mist.utils.metersToFeet(altitude) + end + + local j = #friendlyTable + 1 + friendlyTable[j] = {} + friendlyTable[j].unitType = bogeyType + friendlyTable[j].bearing = bearing + friendlyTable[j].range = range + friendlyTable[j].altitude = altitude + friendlyTable[j].speed = speed + friendlyTable[j].heading = heading + else + --env.info("Friendly Picture - Found Self") + end + end + + table.sort(friendlyTable,sortRanges) + + return friendlyTable +end + +function ewrs.friendlyPicture(args) + local status, result = pcall(function() + for i = 1, #ewrs.activePlayers do + if ewrs.activePlayers[i].groupID == args[1] then + local sideString = nil + if ewrs.activePlayers[i].side == 1 then + sideString = "[red]" + else + sideString = "[blue]" + end + local filter = {sideString.."[helicopter]", sideString.."[plane]"} + local friendlies = mist.makeUnitTable(filter) --find a way to do this only once if there is more then 1 person in a group + local friendlyTable = ewrs.buildFriendlyTable(friendlies,ewrs.activePlayers[i]) + local greeting = "EWRS Friendly Picture for: " .. ewrs.activePlayers[i].player + ewrs.outText(ewrs.activePlayers[i],friendlyTable,false,greeting) + end + end + end) + if not status then + env.error(string.format("EWRS friendlyPicture Error: %s", result)) + end +end + +function ewrs.buildActivePlayers() + local status, result = pcall(function() + local filter = { "[all][plane]","[all][helicopter]"} + local all_vecs = mist.makeUnitTable(filter) + ewrs.activePlayers = {} + for i = 1, #all_vecs do + local vec = Unit.getByName(all_vecs[i]) + if vec ~= nil and Unit.isActive(vec) then + playerName = Unit.getPlayerName(vec) + local groupID = ewrs.getGroupId(vec) + if playerName ~= nil then + unitCategory = ewrs.acCategories[Unit.getTypeName(vec)] + if ewrs.disableFightersBRA and unitCategory == ewrs.FIGHTER then + --DONT DO ANYTHING + else + local group = Unit.getGroup(vec) + if ewrs.enableBlueTeam and Unit.getCoalition(vec) == 2 then + ewrs.addPlayer(playerName, groupID, vec) + elseif ewrs.enableRedTeam and Unit.getCoalition(vec) == 1 then + ewrs.addPlayer(playerName, groupID, vec) + end + end + end + end + end + end) -- pcall + + if not status then + env.error(string.format("EWRS buildActivePlayers Error: %s", result)) + end +end + +--THANK YOU ciribob http://forums.eagle.ru/showpost.php?p=2499638&postcount=5 +--And Stonehouse for pointing me there +function ewrs.getGroupId(_unit) --Temp fix for client groups not being accessable + + local _unitDB = mist.DBs.unitsById[tonumber(_unit:getID())] + if _unitDB ~= nil and _unitDB.groupId then + return _unitDB.groupId + end + + return nil +end + +function ewrs.getGroupCategory(unit) + local unitDB = mist.DBs.unitsById[tonumber(unit:getID())] + if unitDB ~= nil and unitDB.category then + return unitDB.category + end + + return nil +end + +function ewrs.addPlayer(playerName, groupID, unit ) + local status, result = pcall(function() + local i = #ewrs.activePlayers + 1 + ewrs.activePlayers[i] = {} + ewrs.activePlayers[i].player = playerName + ewrs.activePlayers[i].groupID = groupID + ewrs.activePlayers[i].unitname = unit:getName() + ewrs.activePlayers[i].side = unit:getCoalition() + + -- add default settings to settings table if it hasn't been done yet + if ewrs.groupSettings[tostring(groupID)] == nil then + ewrs.addGroupSettings(tostring(groupID)) + end + end) + if not status then + env.error(string.format("EWRS addPlayer Error: %s", result)) + end +end + +-- filters units so ones detected by multiple radar sites still only get listed once +-- Filters out anything that isn't a plane or helicopter +function ewrs.filterUnits(units) + local newUnits = {} + for k,v in pairs(units) do + local valid = true + if v["object"]:getCategory() ~= Object.Category.UNIT then --rare but i've had it detect missiles + valid = false + end + if valid then --another check cause it seems AI radar can detected some ground units + local category = ewrs.getGroupCategory(v["object"]) + if category ~= "plane" and category ~= "helicopter" then valid = false end + end + + if valid then + for nk,nv in pairs (newUnits) do --recursive loop, can't see a way around this + if v["object"]:getName() == nv["object"]:getName() then + valid = false + --update already found unit incase the first detection(s) didn't know type or distance + if v["type"] then + nv["type"] = true + end + if v["distance"] then + nv["distance"] = true + end + end + end + end + + if valid then + table.insert(newUnits, v) + end + end + return newUnits +end + +function ewrs.getDetectedTargets() + if #ewrs.blueEwrUnits > 0 then + ewrs.currentlyDetectedRedUnits = ewrs.findDetectedTargets("red") + end + if #ewrs.redEwrUnits > 0 then + ewrs.currentlyDetectedBlueUnits = ewrs.findDetectedTargets("blue") + end +end + +function ewrs.findDetectedTargets(side) + local units = {} + local ewrUnits = {} + + if side == "red" then + ewrUnits = ewrs.blueEwrUnits + elseif side == "blue" then + ewrUnits = ewrs.redEwrUnits + end + + for n = 1, #ewrUnits do + local ewrUnit = Unit.getByName(ewrUnits[n]) + if ewrUnit ~= nil then + local ewrControl = ewrUnit:getGroup():getController() + local detectedTargets = ewrControl:getDetectedTargets(Controller.Detection.RADAR) + for k,v in pairs (detectedTargets) do + table.insert(units, v) + end + end + end + return ewrs.filterUnits(units) +end + +function ewrs.findRadarUnits() + local filter = {} + if ewrs.enableBlueTeam and ewrs.enableRedTeam then + filter = { "[all][plane]", "[all][vehicle]", "[all][ship]"} + elseif ewrs.enableBlueTeam then + filter = { "[blue][plane]", "[blue][vehicle]", "[blue][ship]"} + elseif ewrs.enableRedTeam then + filter = { "[red][plane]", "[red][vehicle]", "[red][ship]"} + end + local all_vecs = mist.makeUnitTable(filter) + local redUnits = {} + local blueUnits = {} + + for i = 1, #all_vecs do + local vec = Unit.getByName(all_vecs[i]) + + if vec ~= nil then + if Unit.isActive(vec) then + local vec_type = Unit.getTypeName(vec) + for n = 1, #ewrs.validSearchRadars do + if ewrs.validSearchRadars[n] == vec_type and Unit.getCoalition(vec) == 2 then + table.insert(blueUnits, Unit.getName(vec)) + break + end + if ewrs.validSearchRadars[n] == vec_type and Unit.getCoalition(vec) == 1 then + table.insert(redUnits, Unit.getName(vec)) + break + end + end --for n = 1, #ewrs.validSearchRadars do + end --if Unit.isActive(vec) then + end --if vec ~= nil then + end --for i = 1, #all_vecs do + ewrs.blueEwrUnits = blueUnits + ewrs.redEwrUnits = redUnits +end + +function ewrs.addGroupSettings(groupID) + ewrs.groupSettings[groupID] = {} + ewrs.groupSettings[groupID].reference = ewrs.defaultReference + ewrs.groupSettings[groupID].measurements = ewrs.defaultMeasurements + ewrs.groupSettings[groupID].messages = true +end + +function ewrs.setGroupReference(args) + local groupID = args[1] + ewrs.groupSettings[tostring(groupID)].reference = args[2] + trigger.action.outTextForGroup(groupID,"Reference changed to "..args[2],ewrs.messageDisplayTime) +end + +function ewrs.setGroupMeasurements(args) + local groupID = args[1] + ewrs.groupSettings[tostring(groupID)].measurements = args[2] + trigger.action.outTextForGroup(groupID,"Measurement units changed to "..args[2],ewrs.messageDisplayTime) +end + +function ewrs.setGroupMessages(args) + local groupID = args[1] + local onOff + if args[2] then onOff = "on" else onOff = "off" end + ewrs.groupSettings[tostring(groupID)].messages = args[2] + trigger.action.outTextForGroup(groupID,"Picture reports for group turned "..onOff,ewrs.messageDisplayTime) +end + +function ewrs.buildF10Menu() + local status, result = pcall(function() + for i = 1, #ewrs.activePlayers do + local groupID = ewrs.activePlayers[i].groupID + local stringGroupID = tostring(groupID) + if ewrs.builtF10Menus[stringGroupID] == nil then + local rootPath = missionCommands.addSubMenuForGroup(groupID, "EWRS") + + if ewrs.allowBogeyDope then + missionCommands.addCommandForGroup(groupID, "Request Bogey Dope",rootPath,ewrs.onDemandMessage,{groupID,true}) + end + + if ewrs.onDemand then + missionCommands.addCommandForGroup(groupID, "Request Picture",rootPath,ewrs.onDemandMessage,{groupID}) + end + + if ewrs.allowFriendlyPicture then + missionCommands.addCommandForGroup(groupID, "Request Friendly Picture",rootPath,ewrs.friendlyPicture,{groupID}) + end + + if not ewrs.restrictToOneReference then + local referenceSetPath = missionCommands.addSubMenuForGroup(groupID,"Set GROUP's reference point", rootPath) + missionCommands.addCommandForGroup(groupID, "Set to Bullseye",referenceSetPath,ewrs.setGroupReference,{groupID, "bulls"}) + missionCommands.addCommandForGroup(groupID, "Set to Self",referenceSetPath,ewrs.setGroupReference,{groupID, "self"}) + end + + local measurementsSetPath = missionCommands.addSubMenuForGroup(groupID,"Set GROUP's measurement units",rootPath) + missionCommands.addCommandForGroup(groupID, "Set to Imperial (feet, knts)",measurementsSetPath,ewrs.setGroupMeasurements,{groupID, "imperial"}) + missionCommands.addCommandForGroup(groupID, "Set to Metric (meters, km/h)",measurementsSetPath,ewrs.setGroupMeasurements,{groupID, "metric"}) + + if not ewrs.onDemand then + local messageOnOffPath = missionCommands.addSubMenuForGroup(groupID, "Turn Picture Report On/Off",rootPath) + missionCommands.addCommandForGroup(groupID, "Message ON", messageOnOffPath, ewrs.setGroupMessages, {groupID, true}) + missionCommands.addCommandForGroup(groupID, "Message OFF", messageOnOffPath, ewrs.setGroupMessages, {groupID, false}) + end + + ewrs.builtF10Menus[stringGroupID] = true + end + end + end) + + if not status then + env.error(string.format("EWRS buildF10Menu Error: %s", result)) + end +end + +--temp fix for ground units being detected by radar - FIXED +--[[ Leaving this here just in case I introduce threat based filtering, saves me writing it again +ewrs.validThreats = { +["A-10A"] = true, +["A-10C"] = true, +["A-50"] = true, +["AH-1W"] = true, +["AH-64A"] = true, +["AH-64D"] = true, +["An-26B"] = true, +["An-30M"] = true, +["B-1B"] = true, +["B-52H"] = true, +["Bf-109K-4"] = true, +["C-101CC"] = true, +["C-101EB"] = true, +["C-130"] = true, +["C-17A"] = true, +["CH-47D"] = true, +["CH-53E"] = true, +["E-2D"] = true, +["E-2C"] = true, +["E-3A"] = true, +["F-111F"] = true, +["F-117A"] = true, +["F-14A"] = true, +["F-15C"] = true, +["F-15E"] = true, +["F-16A"] = true, +["F-16A MLU"] = true, +["F-16C bl.50"] = true, +["F-16C bl.52d"] = true, +["F-4E"] = true, +["F-5E"] = true, +["F-5E-3"] = true, +["F-86F Sabre"] = true, +["F/A-18A"] = true, +["F/A-18C"] = true, +["FW-190D9"] = true, +["Hawk"] = true, +["IL-76MD"] = true, +["IL-78M"] = true, +["KC-135"] = true, +["Ka-27"] = true, +["Ka-50"] = true, +["Ka-52"] = true, +["L-39C"] = true, +["L-39ZA"] = true, +["M-2000C"] = true, +["MQ-9 Reaper"] = true, +["Mi-24V"] = true, +["Mi-26"] = true, +["Mi-28N"] = true, +["Mi-8MT"] = true, +["MiG-15bis"] = true, +["MiG-21Bis"] = true, +["MiG-23MLD"] = true, +["MiG-25PD"] = true, +["MiG-25RBT"] = true, +["MiG-27K"] = true, +["MiG-29A"] = true, +["MiG-29G"] = true, +["MiG-29K"] = true, +["MiG-29S"] = true, +["MiG-31"] = true, +["Mirage 2000-5"] = true, +["OH-58D"] = true, +["P-51D"] = true, +["RQ-1A Predator"] = true, +["S-3B"] = true, +["S-3B Tanker"] = true, +["SH-3W"] = true, +["SH-60B"] = true, +["Su-17M4"] = true, +["Su-24M"] = true, +["Su-24MR"] = true, +["Su-25"] = true, +["Su-25T"] = true, +["Su-25TM"] = true, +["Su-27"] = true, +["Su-30"] = true, +["Su-33"] = true, +["Su-34"] = true, +["TF-51D"] = true, +["Tornado GR4"] = true, +["Tornado IDS"] = true, +["Tu-142"] = true, +["Tu-160"] = true, +["Tu-22M3"] = true, +["Tu-95MS"] = true, +["UH-1H"] = true, +["UH-60A"] = true, +["Yak-40"] = true, +} +]] + +--SCRIPT INIT +ewrs.currentlyDetectedRedUnits = {} +ewrs.currentlyDetectedBlueUnits = {} +ewrs.redEwrUnits = {} +ewrs.blueEwrUnits = {} +ewrs.activePlayers = {} +ewrs.groupSettings = {} +ewrs.builtF10Menus = {} +ewrs.notAvailable = 999999 + +ewrs.update() +if not ewrs.onDemand then + timer.scheduleFunction(ewrs.displayMessageToAll, nil, timer.getTime() + ewrs.messageUpdateInterval) +end +trigger.action.outText("EWRS by Steggles is now running",15) +env.info("EWRS Running") + +--[[ +TODO: + - Add check on friendly picture to not give one if no AWACS / EWR units are active. Doesn't use radar info anyway. Maybe just leave it to help out people with SA? Feedback Please!! + - Clean up functions and arguments from bogeyDope and friendly picture additions + - Threat based filtering if theres interest. +]] \ No newline at end of file diff --git a/resources/plugins/ewrs/plugin.json b/resources/plugins/ewrs/plugin.json new file mode 100644 index 00000000..8787db14 --- /dev/null +++ b/resources/plugins/ewrs/plugin.json @@ -0,0 +1,12 @@ +{ + "nameInUI": "EWRS", + "defaultValue": false, + "specificOptions": [], + "scriptsWorkOrders": [ + { + "file": "ewrs.lua", + "mnemonic": "ewrs" + } + ], + "configurationWorkOrders": [] +} \ No newline at end of file diff --git a/resources/plugins/plugins.json b/resources/plugins/plugins.json index 377cda18..9b415eea 100644 --- a/resources/plugins/plugins.json +++ b/resources/plugins/plugins.json @@ -2,5 +2,6 @@ "base", "jtacautolase", "skynetiads", + "ewrs", "herculescargo" ]