mirror of
https://github.com/akaAgar/the-universal-mission-for-dcs-world.git
synced 2025-11-25 19:31:01 +00:00
Added wingmen spontaneous contact reports
This commit is contained in:
@@ -117,6 +117,24 @@ Library.radioMessages = {
|
|||||||
"$1. Reporting.\n\n$2",
|
"$1. Reporting.\n\n$2",
|
||||||
"$1. Status report.\n\n$2",
|
"$1. Status report.\n\n$2",
|
||||||
},
|
},
|
||||||
|
pilotWingmanReportTargets = {
|
||||||
|
"$1, tally contacts.$2",
|
||||||
|
"$1, tally groups, pushing details.$2",
|
||||||
|
"$1, visual on targets.$2",
|
||||||
|
"$1, eyes on units.$2",
|
||||||
|
"$1, contacts hot.$2",
|
||||||
|
"$1, eyes on possible targets.$2",
|
||||||
|
"$1, got activity out here.$2",
|
||||||
|
"$1, picking something up.$2",
|
||||||
|
"$1, contact confirmed.$2",
|
||||||
|
},
|
||||||
|
pilotWingmanReportTargetsNoJoy = {
|
||||||
|
"$1, negative contacts.",
|
||||||
|
"$1, clean all sectors.",
|
||||||
|
"$1, sensors are clean.",
|
||||||
|
"$1, nothing showing on my end.",
|
||||||
|
"$1, area looks clear.",
|
||||||
|
},
|
||||||
|
|
||||||
atcSafeLanding = { "Be advised: $1 is wheels down at $2 and clear of runway.", "All aircraft, $1 has landed at $2 and vacated active. Runway is open for next inbound.", "Traffic, $1 is on deck at $2 and heading to parking. Runway clear.", "All flights, $1 just rolled out at $2 and cleared the active.", "Heads up, $1 landed at $2 and moving to the ramp. Runway available for next approach." },
|
atcSafeLanding = { "Be advised: $1 is wheels down at $2 and clear of runway.", "All aircraft, $1 has landed at $2 and vacated active. Runway is open for next inbound.", "Traffic, $1 is on deck at $2 and heading to parking. Runway clear.", "All flights, $1 just rolled out at $2 and cleared the active.", "Heads up, $1 landed at $2 and moving to the ramp. Runway available for next approach." },
|
||||||
atcSafeLandingPlayer = { "$1, wheels on deck, welcome back. You may taxi to the parking area.", "$1, good copy on landing. Exit when able, proceed to the parking area.", "$1, touchdown confirmed. Continue to parking.", "$1, welcome home. Clear of runway and taxi to parking area.", "$1, nice landing. Taxi to parking when ready." },
|
atcSafeLandingPlayer = { "$1, wheels on deck, welcome back. You may taxi to the parking area.", "$1, good copy on landing. Exit when able, proceed to the parking area.", "$1, touchdown confirmed. Continue to parking.", "$1, welcome home. Clear of runway and taxi to parking area.", "$1, nice landing. Taxi to parking when ready." },
|
||||||
@@ -270,6 +288,13 @@ Library.radioMessages = {
|
|||||||
"$1, status check.",
|
"$1, status check.",
|
||||||
"$1, talk me through your state."
|
"$1, talk me through your state."
|
||||||
},
|
},
|
||||||
|
playerWingmanReportTargets = {
|
||||||
|
"$1, you tally anything?",
|
||||||
|
"$1, you got any hits?",
|
||||||
|
"$1, eyes or sensors on anything?",
|
||||||
|
"$1, you see anything out there?",
|
||||||
|
"$1, any contacts your side?",
|
||||||
|
},
|
||||||
playerJTACSmoke = {
|
playerJTACSmoke = {
|
||||||
"$1, request smoke on objective $2, over.",
|
"$1, request smoke on objective $2, over.",
|
||||||
"$1, mark objective $2 with smoke, how copy?",
|
"$1, mark objective $2 with smoke, how copy?",
|
||||||
|
|||||||
@@ -139,10 +139,12 @@ do
|
|||||||
if TUM.mission.onClockTick() then return nextTickTime end
|
if TUM.mission.onClockTick() then return nextTickTime end
|
||||||
elseif clockTick % 4 == 1 then
|
elseif clockTick % 4 == 1 then
|
||||||
if TUM.airForce.onClockTick(TUM.settings.getPlayerCoalition()) then return nextTickTime end
|
if TUM.airForce.onClockTick(TUM.settings.getPlayerCoalition()) then return nextTickTime end
|
||||||
|
if TUM.supportWingmen.onClockTick() then return nextTickTime end
|
||||||
elseif clockTick % 4 == 2 then
|
elseif clockTick % 4 == 2 then
|
||||||
if TUM.supportAWACS.onClockTick() then return nextTickTime end
|
if TUM.supportAWACS.onClockTick() then return nextTickTime end
|
||||||
else
|
else
|
||||||
if TUM.airForce.onClockTick(TUM.settings.getEnemyCoalition()) then return nextTickTime end
|
if TUM.airForce.onClockTick(TUM.settings.getEnemyCoalition()) then return nextTickTime end
|
||||||
|
if TUM.supportWingmen.onClockTick() then return nextTickTime end
|
||||||
end
|
end
|
||||||
|
|
||||||
return nextTickTime
|
return nextTickTime
|
||||||
|
|||||||
@@ -12,9 +12,14 @@ do
|
|||||||
ENGAGE_BANDITS = 3,
|
ENGAGE_BANDITS = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local CONTACT_REPORT_INTERVAL = 4 -- onClockTick is called twice by minute, so multiply this by 30 seconds (CONTACT_REPORT_INTERVAL = 4 means "every 2 minutes")
|
||||||
|
local WINGMEN_COUNT = 2 -- TODO: load from setting
|
||||||
|
|
||||||
|
local knownContacts = {}
|
||||||
|
local newContacts = {}
|
||||||
|
local nextContactReport = CONTACT_REPORT_INTERVAL
|
||||||
local wingmenGroupID = nil
|
local wingmenGroupID = nil
|
||||||
local wingmenUnitID = {}
|
local wingmenUnitID = {}
|
||||||
local WINGMEN_COUNT = 2 -- TODO: load from setting
|
|
||||||
|
|
||||||
local function getWingmenGroup()
|
local function getWingmenGroup()
|
||||||
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return nil end -- No wingmen in multiplayer
|
if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return nil end -- No wingmen in multiplayer
|
||||||
@@ -64,12 +69,9 @@ do
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getDetectedTargets(wingmanIndex, attributes, maxRangeInNM)
|
local function getDetectedContacts(wingmanIndex, attributes, groupCategory)
|
||||||
TUM.log("A")
|
|
||||||
attributes = attributes or {}
|
attributes = attributes or {}
|
||||||
local maxRange = DCSEx.converter.nmToMeters(maxRangeInNM or 1000)
|
|
||||||
|
|
||||||
TUM.log("B")
|
|
||||||
local searchPoints = {}
|
local searchPoints = {}
|
||||||
if wingmanIndex then
|
if wingmanIndex then
|
||||||
if wingmanIndex < 1 or wingmanIndex > #wingmenUnitID then return {} end
|
if wingmanIndex < 1 or wingmanIndex > #wingmenUnitID then return {} end
|
||||||
@@ -97,18 +99,34 @@ do
|
|||||||
|
|
||||||
local knownGroups = {}
|
local knownGroups = {}
|
||||||
local detectedTargets = {}
|
local detectedTargets = {}
|
||||||
local allGroups = coalition.getGroups(TUM.settings.getEnemyCoalition())
|
local allGroups = coalition.getGroups(TUM.settings.getEnemyCoalition(), groupCategory)
|
||||||
for _,g in ipairs(allGroups) do
|
for _,g in ipairs(allGroups) do
|
||||||
local gID = g:getID()
|
local gID = g:getID()
|
||||||
if g:isExist() and g:getSize() > 0 and not DCSEx.table.contains(knownGroups, gID) then
|
if g:isExist() and g:getSize() > 0 and not DCSEx.table.contains(knownGroups, gID) then
|
||||||
local gPos = DCSEx.world.getGroupCenter(g)
|
local gPos = DCSEx.world.getGroupCenter(g)
|
||||||
local gCateg = Group.getCategory(g)
|
local gCateg = Group.getCategory(g)
|
||||||
|
|
||||||
|
local specialGroupProperties = nil
|
||||||
|
|
||||||
local detectionRange = DCSEx.converter.nmToMeters(20 * detectionRangeMultiplier)
|
local detectionRange = DCSEx.converter.nmToMeters(20 * detectionRangeMultiplier)
|
||||||
if gCateg == Group.Category.AIRPLANE then
|
if gCateg == Group.Category.AIRPLANE then
|
||||||
detectionRange = DCSEx.converter.nmToMeters(50 * detectionRangeMultiplier)
|
detectionRange = DCSEx.converter.nmToMeters(50 * detectionRangeMultiplier)
|
||||||
elseif gCateg == Group.Category.SHIP then
|
elseif gCateg == Group.Category.SHIP then
|
||||||
detectionRange = DCSEx.converter.nmToMeters(30 * detectionRangeMultiplier)
|
detectionRange = DCSEx.converter.nmToMeters(30 * detectionRangeMultiplier)
|
||||||
|
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 -- Infantry is HARD to spot
|
||||||
|
specialGroupProperties = "Infantry"
|
||||||
|
elseif airDefenseCount >= g:getSize() / 1.5 then
|
||||||
|
specialGroupProperties = "Air Defence"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if at least one wingman is in detection range
|
-- Check if at least one wingman is in detection range
|
||||||
@@ -123,6 +141,11 @@ do
|
|||||||
if inRange then
|
if inRange then
|
||||||
table.insert(knownGroups, gID)
|
table.insert(knownGroups, gID)
|
||||||
|
|
||||||
|
if not DCSEx.table.contains(knownContacts) then
|
||||||
|
table.insert(knownContacts, gID)
|
||||||
|
table.insert(newContacts, gID)
|
||||||
|
end
|
||||||
|
|
||||||
local groupInfo = {
|
local groupInfo = {
|
||||||
id = gID,
|
id = gID,
|
||||||
point2 = gPos,
|
point2 = gPos,
|
||||||
@@ -135,8 +158,10 @@ do
|
|||||||
elseif gCateg == Group.Category.HELICOPTER then
|
elseif gCateg == Group.Category.HELICOPTER then
|
||||||
groupInfo.type = "helicopters"
|
groupInfo.type = "helicopters"
|
||||||
elseif gCateg == Group.Category.GROUND then
|
elseif gCateg == Group.Category.GROUND then
|
||||||
if g:getUnits()[1]:hasAttribute("Infantry") then
|
if specialGroupProperties == "Infantry" then
|
||||||
groupInfo.type = "infantry"
|
groupInfo.type = "infantry"
|
||||||
|
elseif specialGroupProperties == "Air Defence" then
|
||||||
|
groupInfo.type = "air defense"
|
||||||
else
|
else
|
||||||
groupInfo.type = "vehicles"
|
groupInfo.type = "vehicles"
|
||||||
end
|
end
|
||||||
@@ -230,7 +255,7 @@ do
|
|||||||
local wingmenCtrl = getWingmanController(args.index)
|
local wingmenCtrl = getWingmanController(args.index)
|
||||||
if not wingmenCtrl then return end
|
if not wingmenCtrl then return end
|
||||||
|
|
||||||
local targets = getDetectedTargets(args.index, args.attributes, args.maxRange)
|
local targets = getDetectedContacts(args.index, args.attributes, args.maxRange)
|
||||||
if not targets or #targets == 0 then
|
if not targets or #targets == 0 then
|
||||||
TUM.radio.playForAll("pilotWingmanEngageNoTarget", { getWingmanNumberAsWord(args.index) }, getWingmanCallsign(args.index), true)
|
TUM.radio.playForAll("pilotWingmanEngageNoTarget", { getWingmanNumberAsWord(args.index) }, getWingmanCallsign(args.index), true)
|
||||||
return
|
return
|
||||||
@@ -260,19 +285,30 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- NOTE: returns true if a report was radioed, true if not
|
||||||
|
local function doWingmenCommandReportContacts(args)
|
||||||
|
args.noPlayerMessage = args.noPlayerMessage or false
|
||||||
|
local player = world:getPlayer()
|
||||||
|
if not player then return false end
|
||||||
|
|
||||||
local function doWingmenCommandReportTargets(args)
|
if not args.noPlayerMessage then
|
||||||
local detectedTargets = getDetectedTargets(args.index, args.attributes)
|
TUM.radio.playForAll("playerWingmanReportTargets", { getWingmanNumberAsWord(args.index) }, player:getCallsign(), false)
|
||||||
|
end
|
||||||
|
|
||||||
local reportText = "Detected targets:"
|
local detectedTargets = getDetectedContacts(args.index, args.attributes)
|
||||||
if #detectedTargets == 0 then
|
|
||||||
reportText = reportText.." none"
|
if not detectedTargets or #detectedTargets == 0 then
|
||||||
|
if args.noPlayerMessage then return false end -- No need to bother the player with "I don't have any contacts" spontaneous messages
|
||||||
|
TUM.radio.playForAll("pilotWingmanReportTargetsNoJoy", { getWingmanNumberAsWord(args.index) }, getWingmanCallsign(args.index), true)
|
||||||
|
return true
|
||||||
else
|
else
|
||||||
|
local reportText = ""
|
||||||
for _,t in ipairs(detectedTargets) do
|
for _,t in ipairs(detectedTargets) do
|
||||||
reportText = reportText.."\n - "..tostring(t.size).."x "..t.type..", "..DCSEx.dcs.getBRAA(t.point2, DCSEx.math.vec3ToVec2(world:getPlayer():getPoint()), false, false, false).." from you"
|
reportText = reportText.."\n - "..tostring(t.size).."x "..t.type..", "..DCSEx.dcs.getBRAA(t.point2, DCSEx.math.vec3ToVec2(world:getPlayer():getPoint()), false, false, false).." from you"
|
||||||
end
|
end
|
||||||
|
TUM.radio.playForAll("pilotWingmanReportTargets", { getWingmanNumberAsWord(args.index), reportText }, getWingmanCallsign(args.index), not args.noPlayerMessage)
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
trigger.action.outText(reportText, 5)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function doWingmenCommandReportStatus(args)
|
local function doWingmenCommandReportStatus(args)
|
||||||
@@ -373,6 +409,10 @@ do
|
|||||||
wingmenGroupID = groupInfo.groupID
|
wingmenGroupID = groupInfo.groupID
|
||||||
wingmenUnitID = DCSEx.table.deepCopy(groupInfo.unitsID)
|
wingmenUnitID = DCSEx.table.deepCopy(groupInfo.unitsID)
|
||||||
|
|
||||||
|
knownContacts = {}
|
||||||
|
newContacts = {}
|
||||||
|
nextContactReport = CONTACT_REPORT_INTERVAL
|
||||||
|
|
||||||
TUM.log("Spawned AI wingmen")
|
TUM.log("Spawned AI wingmen")
|
||||||
TUM.radio.playForAll("pilotWingmanRejoin", { getWingmanNumberAsWord() }, getWingmanCallsign(), true)
|
TUM.radio.playForAll("pilotWingmanRejoin", { getWingmanNumberAsWord() }, getWingmanCallsign(), true)
|
||||||
end
|
end
|
||||||
@@ -419,8 +459,8 @@ do
|
|||||||
local wingmanPath = missionCommands.addSubMenu(getWingmanNumberAsWord(wingmanIndex), rootPath)
|
local wingmanPath = missionCommands.addSubMenu(getWingmanNumberAsWord(wingmanIndex), rootPath)
|
||||||
|
|
||||||
missionCommands.addCommand("Engage bandits", wingmanPath, doWingmenCommandEngage, { index = wingmanIndex, attributes = { "Battle airplanes" }, maxRange = 60, radioSuffix = "Bandits" })
|
missionCommands.addCommand("Engage bandits", wingmanPath, doWingmenCommandEngage, { index = wingmanIndex, attributes = { "Battle airplanes" }, maxRange = 60, radioSuffix = "Bandits" })
|
||||||
missionCommands.addCommand("Report targets", wingmanPath, doWingmenCommandReportTargets, { index = wingmanIndex })
|
missionCommands.addCommand("Any contacts?", wingmanPath, doWingmenCommandReportContacts, { index = wingmanIndex, noPlayerMessage = false })
|
||||||
missionCommands.addCommand("Report status", wingmanPath, doWingmenCommandReportStatus, { index = wingmanIndex, noPlayerMessage = false } )
|
missionCommands.addCommand("Status report", wingmanPath, doWingmenCommandReportStatus, { index = wingmanIndex, noPlayerMessage = false } )
|
||||||
missionCommands.addCommand("Go to map marker WINGMAN", wingmanPath, doWingmenCommandGoToMapMarker, { index = wingmanIndex } )
|
missionCommands.addCommand("Go to map marker WINGMAN", wingmanPath, doWingmenCommandGoToMapMarker, { index = wingmanIndex } )
|
||||||
missionCommands.addCommand("Orbit at position", wingmanPath, doWingmenCommandOrbit, { index = wingmanIndex })
|
missionCommands.addCommand("Orbit at position", wingmanPath, doWingmenCommandOrbit, { index = wingmanIndex })
|
||||||
missionCommands.addCommand("Rejoin", wingmanPath, doWingmenCommandRejoin, { index = wingmanIndex })
|
missionCommands.addCommand("Rejoin", wingmanPath, doWingmenCommandRejoin, { index = wingmanIndex })
|
||||||
@@ -438,6 +478,21 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------
|
||||||
|
-- Called on every mission update tick (every 10-20 seconds)
|
||||||
|
-- @return True if something was done this tick, false otherwise
|
||||||
|
----------------------------------------------------------
|
||||||
|
function TUM.supportWingmen.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
|
||||||
|
|
||||||
|
nextContactReport = nextContactReport - 1
|
||||||
|
if nextContactReport > 0 then return false end
|
||||||
|
nextContactReport = CONTACT_REPORT_INTERVAL
|
||||||
|
|
||||||
|
return doWingmenCommandReportContacts({ index = nil, noPlayerMessage = true })
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
-- Called when an event is raised
|
-- Called when an event is raised
|
||||||
-- @param event The DCS World event
|
-- @param event The DCS World event
|
||||||
|
|||||||
Reference in New Issue
Block a user