diff --git a/Script/Script.lua b/Script/Script.lua index b05ff78..6571ee5 100644 --- a/Script/Script.lua +++ b/Script/Script.lua @@ -149,7 +149,7 @@ do if TUM.airForce.onClockTick(TUM.settings.getEnemyCoalition()) then return nextTickTime end end - if TUM.wingmen.onClockTick() then return nextTickTime end + if TUM.wingmenContacts.onClockTick() then return nextTickTime end -- Called every tick if no other action has taken place return nextTickTime end diff --git a/Script/The Universal Mission/Wingmen.lua b/Script/The Universal Mission/Wingmen.lua index f28ab3a..88fb6cc 100644 --- a/Script/The Universal Mission/Wingmen.lua +++ b/Script/The Universal Mission/Wingmen.lua @@ -6,12 +6,8 @@ TUM.wingmen = {} do - local CONTACT_REPORT_INTERVAL = 8 -- Called AT MOST every 15 seconds, so 6 means "AT MOST every two minutes" local DEFAULT_PAYLOAD = "attack" -- Default payload - local knownGroupsID = {} - local newGroupsID = {} - local ticksLeftBeforeContactReport = CONTACT_REPORT_INTERVAL local wingmenGroupID = nil local wingmenUnitID = {} @@ -93,9 +89,7 @@ do wingmenUnitID = DCSEx.table.deepCopy(groupInfo.unitsID) -- Reinitialize list of known contacts and contact report interval - knownGroupsID = {} - newGroupsID = {} - ticksLeftBeforeContactReport = CONTACT_REPORT_INTERVAL + TUM.wingmenContacts.clearKnownContacts() TUM.log("Spawned AI wingmen") @@ -103,110 +97,6 @@ do TUM.wingmenTasking.commandRejoin(nil, true, true) -- Task the new wingmen to rejoin end - function TUM.wingmen.getContacts(groupCategory) - if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return {} end -- No wingmen in multiplayer - if TUM.mission.getStatus() == TUM.mission.status.NONE then return {} end - local wingmenGroup = TUM.wingmen.getGroup() - if not wingmenGroup then return {} end - - local searchPoint = DCSEx.world.getGroupCenter(wingmenGroup) - - -- Take into account better sensors (radars, TGPs...) in later periods - local detectionRangeMultiplier = 1.0 - if TUM.settings.getValue(TUM.settings.id.TIME_PERIOD) == DCSEx.enums.timePeriod.MODERN then - detectionRangeMultiplier = 1.5 - elseif TUM.settings.getValue(TUM.settings.id.TIME_PERIOD) == DCSEx.enums.timePeriod.COLD_WAR then - detectionRangeMultiplier = 1.25 - end - - local detectedTargets = {} - local allGroups = coalition.getGroups(TUM.settings.getEnemyCoalition(), groupCategory) - for _,g in ipairs(allGroups) do - local gID = g:getID() - if g:isExist() and g:getSize() > 0 then - local gPos = DCSEx.world.getGroupCenter(g) - local gCateg = Group.getCategory(g) - - local detectionRange = DCSEx.converter.nmToMeters(20 * detectionRangeMultiplier) - if gCateg == Group.Category.AIRPLANE then - detectionRange = DCSEx.converter.nmToMeters(50 * detectionRangeMultiplier) - elseif gCateg == Group.Category.SHIP then - detectionRange = DCSEx.converter.nmToMeters(30 * detectionRangeMultiplier) - local allSpeedboats = true - for _,u in ipairs(g:getUnits()) do - if not u:getTypeName() == "speedboat" then allSpeedboats = false end - end - if allSpeedboats then detectionRange = detectionRange / 8 end -- Speedboats are HARD to spot - elseif gCateg == Group.Category.GROUND then - local allInfantry = true - local airDefenseCount = 0 - for _,u in ipairs(g:getUnits()) do - if not u:hasAttribute("Infantry") then allInfantry = false end - if u:hasAttribute("Air Defence") then airDefenseCount = airDefenseCount + 1 end - end - if allInfantry then detectionRange = detectionRange / 8 end -- Infantry is HARD to spot - end - - local distanceToGroup = DCSEx.math.getDistance2D(gPos, searchPoint) - if distanceToGroup <= detectionRange then -- Check if wingman group is in detection range - if not DCSEx.table.contains(knownGroupsID, gID) then - table.insert(knownGroupsID, gID) - table.insert(newGroupsID, gID) - end - - local groupInfo = { - id = gID, - point2 = gPos, - size = g:getSize(), - type = "unknown" - } - - if gCateg == Group.Category.AIRPLANE or gCateg == Group.Category.HELICOPTER then - if distanceToGroup < detectionRange / 2 then -- Return exact type when aircraft is close enough - groupInfo.type = Library.objectNames.get(g:getUnit(1)) - else - groupInfo.type = Library.objectNames.getGenericGroup(g) - end - else - if distanceToGroup > 2 * detectionRange / 3 then - groupInfo.type = Library.objectNames.getGenericGroup(g, true) - elseif distanceToGroup > detectionRange / 3 then - groupInfo.type = Library.objectNames.getGenericGroup(g, false) - else - groupInfo.type = Library.objectNames.get(g:getUnit(1)) - end - end - - table.insert(detectedTargets, groupInfo) - end - end - end - - return detectedTargets - end - - function TUM.wingmen.getContactsAsReportString(groupCategory, giveRelativePosition, newContactsOnly) - giveRelativePosition = giveRelativePosition or false - newContactsOnly = newContactsOnly or false - - if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return nil end -- No wingmen in multiplayer - local contacts = TUM.wingmen.getContacts(groupCategory) - if not contacts or #contacts == 0 then return nil end - - local refPoint = DCSEx.world.getGroupCenter(TUM.wingmen.getGroup()) - - local reportText = "" - for _,t in ipairs(contacts) do - if not newContactsOnly or DCSEx.table.contains(newGroupsID, t.id) then - reportText = reportText.."\n - "..tostring(t.size).."x "..t.type - if refPoint and giveRelativePosition then - reportText = reportText..", "..DCSEx.dcs.getBRAA(t.point2, refPoint, false, false, false) - end - end - end - return reportText - end - function TUM.wingmen.getController() local wingmenGroup = TUM.wingmen.getGroup() if not wingmenGroup then return nil end @@ -252,37 +142,6 @@ do wingmenUnitID = {} end - function TUM.wingmen.updateContacts() - if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return nil end -- No wingmen in multiplayer - if not wingmenGroupID then return end - end - - ---------------------------------------------------------- - -- Called on every mission update tick (every 10-20 seconds) - -- @return True if something was done this tick, false otherwise - ---------------------------------------------------------- - function TUM.wingmen.onClockTick() - if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return false end -- No wingmen in multiplayer - if TUM.mission.getStatus() == TUM.mission.status.NONE then return false end - - TUM.wingmen.getContacts() -- Check for new contacts, just in case - if #newGroupsID > 0 then -- New contacts found, alert the player! - local newContactsReportString = TUM.wingmen.getContactsAsReportString(nil, true, true) - TUM.radio.playForAll("pilotWingmanReportContactsNew", { TUM.wingmen.getFirstWingmanNumber(), newContactsReportString }, TUM.wingmen.getFirstWingmanCallsign(), false) - newGroupsID = {} - if ticksLeftBeforeContactReport < math.floor(CONTACT_REPORT_INTERVAL / 2) then - ticksLeftBeforeContactReport = math.floor(CONTACT_REPORT_INTERVAL / 2) - end - return true - end - - ticksLeftBeforeContactReport = ticksLeftBeforeContactReport - 1 - if ticksLeftBeforeContactReport > 0 then return false end - ticksLeftBeforeContactReport = CONTACT_REPORT_INTERVAL - - return TUM.wingmenTasking.commandReportContacts(nil, true, false) - end - ------------------------------------- -- Called when an event is raised -- @param event The DCS World event diff --git a/Script/The Universal Mission/WingmenContacts.lua b/Script/The Universal Mission/WingmenContacts.lua new file mode 100644 index 0000000..0281563 --- /dev/null +++ b/Script/The Universal Mission/WingmenContacts.lua @@ -0,0 +1,153 @@ +-- ==================================================================================== +-- TUM.WINGMEN - HANDLES THE WINGMEN'S CONTACTS +-- ==================================================================================== +-- ==================================================================================== + +TUM.wingmenContacts = {} + +do + local CONTACT_REPORT_INTERVAL = 8 -- Called AT MOST every 15 seconds, so 6 means "AT MOST every two minutes" + + local knownGroupsID = {} + local newGroupsID = {} + local ticksLeftBeforeContactReport = CONTACT_REPORT_INTERVAL + + local function getGroupDetectionRange(grp) + if not grp then return 0.0 end + + local gCateg = Group.getCategory(grp) + + -- Take into account better sensors (radars, TGPs...) in later periods + local detectionRangeMultiplier = 1.0 + if TUM.settings.getValue(TUM.settings.id.TIME_PERIOD) == DCSEx.enums.timePeriod.MODERN then + detectionRangeMultiplier = 2.0 + elseif TUM.settings.getValue(TUM.settings.id.TIME_PERIOD) == DCSEx.enums.timePeriod.COLD_WAR then + detectionRangeMultiplier = 1.5 + elseif TUM.settings.getValue(TUM.settings.id.TIME_PERIOD) == DCSEx.enums.timePeriod.VIETNAM_WAR then + detectionRangeMultiplier = 1.25 + end + + local detectionRange = DCSEx.converter.nmToMeters(15 * detectionRangeMultiplier) + if gCateg == Group.Category.AIRPLANE then + detectionRange = DCSEx.converter.nmToMeters(40 * detectionRangeMultiplier) + elseif gCateg == Group.Category.SHIP then + detectionRange = DCSEx.converter.nmToMeters(22 * detectionRangeMultiplier) + local allSpeedboats = true + for _,u in ipairs(grp:getUnits()) do + if not u:getTypeName() == "speedboat" then allSpeedboats = false end + end + if allSpeedboats then detectionRange = detectionRange / 8 end -- Speedboats are HARD to spot + elseif gCateg == Group.Category.GROUND then + local allInfantry = true + local airDefenseCount = 0 + for _,u in ipairs(grp:getUnits()) do + if not u:hasAttribute("Infantry") then allInfantry = false end + if u:hasAttribute("Air Defence") then airDefenseCount = airDefenseCount + 1 end + end + if allInfantry then detectionRange = detectionRange / 8 end -- Infantry is HARD to spot + end + + return detectionRange + end + + function TUM.wingmenContacts.clearKnownContacts() + knownGroupsID = {} + newGroupsID = {} + ticksLeftBeforeContactReport = CONTACT_REPORT_INTERVAL + end + + function TUM.wingmenContacts.getContacts(groupCategory) + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return {} end -- No wingmen in multiplayer + if TUM.mission.getStatus() == TUM.mission.status.NONE then return {} end + local wingmenGroup = TUM.wingmen.getGroup() + if not wingmenGroup then return {} end + + local searchPoint = DCSEx.world.getGroupCenter(wingmenGroup) + + local detectedTargets = {} + local allGroups = coalition.getGroups(TUM.settings.getEnemyCoalition(), groupCategory) + for _,g in ipairs(allGroups) do + local gID = g:getID() + if g:isExist() and g:getSize() > 0 then + local gPos = DCSEx.world.getGroupCenter(g) + local gCateg = Group.getCategory(g) + + local detectionRange = getGroupDetectionRange(g) + + local distanceToGroup = DCSEx.math.getDistance2D(gPos, searchPoint) + if distanceToGroup <= detectionRange then -- Check if wingman group is in detection range + if not DCSEx.table.contains(knownGroupsID, gID) then + table.insert(knownGroupsID, gID) + table.insert(newGroupsID, gID) + end + + local groupInfo = { + id = gID, + point2 = gPos, + size = g:getSize(), + type = "contact" + } + + -- Return exact type when contact is close enough + if distanceToGroup < detectionRange / 2 then + groupInfo.type = Library.objectNames.get(g:getUnit(1)) + else + groupInfo.type = Library.objectNames.getGenericGroup(g) + end + + table.insert(detectedTargets, groupInfo) + end + end + end + + return detectedTargets + end + + function TUM.wingmenContacts.getContactsAsReportString(groupCategory, giveRelativePosition, newContactsOnly) + giveRelativePosition = giveRelativePosition or false + newContactsOnly = newContactsOnly or false + + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return nil end -- No wingmen in multiplayer + local contacts = TUM.wingmenContacts.getContacts(groupCategory) + if not contacts or #contacts == 0 then return nil end + + local refPoint = DCSEx.world.getGroupCenter(TUM.wingmen.getGroup()) + + local reportText = "" + for _,t in ipairs(contacts) do + if not newContactsOnly or DCSEx.table.contains(newGroupsID, t.id) then + reportText = reportText.."\n - "..tostring(t.size).."x "..t.type + if refPoint and giveRelativePosition then + reportText = reportText..", "..DCSEx.dcs.getBRAA(t.point2, refPoint, false, false, false) + end + end + end + return reportText + end + + ---------------------------------------------------------- + -- Called on every mission update tick (every 10-20 seconds) + -- @return True if something was done this tick, false otherwise + ---------------------------------------------------------- + function TUM.wingmenContacts.onClockTick() + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return false end -- No wingmen in multiplayer + if TUM.mission.getStatus() == TUM.mission.status.NONE then return false end + + TUM.wingmenContacts.getContacts() -- Check for new contacts, just in case + if #newGroupsID > 0 then -- New contacts found, alert the player! + local newContactsReportString = TUM.wingmenContacts.getContactsAsReportString(nil, true, true) + TUM.radio.playForAll("pilotWingmanReportContactsNew", { TUM.wingmen.getFirstWingmanNumber(), newContactsReportString }, TUM.wingmen.getFirstWingmanCallsign(), false) + newGroupsID = {} + if ticksLeftBeforeContactReport < math.floor(CONTACT_REPORT_INTERVAL / 2) then + ticksLeftBeforeContactReport = math.floor(CONTACT_REPORT_INTERVAL / 2) + end + return true + end + + ticksLeftBeforeContactReport = ticksLeftBeforeContactReport - 1 + if ticksLeftBeforeContactReport > 0 then return false end + ticksLeftBeforeContactReport = CONTACT_REPORT_INTERVAL + + return TUM.wingmenTasking.commandReportContacts(nil, true, false) + end +end diff --git a/Script/The Universal Mission/WingmenTasking.lua b/Script/The Universal Mission/WingmenTasking.lua index 2b2513f..4cf51dd 100644 --- a/Script/The Universal Mission/WingmenTasking.lua +++ b/Script/The Universal Mission/WingmenTasking.lua @@ -55,7 +55,7 @@ do allowWeaponUse(wingmenCtrl, true) - local detectedContacts = TUM.wingmen.getContacts(groupCategory) + local detectedContacts = TUM.wingmenContacts.getContacts(groupCategory) local validTargets = {} for _,c in ipairs(detectedContacts) do local g = DCSEx.world.getGroupByID(c.id) @@ -188,7 +188,7 @@ do delayRadioAnswer = delayRadioAnswer or false if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No wingmen in multiplayer - local reportString = TUM.wingmen.getContactsAsReportString(groupCategory, true) + local reportString = TUM.wingmenContacts.getContactsAsReportString(groupCategory, true) if not reportString then if noReportIfNoContacts then return false end