Jump to content

Recon mission in DCS?

Recommended Posts

If you're using Fog of War map setting you can use Visual Recon Mode to spot targets:


Other than this, I guess you could initially spawn hidden units and then replace them with copies that are visible on the map when they enter the moving zone. If you want to output specific location information you'll have to use scripting.

  • Like 1


Win 10 i5-9600KF 4.6 GHz 64 GB RAM RTX2080Ti 11GB -- Win 7 64 i5-6600K 3.6 GHz 32 GB RAM GTX970 4GB -- A-10C, F-5E, Su-27, F-15C, F-14B, F-16C missions in User Files

Link to comment
Share on other sites

Tnx man. I'll check it.

Edit: I was thinking of like not using visual recon mode from cockpit rather scripted solution that just places markers on map weather AI or player driven plane or even ground unit is scanning thru. All based on moving detection zone. Think it needs some scripting approach. Tx on reply anyway.

Edited by jackmckay
Link to comment
Share on other sites

It should be fairly straightforward with Lua-scripting. While moving, simply check every second or so which units are within detection range of the scout, and if in range, check if the scout has LOS to the unit in range, perhaps add some detection probability. If detected and wasn't detected before, mark it on the map with trigger.action.markToCoalition() , and add the unit itself to the table of detected units so it won't be marked again. You don't even need moving zones for that; probably two hours worth of coding to add niceties like audio feedback and/or messages. Add half an hour to add the ability top remove marks after timeout 🙂



Edited by cfrag
Link to comment
Share on other sites

OK, here's a mission that does what you want. I've thrown the recon script together in a rather short time, so it's not really debugged. The script loads at mission start (note: I load another script, dcsCommon, which is my library of common DCS mission methods. That one loads first).

In this mission, the AI flight "Recon" does the detecting, it's turned on by the the ONCE "Enable recon mode on Recon" trigger that invokes cfxReconMode.addScout("Recon"). That adds the units called "Recon" (the F-14) to the pool of reconnaissance Planes that the script watches, and starts reporting and marking all enemy units it detects on the F-10 Map. If you click on a mark, it expands to the enemy group's name. 




The marks disappear after 10 minutes

You can configure the min and max visibility; the script makes it so that max visibility increases with the scout's altitude.

To see what it does, run the mission, sit in the A-10A plane on ground (if you don't have F3, simply change to any plane you may have), and simply watch the F10 map with the F-14. 

Accelerate time to see the marks disappear after 10 minutes.

Hope this helps. 


reconnai demonstray.miz

Edited by cfrag
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

And here's the raw script. Note that although I'm having ideas about blacklisting groups (so they are never reported) and prioListing (so they are marked different), they are not in the script because I ran out of time 🙂. Invocations starting with dcsCommon are to my library. For brevity I won't discuss them here, the calls are obvious, and it's included in the mission.


cfxReconMode = {}
cfxReconMode.version = "1.0.0"

 1.0.0 - initial version 
 cfxReconMode is a script that allows units to perform reconnaissance
 missions and, after detecting units on, marks them on the map with 
cfxReconMode.ups = 1 -- updates per second
cfxReconMode.scouts = {} -- units that are performing scouting. 
cfxReconMode.detectedGroups = {} -- so we know which have been detected
cfxReconMode.marksFadeAfter = 600 -- after detection, marks disappear after
                     -- this amount of seconds. -1 means no fade
					 -- 600 is ten minutes
cfxReconMode.prioList = {} -- group names that are high prio
cfxReconMode.blackList = {} -- group names athat are never detected
cfxReconMode.detectionMinRange = 3000 -- meters at ground level
cfxReconMode.detectionMaxRange = 4000 -- meters at max alt (10'000m)
cfxReconMode.maxAlt = 10000 -- alt for maxrange

cfxReconMode.callbacks = {} -- sig: cb(reason, side, scout, group)
cfxReconMode.uuidCount = 0 -- for unique marks 

function cfxReconMode.uuid()
	cfxReconMode.uuidCount = cfxReconMode.uuidCount + 1
	return cfxReconMode.uuidCount

function cfxReconMode.addCallback(theCB)
	table.insert(cfxReconMode.callbacks, theCB)

function cfxReconMode.invokeCallbacks(reason, theSide, theSout, theGroup)
	for idx, theCB in pairs(cfxReconMode.callbacks) do 
		theCB(reason, theSide, theScout, theGroup)

function cfxReconMode.addScout(theUnit)
	if not theUnit then 
		trigger.action.outText("+++cfxRecon: nil Unit on add", 30)
	if type(theUnit) == "string" then 
		trigger.action.outText("+++cfxRecon: will access vby name: " .. theUnit, 30)
		local u = Unit.getByName(theUnit) 
		theUnit = u
	if not theUnit then 
		trigger.action.outText("+++cfxRecon: did not find unit on add", 30)
	cfxReconMode.scouts[theUnit:getName()] = theUnit

function cfxReconMode.removeScout(theUnit)
	if type(theUnit) == "string" then 
		theUnit = Unit:getByName(theUnit) 
	if not theUnit then return end	
	cfxReconMode.scouts[theUnit:getName()] = nil

function cfxReconMode.canDetect(scoutPos, theGroup, visRange)
	-- determine if a member of theGroup can be seen from 
	-- scoutPos at visRange 
	-- returns true and pos when detected
	local allUnits = theGroup:getUnits()
	for idx, aUnit in pairs(allUnits) do
		if aUnit:isExist() and aUnit:getLife() >= 1 then 
			local uPos = aUnit:getPoint()
			uPos.y = uPos.y + 3 -- raise my 3 meters
			local d = dcsCommon.distFlat(scoutPos, uPos) 
			if d < visRange then 
				-- is in visual range. do we have LOS?
				if land.isVisible(scoutPos, uPos) then 
					-- group is visible, stop here, return true
					return true, uPos
	return false, nil -- nothing visible

function cfxReconMode.placeMarkForUnit(location, theSide, theGroup) 
	local theID = cfxReconMode.uuid()
					"Contact: "..theGroup:getName(), 
	return theID

function cfxReconMode.removeMarkForArgs(args)
	local theSide = args[1]
	local theScout = args[2]
	local theGroup = args[3]
	local theID = args[4]
	cfxReconMode.detectedGroups[theGroup:getName()] = nil 
	-- invoke callbacks
	cfxReconMode.invokeCallbacks("removed", theSide, theScout, theGroup)

function cfxReconMode.detectedGroup(mySide, theScout, theGroup, theLoc)
	-- put a mark on the map 
	local theID = cfxReconMode.placeMarkForUnit(theLoc, mySide, theGroup)
	-- schedule removal if desired 
	if cfxReconMode.marksFadeAfter > 0 then 
		args = {mySide, theScout, theGroup, theID}
		timer.scheduleFunction(cfxReconMode.removeMarkForArgs, args, timer.getTime() + cfxReconMode.marksFadeAfter)
	-- say something
			theScout:getName() .. " reports new ground contact " .. theGroup:getName(),
	-- play a sound 
	trigger.action.outSoundForCoalition(mySide, "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav")
	-- invoke callbacks
	cfxReconMode.invokeCallbacks("detected", mySide, theSout, theGroup)

function cfxReconMode.performReconForUnit(theScout)
	if not theScout then return end 
	if not theScout:isExist() then return end 
	-- get altitude above ground to calculate visual range 
	local alt = dcsCommon.getUnitAGL(theScout)
	local visRange = dcsCommon.lerp(cfxReconMode.detectionMinRange, cfxReconMode.detectionMaxRange, alt/cfxReconMode.maxAlt)
	local scoutPos = theScout:getPoint()
	-- figure out which groups we are looking for
	local myCoal = theScout:getCoalition()
	local enemyCoal = 1 
	if myCoal == 1 then enemyCoal = 2 end 
	-- iterate all enemy units until we find one 
	-- and then stop this iteration (can only detect one 
	-- group per pass)
	local enemyGroups = coalition.getGroups(enemyCoal)
	for idx, theGroup in pairs (enemyGroups) do 
		-- make sure it's a ground unit 
		local isGround = theGroup:getCategory() == 2
		if theGroup:isExist() and isGround then 
			local visible, location = cfxReconMode.canDetect(scoutPos, theGroup, visRange)
			if visible then 
				-- see if we already detected this one 
				if cfxReconMode.detectedGroups[theGroup:getName()] == nil then 
					-- visible and not yet seen 
					-- perhaps add some percent chance now 
					-- remember that we know this group 
					cfxReconMode.detectedGroups[theGroup:getName()] = theGroup
					cfxReconMode.detectedGroup(myCoal, theScout, theGroup, location)
					return -- stop, as we only detect one group per pass

function cfxReconMode.update()
	-- schedule next call 
	timer.scheduleFunction(cfxReconMode.update, {}, timer.getTime() + 1/cfxReconMode.ups)
	-- now process all scouts
	for idx, scout in pairs(cfxReconMode.scouts) do 

function cfxReconMode.start()
	-- start update cycle
	trigger.action.outText("cfx Recon version " .. cfxReconMode.version .. " started.", 30)
	return true

if not cfxReconMode.start() then 
	cfxReconMode = nil



  • Thanks 1
Link to comment
Share on other sites

50 minutes ago, jackmckay said:

Is your "dcsCommon" script extrapolated from egg. Mist or just your own? Is it mandatory?

It's my own, completely independent of mist or other libraries. It's mandatory in that the recon scripts needs the three methods lerp(), getUnitAGL(), and distFlat() - I'm too lazy to copy them over individually, so I simply include the entire lib. Since it doesn't do anything by itself, it won't waste performance, and simply uses up some 10KB of memory.  You could, if you wanted to save a few bytes, delete all other methods from dcsCommon (except dist(), which is invoked by distFlat) and end up with a smaller lib. Not worth the effort, IMHO. 

  • Like 1
Link to comment
Share on other sites


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...