Got rid of those last changes.

This commit is contained in:
iTracerFacer 2025-10-07 18:06:19 -05:00
parent 6e18524b82
commit 6bb2d2c468
4 changed files with 0 additions and 1682 deletions

View File

@ -1,851 +0,0 @@
--[[
Simple AFAC System v1.0
========================
A lightweight, standalone Forward Air Controller system for DCS World.
No external dependencies required.
Features:
- Automatic AFAC aircraft detection
- Target scanning with line-of-sight verification
- Laser designation with customizable codes
- Visual marking (smoke/flares)
- F10 map markers with detailed target information
- Auto and manual targeting modes
- Clean F10 menu integration
Author: Based on original FAC script, streamlined for simplicity
]]
-- =====================================================
-- CONFIGURATION
-- =====================================================
AFAC = {}
AFAC.Config = {
-- Detection ranges
maxRange = 18520, -- Maximum AFAC detection range in meters
-- Aircraft types that auto-qualify as AFAC
afacAircraft = {
"SA342L",
"SA342Mistral",
"SA342Minigun",
"UH-60L",
"UH-1H",
"OH-58D"
},
-- Available laser codes
laserCodes = {'1688', '1677', '1666', '1113', '1115', '1111'},
-- Smoke/flare colors
smokeColors = {
GREEN = 0,
RED = 1,
WHITE = 2,
ORANGE = 3,
BLUE = 4
},
-- Default marker settings
defaultSmokeColor = {
[1] = 4, -- RED coalition uses BLUE smoke
[2] = 3 -- BLUE coalition uses ORANGE smoke
},
-- Marker duration (seconds)
mapMarkerDuration = 120,
smokeInterval = 300,
-- Target update intervals
autoUpdateInterval = 1.0,
manualScanRange = AFAC.maxRange or 18520,
-- Debug mode
debug = false
}
-- =====================================================
-- CORE DATA STRUCTURES
-- =====================================================
AFAC.Data = {
pilots = {}, -- Active AFAC pilots
targets = {}, -- Current targets per pilot
laserPoints = {}, -- Laser designations
irPoints = {}, -- IR pointers
smokeMarks = {}, -- Smoke marker tracking
onStation = {}, -- On-station status
laserCodes = {}, -- Assigned laser codes per pilot
markerSettings = {}, -- Per-pilot marker preferences
menuIds = {}, -- F10 menu tracking
manualTargets = {}, -- Manual mode target lists
nextMarkerId = 1000 -- Map marker ID counter
}
-- =====================================================
-- UTILITY FUNCTIONS
-- =====================================================
-- Debug logging
function AFAC.log(message)
if AFAC.Config.debug then
env.info("AFAC: " .. tostring(message))
end
end
-- Check if table contains value
function AFAC.contains(table, value)
for i = 1, #table do
if table[i] == value then
return true
end
end
return false
end
-- Calculate distance between two points
function AFAC.getDistance(point1, point2)
local dx = point1.x - point2.x
local dz = point1.z - point2.z
return math.sqrt(dx * dx + dz * dz)
end
-- Get unit heading in radians
function AFAC.getHeading(unit)
local unitPos = unit:getPosition()
if unitPos then
local heading = math.atan2(unitPos.x.z, unitPos.x.x)
if heading < 0 then
heading = heading + 2 * math.pi
end
return heading
end
return 0
end
-- Convert coordinates to Lat/Long string
function AFAC.coordToString(point)
local lat, lon = coord.LOtoLL(point)
local latDeg = math.floor(lat)
local latMin = (lat - latDeg) * 60
local lonDeg = math.floor(lon)
local lonMin = (lon - lonDeg) * 60
local latDir = latDeg >= 0 and "N" or "S"
local lonDir = lonDeg >= 0 and "E" or "W"
return string.format("%02d°%06.3f'%s %03d°%06.3f'%s",
math.abs(latDeg), latMin, latDir,
math.abs(lonDeg), lonMin, lonDir)
end
-- Get MGRS coordinate string
function AFAC.getMGRS(point)
local lat, lon = coord.LOtoLL(point)
-- Simplified MGRS - in real implementation you'd want full MGRS conversion
return string.format("MGRS: %02d%s%05d%05d",
math.floor(lat), "ABC"[math.random(1,3)],
math.random(10000, 99999), math.random(10000, 99999))
end
-- Get group ID from unit
function AFAC.getGroupId(unit)
local group = unit:getGroup()
if group then
return group:getID()
end
return nil
end
-- Notify coalition
function AFAC.notifyCoalition(message, duration, coalition)
trigger.action.outTextForCoalition(coalition, message, duration or 10)
end
-- =====================================================
-- AIRCRAFT DETECTION & MANAGEMENT
-- =====================================================
-- Check if unit qualifies as AFAC
function AFAC.isAFAC(unit)
local group = unit:getGroup()
if not group then return false end
local groupName = group:getName()
local unitType = unit:getTypeName()
-- Check group name for AFAC/RECON keywords
if string.find(groupName:upper(), "AFAC") or
string.find(groupName:upper(), "RECON") then
return true
end
-- Check aircraft type
return AFAC.contains(AFAC.Config.afacAircraft, unitType)
end
-- Add pilot to AFAC system
function AFAC.addPilot(unit)
local unitName = unit:getName()
local groupId = AFAC.getGroupId(unit)
if not groupId then return end
-- Initialize pilot data
AFAC.Data.pilots[unitName] = {
name = unitName,
unit = unit,
coalition = unit:getCoalition(),
groupId = groupId
}
-- Set default laser code
AFAC.Data.laserCodes[unitName] = AFAC.Config.laserCodes[1]
-- Set default marker settings
AFAC.Data.markerSettings[unitName] = {
type = "SMOKE",
color = AFAC.Config.defaultSmokeColor[unit:getCoalition()]
}
-- Set on station
AFAC.Data.onStation[unitName] = true
-- Notify player
local message = string.format("AFAC System Active\nAircraft: %s\nUse F10 menu to control targeting",
unit:getTypeName())
trigger.action.outTextForGroup(groupId, message, 15)
-- Start auto-lasing
AFAC.startAutoLasing(unitName)
AFAC.log("Added AFAC pilot: " .. unitName)
end
-- Remove pilot from system
function AFAC.removePilot(unitName)
if not AFAC.Data.pilots[unitName] then return end
-- Clean up laser points
AFAC.cancelLasing(unitName)
-- Clean up data
AFAC.Data.pilots[unitName] = nil
AFAC.Data.targets[unitName] = nil
AFAC.Data.laserPoints[unitName] = nil
AFAC.Data.irPoints[unitName] = nil
AFAC.Data.smokeMarks[unitName] = nil
AFAC.Data.onStation[unitName] = nil
AFAC.Data.laserCodes[unitName] = nil
AFAC.Data.markerSettings[unitName] = nil
AFAC.Data.manualTargets[unitName] = nil
AFAC.log("Removed AFAC pilot: " .. unitName)
end
-- =====================================================
-- TARGET DETECTION
-- =====================================================
-- Find nearest visible enemy to AFAC unit
function AFAC.findNearestTarget(afacUnit, maxRange)
local afacPoint = afacUnit:getPoint()
local afacCoalition = afacUnit:getCoalition()
local enemyCoalition = afacCoalition == 1 and 2 or 1
local nearestTarget = nil
local nearestDistance = maxRange or AFAC.Config.maxRange
-- Search for enemy units
local searchVolume = {
id = world.VolumeType.SPHERE,
params = {
point = afacPoint,
radius = nearestDistance
}
}
local function checkUnit(foundUnit)
if foundUnit:getCoalition() ~= enemyCoalition then return end
if not foundUnit:isActive() then return end
if foundUnit:inAir() then return end
if foundUnit:getLife() <= 1 then return end
local unitPoint = foundUnit:getPoint()
local distance = AFAC.getDistance(afacPoint, unitPoint)
if distance >= nearestDistance then return end
-- Check line of sight
local offsetAfacPos = {x = afacPoint.x, y = afacPoint.y + 2, z = afacPoint.z}
local offsetUnitPos = {x = unitPoint.x, y = unitPoint.y + 2, z = unitPoint.z}
if not land.isVisible(offsetAfacPos, offsetUnitPos) then return end
-- Priority system: SAMs first, then vehicles, then infantry
local priority = 1
if foundUnit:hasAttribute("SAM TR") or foundUnit:hasAttribute("IR Guided SAM") then
priority = 0.1 -- Highest priority
elseif foundUnit:hasAttribute("Vehicles") then
priority = 0.5
end
local adjustedDistance = distance * priority
if adjustedDistance < nearestDistance then
nearestDistance = adjustedDistance
nearestTarget = foundUnit
end
end
world.searchObjects(Object.Category.UNIT, searchVolume, checkUnit)
return nearestTarget
end
-- Scan area for multiple targets (manual mode)
function AFAC.scanForTargets(afacUnit, maxCount)
local afacPoint = afacUnit:getPoint()
local afacCoalition = afacUnit:getCoalition()
local enemyCoalition = afacCoalition == 1 and 2 or 1
local targets = {}
local samTargets = {}
local searchVolume = {
id = world.VolumeType.SPHERE,
params = {
point = afacPoint,
radius = AFAC.Config.manualScanRange
}
}
local function checkUnit(foundUnit)
if foundUnit:getCoalition() ~= enemyCoalition then return end
if not foundUnit:isActive() then return end
if foundUnit:inAir() then return end
if foundUnit:getLife() <= 1 then return end
local unitPoint = foundUnit:getPoint()
-- Check line of sight
local offsetAfacPos = {x = afacPoint.x, y = afacPoint.y + 2, z = afacPoint.z}
local offsetUnitPos = {x = unitPoint.x, y = unitPoint.y + 2, z = unitPoint.z}
if not land.isVisible(offsetAfacPos, offsetUnitPos) then return end
-- Separate SAMs from other targets
if foundUnit:hasAttribute("SAM TR") or foundUnit:hasAttribute("IR Guided SAM") then
table.insert(samTargets, foundUnit)
else
table.insert(targets, foundUnit)
end
end
world.searchObjects(Object.Category.UNIT, searchVolume, checkUnit)
-- Priority: SAMs first, then others
local finalTargets = {}
for i = 1, math.min(#samTargets, maxCount or 10) do
table.insert(finalTargets, samTargets[i])
end
local remainingSlots = (maxCount or 10) - #finalTargets
for i = 1, math.min(#targets, remainingSlots) do
table.insert(finalTargets, targets[i])
end
return finalTargets
end
-- =====================================================
-- LASER DESIGNATION SYSTEM
-- =====================================================
-- Start laser designation on target
function AFAC.startLasing(afacUnit, target, laserCode)
local unitName = afacUnit:getName()
-- Cancel existing lasing
AFAC.cancelLasing(unitName)
local targetPoint = target:getPoint()
local targetVector = {x = targetPoint.x, y = targetPoint.y + 2, z = targetPoint.z}
-- Create laser and IR points
local success, result = pcall(function()
local laserSpot = Spot.createLaser(afacUnit, {x = 0, y = 2, z = 0}, targetVector, laserCode)
local irSpot = Spot.createInfraRed(afacUnit, {x = 0, y = 2, z = 0}, targetVector)
return {laser = laserSpot, ir = irSpot}
end)
if success and result then
AFAC.Data.laserPoints[unitName] = result.laser
AFAC.Data.irPoints[unitName] = result.ir
AFAC.log("Started lasing target for " .. unitName)
else
AFAC.log("Failed to create laser designation for " .. unitName)
end
end
-- Update laser position
function AFAC.updateLasing(unitName, target)
local laserSpot = AFAC.Data.laserPoints[unitName]
local irSpot = AFAC.Data.irPoints[unitName]
if not laserSpot or not irSpot then return end
local targetPoint = target:getPoint()
local targetVector = {x = targetPoint.x, y = targetPoint.y + 2, z = targetPoint.z}
laserSpot:setPoint(targetVector)
irSpot:setPoint(targetVector)
end
-- Cancel laser designation
function AFAC.cancelLasing(unitName)
local laserSpot = AFAC.Data.laserPoints[unitName]
local irSpot = AFAC.Data.irPoints[unitName]
if laserSpot then
Spot.destroy(laserSpot)
AFAC.Data.laserPoints[unitName] = nil
end
if irSpot then
Spot.destroy(irSpot)
AFAC.Data.irPoints[unitName] = nil
end
end
-- =====================================================
-- VISUAL MARKING SYSTEM
-- =====================================================
-- Create smoke or flare marker
function AFAC.createVisualMarker(target, markerType, color)
local targetPoint = target:getPoint()
local markerPoint = {x = targetPoint.x, y = targetPoint.y + 2, z = targetPoint.z}
if markerType == "SMOKE" then
trigger.action.smoke(markerPoint, color)
else -- FLARE
trigger.action.signalFlare(markerPoint, color, 0)
end
end
-- Create F10 map marker with target details
function AFAC.createMapMarker(target, spotter)
local targetPoint = target:getPoint()
local coalition = AFAC.Data.pilots[spotter].coalition
-- Get target details
local velocity = target:getVelocity()
local speed = 0
if velocity then
speed = math.sqrt(velocity.x^2 + velocity.z^2) * 2.237 -- Convert to mph
end
local heading = AFAC.getHeading(target) * 180 / math.pi
local coords = AFAC.coordToString(targetPoint)
local mgrs = AFAC.getMGRS(targetPoint)
local altitude = math.floor(targetPoint.y)
local markerText = string.format("%s\n%s\n%s\nAlt: %dm/%dft\nHdg: %03d°\nSpd: %.0f mph\nSpotter: %s",
target:getTypeName(),
coords,
mgrs,
altitude,
altitude * 3.28084,
heading,
speed,
spotter
)
local markerId = AFAC.Data.nextMarkerId
AFAC.Data.nextMarkerId = AFAC.Data.nextMarkerId + 1
trigger.action.markToCoalition(markerId, markerText, targetPoint, coalition, false, "AFAC Target")
-- Schedule marker removal
timer.scheduleFunction(
function(args)
trigger.action.removeMark(args[1])
end,
{markerId},
timer.getTime() + AFAC.Config.mapMarkerDuration
)
return markerId
end
-- =====================================================
-- AUTOMATIC LASING SYSTEM
-- =====================================================
-- Start automatic target lasing
function AFAC.startAutoLasing(unitName)
timer.scheduleFunction(AFAC.autoLaseUpdate, {unitName}, timer.getTime() + 1)
end
-- Auto-lasing update function
function AFAC.autoLaseUpdate(args)
local unitName = args[1]
local pilot = AFAC.Data.pilots[unitName]
-- Check if pilot still exists and is on station
if not pilot or not AFAC.Data.onStation[unitName] then
return -- Don't reschedule
end
local afacUnit = pilot.unit
if not afacUnit or not afacUnit:isActive() or afacUnit:getLife() <= 0 then
AFAC.removePilot(unitName)
return
end
local currentTarget = AFAC.Data.targets[unitName]
local laserCode = AFAC.Data.laserCodes[unitName]
local markerSettings = AFAC.Data.markerSettings[unitName]
-- Check if current target is still valid
if currentTarget and (not currentTarget:isActive() or currentTarget:getLife() <= 1) then
-- Target destroyed
local message = string.format("[%s] Target %s destroyed. Good job! Scanning for new targets.",
unitName, currentTarget:getTypeName())
AFAC.notifyCoalition(message, 10, pilot.coalition)
AFAC.Data.targets[unitName] = nil
AFAC.cancelLasing(unitName)
currentTarget = nil
end
-- Find new target if needed
if not currentTarget then
local newTarget = AFAC.findNearestTarget(afacUnit)
if newTarget then
AFAC.Data.targets[unitName] = newTarget
-- Start lasing
AFAC.startLasing(afacUnit, newTarget, laserCode)
-- Create visual markers
AFAC.createVisualMarker(newTarget, markerSettings.type, markerSettings.color)
AFAC.createMapMarker(newTarget, unitName)
-- Notify coalition
local message = string.format("[%s] Lasing new target: %s, CODE: %s",
unitName, newTarget:getTypeName(), laserCode)
AFAC.notifyCoalition(message, 10, pilot.coalition)
currentTarget = newTarget
end
end
-- Update laser position if we have a target
if currentTarget then
AFAC.updateLasing(unitName, currentTarget)
-- Update smoke markers periodically
local nextSmokeTime = AFAC.Data.smokeMarks[unitName]
if not nextSmokeTime or nextSmokeTime < timer.getTime() then
AFAC.createVisualMarker(currentTarget, markerSettings.type, markerSettings.color)
AFAC.Data.smokeMarks[unitName] = timer.getTime() + AFAC.Config.smokeInterval
end
end
-- Schedule next update
timer.scheduleFunction(AFAC.autoLaseUpdate, args, timer.getTime() + AFAC.Config.autoUpdateInterval)
end
-- =====================================================
-- F10 MENU SYSTEM
-- =====================================================
-- Add F10 menus for AFAC pilot
function AFAC.addMenus(unitName)
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
local groupId = pilot.groupId
-- Main AFAC menu
local mainMenu = missionCommands.addSubMenuForGroup(groupId, "AFAC Control")
-- Targeting mode menu
local targetMenu = missionCommands.addSubMenuForGroup(groupId, "Targeting Mode", mainMenu)
missionCommands.addCommandForGroup(groupId, "Auto Mode ON", targetMenu, AFAC.setAutoMode, {unitName, true})
missionCommands.addCommandForGroup(groupId, "Auto Mode OFF", targetMenu, AFAC.setAutoMode, {unitName, false})
-- Manual targeting
local manualMenu = missionCommands.addSubMenuForGroup(groupId, "Manual Targeting", targetMenu)
missionCommands.addCommandForGroup(groupId, "Scan for Targets", manualMenu, AFAC.manualScan, {unitName})
-- Target selection (will be populated after scan)
local selectMenu = missionCommands.addSubMenuForGroup(groupId, "Select Target", manualMenu)
for i = 1, 10 do
missionCommands.addCommandForGroup(groupId, "Target " .. i, selectMenu, AFAC.selectManualTarget, {unitName, i})
end
-- Laser code menu
local laserMenu = missionCommands.addSubMenuForGroup(groupId, "Laser Codes", mainMenu)
for _, code in ipairs(AFAC.Config.laserCodes) do
missionCommands.addCommandForGroup(groupId, "Code: " .. code, laserMenu, AFAC.setLaserCode, {unitName, code})
end
-- Marker settings
local markerMenu = missionCommands.addSubMenuForGroup(groupId, "Marker Settings", mainMenu)
-- Smoke colors
local smokeMenu = missionCommands.addSubMenuForGroup(groupId, "Smoke Color", markerMenu)
for colorName, colorValue in pairs(AFAC.Config.smokeColors) do
missionCommands.addCommandForGroup(groupId, colorName, smokeMenu, AFAC.setMarkerColor, {unitName, "SMOKE", colorValue})
end
-- Flare colors (limited selection)
local flareMenu = missionCommands.addSubMenuForGroup(groupId, "Flare Color", markerMenu)
missionCommands.addCommandForGroup(groupId, "GREEN", flareMenu, AFAC.setMarkerColor, {unitName, "FLARE", 0})
missionCommands.addCommandForGroup(groupId, "WHITE", flareMenu, AFAC.setMarkerColor, {unitName, "FLARE", 2})
missionCommands.addCommandForGroup(groupId, "ORANGE", flareMenu, AFAC.setMarkerColor, {unitName, "FLARE", 3})
-- Status
missionCommands.addCommandForGroup(groupId, "AFAC Status", mainMenu, AFAC.showStatus, {unitName})
end
-- =====================================================
-- F10 MENU FUNCTIONS
-- =====================================================
-- Set auto/manual mode
function AFAC.setAutoMode(args)
local unitName = args[1]
local autoMode = args[2]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
AFAC.Data.onStation[unitName] = autoMode
if autoMode then
trigger.action.outTextForGroup(pilot.groupId, "Auto targeting mode enabled", 10)
AFAC.startAutoLasing(unitName)
else
trigger.action.outTextForGroup(pilot.groupId, "Auto targeting mode disabled", 10)
AFAC.cancelLasing(unitName)
AFAC.Data.targets[unitName] = nil
end
end
-- Manual target scan
function AFAC.manualScan(args)
local unitName = args[1]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
local targets = AFAC.scanForTargets(pilot.unit, 10)
AFAC.Data.manualTargets[unitName] = targets
-- Report found targets
if #targets > 0 then
local afacPoint = pilot.unit:getPoint()
trigger.action.outTextForGroup(pilot.groupId, "Targets found:", 5)
for i, target in ipairs(targets) do
local targetPoint = target:getPoint()
local distance = AFAC.getDistance(afacPoint, targetPoint)
local bearing = math.atan2(targetPoint.z - afacPoint.z, targetPoint.x - afacPoint.x) * 180 / math.pi
if bearing < 0 then bearing = bearing + 360 end
local message = string.format("Target %d: %s, Bearing %03d°, Range %.1fkm",
i, target:getTypeName(), bearing, distance / 1000)
trigger.action.outTextForGroup(pilot.groupId, message, 15)
-- Create map marker
AFAC.createMapMarker(target, unitName)
end
else
trigger.action.outTextForGroup(pilot.groupId, "No targets found in range", 10)
end
end
-- Select manual target
function AFAC.selectManualTarget(args)
local unitName = args[1]
local targetIndex = args[2]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
local targets = AFAC.Data.manualTargets[unitName]
if not targets or not targets[targetIndex] then
trigger.action.outTextForGroup(pilot.groupId, "Invalid target selection", 10)
return
end
local target = targets[targetIndex]
if not target:isActive() or target:getLife() <= 1 then
trigger.action.outTextForGroup(pilot.groupId, "Selected target is no longer active", 10)
return
end
-- Set as current target
AFAC.Data.targets[unitName] = target
-- Start lasing
local laserCode = AFAC.Data.laserCodes[unitName]
AFAC.startLasing(pilot.unit, target, laserCode)
-- Create markers
local markerSettings = AFAC.Data.markerSettings[unitName]
AFAC.createVisualMarker(target, markerSettings.type, markerSettings.color)
-- Notify
local message = string.format("Designating Target %d: %s, CODE: %s",
targetIndex, target:getTypeName(), laserCode)
trigger.action.outTextForGroup(pilot.groupId, message, 10)
AFAC.notifyCoalition("[" .. unitName .. "] " .. message, 10, pilot.coalition)
end
-- Set laser code
function AFAC.setLaserCode(args)
local unitName = args[1]
local laserCode = args[2]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
AFAC.Data.laserCodes[unitName] = laserCode
trigger.action.outTextForGroup(pilot.groupId, "Laser code set to: " .. laserCode, 10)
-- Update current lasing if active
local currentTarget = AFAC.Data.targets[unitName]
if currentTarget then
AFAC.startLasing(pilot.unit, currentTarget, laserCode)
end
end
-- Set marker color/type
function AFAC.setMarkerColor(args)
local unitName = args[1]
local markerType = args[2]
local color = args[3]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
AFAC.Data.markerSettings[unitName] = {
type = markerType,
color = color
}
local colorNames = {"GREEN", "RED", "WHITE", "ORANGE", "BLUE"}
trigger.action.outTextForGroup(pilot.groupId,
string.format("Marker set to %s %s", colorNames[color + 1] or "UNKNOWN", markerType), 10)
end
-- Show AFAC status
function AFAC.showStatus(args)
local unitName = args[1]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
local status = "AFAC STATUS:\n\n"
for pilotName, pilotData in pairs(AFAC.Data.pilots) do
if pilotData.coalition == pilot.coalition then
local target = AFAC.Data.targets[pilotName]
local laserCode = AFAC.Data.laserCodes[pilotName]
local onStation = AFAC.Data.onStation[pilotName]
if target and target:isActive() then
status = status .. string.format("%s: Targeting %s, CODE: %s\n",
pilotName, target:getTypeName(), laserCode)
elseif onStation then
status = status .. string.format("%s: On station, searching for targets\n", pilotName)
else
status = status .. string.format("%s: Off station\n", pilotName)
end
end
end
trigger.action.outTextForGroup(pilot.groupId, status, 30)
end
-- =====================================================
-- EVENT HANDLERS
-- =====================================================
-- Event handler for unit spawning
AFAC.EventHandler = {}
function AFAC.EventHandler:onEvent(event)
if event.id == world.event.S_EVENT_BIRTH then
local unit = event.initiator
if unit and Object.getCategory(unit) == Object.Category.UNIT then
local objDesc = unit:getDesc()
if objDesc.category == Unit.Category.AIRPLANE or objDesc.category == Unit.Category.HELICOPTER then
if AFAC.isAFAC(unit) then
-- Delay slightly to ensure unit is fully initialized
timer.scheduleFunction(
function(args)
local u = args[1]
if u and u:isActive() then
AFAC.addPilot(u)
AFAC.addMenus(u:getName())
end
end,
{unit},
timer.getTime() + 2
)
end
end
end
end
end
-- =====================================================
-- INITIALIZATION
-- =====================================================
-- Add event handler
world.addEventHandler(AFAC.EventHandler)
-- Initialize existing units (for mission restart)
timer.scheduleFunction(
function()
for coalitionId = 1, 2 do
local airGroups = coalition.getGroups(coalitionId, Group.Category.AIRPLANE)
local heliGroups = coalition.getGroups(coalitionId, Group.Category.HELICOPTER)
local allGroups = {}
for _, group in ipairs(airGroups) do table.insert(allGroups, group) end
for _, group in ipairs(heliGroups) do table.insert(allGroups, group) end
for _, group in ipairs(allGroups) do
local units = group:getUnits()
if units then
for _, unit in ipairs(units) do
if unit and unit:isActive() and AFAC.isAFAC(unit) then
AFAC.addPilot(unit)
AFAC.addMenus(unit:getName())
end
end
end
end
end
AFAC.log("AFAC System initialized")
end,
nil,
timer.getTime() + 5
)
-- Success message
trigger.action.outText("Simple AFAC System v1.0 loaded successfully!", 10)

View File

@ -1,606 +0,0 @@
--[[
Simple AFAC System v1.0
========================
A lightweight, standalone Forward Air Controller system for DCS World.
No external dependencies required.
]]
-- Initialize with success message
trigger.action.outText("Simple AFAC System v1.0 loading...", 10)
-- =====================================================
-- CONFIGURATION & INITIALIZATION
-- =====================================================
AFAC = {}
AFAC.Config = {
maxRange = 18520,
laserCodes = {'1688', '1677', '1666', '1113', '1115', '1111'},
smokeColors = {GREEN = 0, RED = 1, WHITE = 2, ORANGE = 3, BLUE = 4},
defaultSmokeColor = {[1] = 4, [2] = 3}, -- RED uses BLUE, BLUE uses ORANGE
mapMarkerDuration = 120,
smokeInterval = 300,
autoUpdateInterval = 1.0,
debug = true,
afacAircraft = {
"UH-1H", "UH-60L", "SA342L", "SA342Mistral", "SA342Minigun", "OH-58D", "Mi-8MT", "CH-47F",
"P-51D", "A-4E-C", "L-39C", "C-101CC"
}
}
AFAC.Data = {
pilots = {},
targets = {},
laserPoints = {},
irPoints = {},
smokeMarks = {},
onStation = {},
laserCodes = {},
markerSettings = {},
manualTargets = {},
menuIds = {},
nextMarkerId = 1000
}
-- =====================================================
-- UTILITY FUNCTIONS
-- =====================================================
function AFAC.log(message)
if AFAC.Config.debug then
env.info("AFAC: " .. tostring(message))
end
end
function AFAC.contains(table, value)
for i = 1, #table do
if table[i] == value then return true end
end
return false
end
function AFAC.getDistance(point1, point2)
local dx = point1.x - point2.x
local dz = point1.z - point2.z
return math.sqrt(dx * dx + dz * dz)
end
function AFAC.getGroupId(unit)
local group = unit:getGroup()
if group then return group:getID() end
return nil
end
function AFAC.notifyCoalition(message, duration, coalition)
trigger.action.outTextForCoalition(coalition, message, duration or 10)
end
-- =====================================================
-- AIRCRAFT DETECTION
-- =====================================================
function AFAC.isAFAC(unit)
if not unit then return false end
local unitType = unit:getTypeName()
AFAC.log("Checking aircraft type: " .. unitType)
local result = AFAC.contains(AFAC.Config.afacAircraft, unitType)
AFAC.log("isAFAC result for " .. unitType .. ": " .. tostring(result))
return result
end
function AFAC.addPilot(unit)
local unitName = unit:getName()
local groupId = AFAC.getGroupId(unit)
if not groupId then return end
-- Check if pilot is already registered
if AFAC.Data.pilots[unitName] then
AFAC.log("Pilot " .. unitName .. " already registered, skipping")
return
end
AFAC.log("Adding AFAC pilot: " .. unitName)
-- Initialize pilot data
AFAC.Data.pilots[unitName] = {
name = unitName,
unit = unit,
coalition = unit:getCoalition(),
groupId = groupId
}
-- Set defaults
AFAC.Data.laserCodes[unitName] = AFAC.Config.laserCodes[1]
AFAC.Data.markerSettings[unitName] = {
type = "SMOKE",
color = AFAC.Config.defaultSmokeColor[unit:getCoalition()]
}
AFAC.Data.onStation[unitName] = true
-- Notify player
local message = string.format("AFAC System Active\nAircraft: %s\nUse F10 menu to control targeting", unit:getTypeName())
trigger.action.outTextForGroup(groupId, message, 15)
-- Add F10 menu with slight delay to ensure everything is initialized
timer.scheduleFunction(function(args)
AFAC.addMenus(args[1])
end, {unitName}, timer.getTime() + 0.5)
-- Start auto-lasing
AFAC.startAutoLasing(unitName)
end
function AFAC.removePilot(unitName)
if not AFAC.Data.pilots[unitName] then return end
AFAC.cancelLasing(unitName)
-- Clean up data
AFAC.Data.pilots[unitName] = nil
AFAC.Data.targets[unitName] = nil
AFAC.Data.laserPoints[unitName] = nil
AFAC.Data.irPoints[unitName] = nil
AFAC.Data.smokeMarks[unitName] = nil
AFAC.Data.onStation[unitName] = nil
AFAC.Data.laserCodes[unitName] = nil
AFAC.Data.markerSettings[unitName] = nil
AFAC.Data.manualTargets[unitName] = nil
AFAC.Data.menuIds[unitName] = nil
AFAC.log("Removed AFAC pilot: " .. unitName)
end
-- =====================================================
-- TARGET DETECTION
-- =====================================================
function AFAC.findNearestTarget(afacUnit)
local afacPoint = afacUnit:getPoint()
local afacCoalition = afacUnit:getCoalition()
local enemyCoalition = afacCoalition == 1 and 2 or 1
local nearestTarget = nil
local nearestDistance = AFAC.Config.maxRange
local searchVolume = {
id = world.VolumeType.SPHERE,
params = {point = afacPoint, radius = nearestDistance}
}
local function checkUnit(foundUnit)
if foundUnit:getCoalition() ~= enemyCoalition then return end
if not foundUnit:isActive() then return end
if foundUnit:inAir() then return end
if foundUnit:getLife() <= 1 then return end
local unitPoint = foundUnit:getPoint()
local distance = AFAC.getDistance(afacPoint, unitPoint)
if distance >= nearestDistance then return end
-- Check line of sight
local offsetAfacPos = {x = afacPoint.x, y = afacPoint.y + 2, z = afacPoint.z}
local offsetUnitPos = {x = unitPoint.x, y = unitPoint.y + 2, z = unitPoint.z}
if not land.isVisible(offsetAfacPos, offsetUnitPos) then return end
-- Priority system
local priority = 1
if foundUnit:hasAttribute("SAM TR") or foundUnit:hasAttribute("IR Guided SAM") then
priority = 0.1
elseif foundUnit:hasAttribute("Vehicles") then
priority = 0.5
end
local adjustedDistance = distance * priority
if adjustedDistance < nearestDistance then
nearestDistance = adjustedDistance
nearestTarget = foundUnit
end
end
world.searchObjects(Object.Category.UNIT, searchVolume, checkUnit)
return nearestTarget
end
-- =====================================================
-- LASER DESIGNATION
-- =====================================================
function AFAC.startLasing(afacUnit, target, laserCode)
local unitName = afacUnit:getName()
AFAC.cancelLasing(unitName)
local targetPoint = target:getPoint()
local targetVector = {x = targetPoint.x, y = targetPoint.y + 2, z = targetPoint.z}
local success, result = pcall(function()
local laserSpot = Spot.createLaser(afacUnit, {x = 0, y = 2, z = 0}, targetVector, laserCode)
local irSpot = Spot.createInfraRed(afacUnit, {x = 0, y = 2, z = 0}, targetVector)
return {laser = laserSpot, ir = irSpot}
end)
if success and result then
AFAC.Data.laserPoints[unitName] = result.laser
AFAC.Data.irPoints[unitName] = result.ir
AFAC.log("Started lasing target for " .. unitName)
end
end
function AFAC.updateLasing(unitName, target)
local laserSpot = AFAC.Data.laserPoints[unitName]
local irSpot = AFAC.Data.irPoints[unitName]
if not laserSpot or not irSpot then return end
local targetPoint = target:getPoint()
local targetVector = {x = targetPoint.x, y = targetPoint.y + 2, z = targetPoint.z}
laserSpot:setPoint(targetVector)
irSpot:setPoint(targetVector)
end
function AFAC.cancelLasing(unitName)
local laserSpot = AFAC.Data.laserPoints[unitName]
local irSpot = AFAC.Data.irPoints[unitName]
if laserSpot then
Spot.destroy(laserSpot)
AFAC.Data.laserPoints[unitName] = nil
end
if irSpot then
Spot.destroy(irSpot)
AFAC.Data.irPoints[unitName] = nil
end
end
-- =====================================================
-- VISUAL MARKING
-- =====================================================
function AFAC.createVisualMarker(target, markerType, color)
local targetPoint = target:getPoint()
local markerPoint = {x = targetPoint.x, y = targetPoint.y + 2, z = targetPoint.z}
if markerType == "SMOKE" then
trigger.action.smoke(markerPoint, color)
else
trigger.action.signalFlare(markerPoint, color, 0)
end
end
function AFAC.createMapMarker(target, spotter)
local targetPoint = target:getPoint()
local coalition = AFAC.Data.pilots[spotter].coalition
local markerText = string.format("%s\nSpotter: %s", target:getTypeName(), spotter)
local markerId = AFAC.Data.nextMarkerId
AFAC.Data.nextMarkerId = AFAC.Data.nextMarkerId + 1
trigger.action.markToCoalition(markerId, markerText, targetPoint, coalition, false, "AFAC Target")
timer.scheduleFunction(function(args)
trigger.action.removeMark(args[1])
end, {markerId}, timer.getTime() + AFAC.Config.mapMarkerDuration)
return markerId
end
-- =====================================================
-- AUTO-LASING SYSTEM
-- =====================================================
function AFAC.startAutoLasing(unitName)
timer.scheduleFunction(AFAC.autoLaseUpdate, {unitName}, timer.getTime() + 1)
end
function AFAC.autoLaseUpdate(args)
local unitName = args[1]
local pilot = AFAC.Data.pilots[unitName]
if not pilot or not AFAC.Data.onStation[unitName] then
return
end
local afacUnit = pilot.unit
if not afacUnit or not afacUnit:isActive() or afacUnit:getLife() <= 0 then
AFAC.removePilot(unitName)
return
end
local currentTarget = AFAC.Data.targets[unitName]
local laserCode = AFAC.Data.laserCodes[unitName]
local markerSettings = AFAC.Data.markerSettings[unitName]
-- Check if current target is still valid
if currentTarget and (not currentTarget:isActive() or currentTarget:getLife() <= 1) then
local message = string.format("[%s] Target %s destroyed. Good job! Scanning for new targets.",
unitName, currentTarget:getTypeName())
AFAC.notifyCoalition(message, 10, pilot.coalition)
AFAC.Data.targets[unitName] = nil
AFAC.cancelLasing(unitName)
currentTarget = nil
end
-- Find new target if needed
if not currentTarget then
local newTarget = AFAC.findNearestTarget(afacUnit)
if newTarget then
AFAC.Data.targets[unitName] = newTarget
AFAC.startLasing(afacUnit, newTarget, laserCode)
AFAC.createVisualMarker(newTarget, markerSettings.type, markerSettings.color)
AFAC.createMapMarker(newTarget, unitName)
local message = string.format("[%s] Lasing new target: %s, CODE: %s",
unitName, newTarget:getTypeName(), laserCode)
AFAC.notifyCoalition(message, 10, pilot.coalition)
currentTarget = newTarget
end
end
-- Update laser position if we have a target
if currentTarget then
AFAC.updateLasing(unitName, currentTarget)
-- Update smoke markers periodically
local nextSmokeTime = AFAC.Data.smokeMarks[unitName]
if not nextSmokeTime or nextSmokeTime < timer.getTime() then
AFAC.createVisualMarker(currentTarget, markerSettings.type, markerSettings.color)
AFAC.Data.smokeMarks[unitName] = timer.getTime() + AFAC.Config.smokeInterval
end
end
-- Schedule next update
timer.scheduleFunction(AFAC.autoLaseUpdate, args, timer.getTime() + AFAC.Config.autoUpdateInterval)
end
-- =====================================================
-- F10 MENU SYSTEM
-- =====================================================
function AFAC.addMenus(unitName)
local pilot = AFAC.Data.pilots[unitName]
if not pilot then
AFAC.log("addMenus: No pilot data found for " .. unitName)
return
end
local groupId = pilot.groupId
if not groupId then
AFAC.log("addMenus: No group ID found for " .. unitName)
return
end
-- Check if menus already exist for this pilot
if AFAC.Data.menuIds[unitName] then
AFAC.log("Menus already exist for " .. unitName .. ", skipping creation")
return
end
AFAC.log("Creating menus for " .. unitName .. " (Group ID: " .. groupId .. ")")
local mainMenu = missionCommands.addSubMenuForGroup(groupId, "AFAC Control")
AFAC.Data.menuIds[unitName] = mainMenu
AFAC.log("Main menu created")
-- Wrap menu creation in pcall to catch any errors
local success, error = pcall(function()
-- Targeting mode
local targetMenu = missionCommands.addSubMenuForGroup(groupId, "Targeting Mode", mainMenu)
missionCommands.addCommandForGroup(groupId, "Auto Mode ON", targetMenu, AFAC.setAutoMode, {unitName, true})
missionCommands.addCommandForGroup(groupId, "Auto Mode OFF", targetMenu, AFAC.setAutoMode, {unitName, false})
AFAC.log("Targeting mode menu created")
-- Laser codes
local laserMenu = missionCommands.addSubMenuForGroup(groupId, "Laser Codes", mainMenu)
for _, code in ipairs(AFAC.Config.laserCodes) do
missionCommands.addCommandForGroup(groupId, "Code: " .. code, laserMenu, AFAC.setLaserCode, {unitName, code})
end
AFAC.log("Laser codes menu created with " .. #AFAC.Config.laserCodes .. " codes")
-- Marker settings
local markerMenu = missionCommands.addSubMenuForGroup(groupId, "Marker Settings", mainMenu)
local smokeMenu = missionCommands.addSubMenuForGroup(groupId, "Smoke Color", markerMenu)
-- Add smoke colors
missionCommands.addCommandForGroup(groupId, "GREEN", smokeMenu, AFAC.setMarkerColor, {unitName, "SMOKE", 0})
missionCommands.addCommandForGroup(groupId, "RED", smokeMenu, AFAC.setMarkerColor, {unitName, "SMOKE", 1})
missionCommands.addCommandForGroup(groupId, "WHITE", smokeMenu, AFAC.setMarkerColor, {unitName, "SMOKE", 2})
missionCommands.addCommandForGroup(groupId, "ORANGE", smokeMenu, AFAC.setMarkerColor, {unitName, "SMOKE", 3})
missionCommands.addCommandForGroup(groupId, "BLUE", smokeMenu, AFAC.setMarkerColor, {unitName, "SMOKE", 4})
AFAC.log("Marker settings menu created")
-- Status
missionCommands.addCommandForGroup(groupId, "AFAC Status", mainMenu, AFAC.showStatus, {unitName})
AFAC.log("All menus created successfully for " .. unitName)
end)
if not success then
AFAC.log("Error creating menus for " .. unitName .. ": " .. tostring(error))
end
end
-- =====================================================
-- F10 MENU FUNCTIONS
-- =====================================================
function AFAC.setAutoMode(args)
local unitName = args[1]
local autoMode = args[2]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
AFAC.Data.onStation[unitName] = autoMode
if autoMode then
trigger.action.outTextForGroup(pilot.groupId, "Auto targeting mode enabled", 10)
AFAC.startAutoLasing(unitName)
else
trigger.action.outTextForGroup(pilot.groupId, "Auto targeting mode disabled", 10)
AFAC.cancelLasing(unitName)
AFAC.Data.targets[unitName] = nil
end
end
function AFAC.setLaserCode(args)
local unitName = args[1]
local laserCode = args[2]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
AFAC.Data.laserCodes[unitName] = laserCode
trigger.action.outTextForGroup(pilot.groupId, "Laser code set to: " .. laserCode, 10)
local currentTarget = AFAC.Data.targets[unitName]
if currentTarget then
AFAC.startLasing(pilot.unit, currentTarget, laserCode)
end
end
function AFAC.setMarkerColor(args)
local unitName = args[1]
local markerType = args[2]
local color = args[3]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
AFAC.Data.markerSettings[unitName] = {type = markerType, color = color}
local colorNames = {"GREEN", "RED", "WHITE", "ORANGE", "BLUE"}
trigger.action.outTextForGroup(pilot.groupId,
string.format("Marker set to %s %s", colorNames[color + 1] or "UNKNOWN", markerType), 10)
end
function AFAC.showStatus(args)
local unitName = args[1]
local pilot = AFAC.Data.pilots[unitName]
if not pilot then return end
local status = "AFAC STATUS:\n\n"
for pilotName, pilotData in pairs(AFAC.Data.pilots) do
if pilotData.coalition == pilot.coalition then
local target = AFAC.Data.targets[pilotName]
local laserCode = AFAC.Data.laserCodes[pilotName]
local onStation = AFAC.Data.onStation[pilotName]
if target and target:isActive() then
status = status .. string.format("%s: Targeting %s, CODE: %s\n",
pilotName, target:getTypeName(), laserCode)
elseif onStation then
status = status .. string.format("%s: On station, searching for targets\n", pilotName)
else
status = status .. string.format("%s: Off station\n", pilotName)
end
end
end
trigger.action.outTextForGroup(pilot.groupId, status, 30)
end
-- =====================================================
-- EVENT HANDLER
-- =====================================================
AFAC.EventHandler = {}
function AFAC.EventHandler:onEvent(event)
if event.id == world.event.S_EVENT_BIRTH then
local unit = event.initiator
if unit and Object.getCategory(unit) == Object.Category.UNIT then
local objDesc = unit:getDesc()
if objDesc.category == Unit.Category.AIRPLANE or objDesc.category == Unit.Category.HELICOPTER then
if AFAC.isAFAC(unit) then
timer.scheduleFunction(function(args)
local u = args[1]
if u and u:isActive() then
AFAC.addPilot(u)
end
end, {unit}, timer.getTime() + 2)
end
end
end
end
if event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then
local unit = event.initiator
if unit and Object.getCategory(unit) == Object.Category.UNIT then
local objDesc = unit:getDesc()
if objDesc.category == Unit.Category.AIRPLANE or objDesc.category == Unit.Category.HELICOPTER then
if AFAC.isAFAC(unit) then
local unitName = unit:getName()
if not AFAC.Data.pilots[unitName] then
timer.scheduleFunction(function(args)
local u = args[1]
if u and u:isActive() then
AFAC.addPilot(u)
end
end, {unit}, timer.getTime() + 1)
end
end
end
end
end
end
-- =====================================================
-- INITIALIZATION
-- =====================================================
world.addEventHandler(AFAC.EventHandler)
-- Check for existing pilots
function AFAC.checkForExistingPilots()
for coalitionId = 1, 2 do
local airGroups = coalition.getGroups(coalitionId, Group.Category.AIRPLANE)
local heliGroups = coalition.getGroups(coalitionId, Group.Category.HELICOPTER)
local allGroups = {}
for _, group in ipairs(airGroups) do table.insert(allGroups, group) end
for _, group in ipairs(heliGroups) do table.insert(allGroups, group) end
for _, group in ipairs(allGroups) do
local units = group:getUnits()
if units then
for _, unit in ipairs(units) do
if unit and unit:isActive() and AFAC.isAFAC(unit) then
local unitName = unit:getName()
if unit:getPlayerName() and not AFAC.Data.pilots[unitName] then
AFAC.addPilot(unit)
end
end
end
end
end
end
end
timer.scheduleFunction(AFAC.checkForExistingPilots, nil, timer.getTime() + 3)
-- Periodic check for new pilots
function AFAC.periodicCheck()
AFAC.checkForExistingPilots()
timer.scheduleFunction(AFAC.periodicCheck, nil, timer.getTime() + 10)
end
timer.scheduleFunction(AFAC.periodicCheck, nil, timer.getTime() + 15)
AFAC.log("AFAC System initialized successfully")
trigger.action.outText("Simple AFAC System v1.0 loaded successfully!", 10)

View File

@ -1,138 +0,0 @@
# TADC Smart Threat Prioritization System
## Overview
The enhanced Tactical Air Defense Controller (TADC) now includes a comprehensive smart threat prioritization system that uses multi-factor analysis to assess threats and assign the most appropriate squadrons for intercepts.
## Key Features
### 1. Multi-Factor Threat Priority Calculation
The system evaluates threats based on multiple weighted factors:
- **Base Threat Type (40% weight)**
- Bombers: 100 points (highest threat - can destroy strategic targets)
- Attack Aircraft: 85 points (high threat - ground attack capability)
- Fighters: 70 points (medium threat - air superiority)
- Helicopters: 50 points (lower threat but still dangerous)
- **Formation Size Factor (15% weight)**
- Each additional aircraft adds 30% threat multiplier
- Capped at 250% maximum multiplier
- **Strategic Proximity Analysis (25% weight)**
- Exponential threat increase closer to strategic targets
- Considers airbase importance weighting
- 75km threat radius around strategic targets
- **Speed and Heading Analysis (10% weight)**
- Fast-moving aircraft are more threatening (less intercept time)
- Heading toward strategic targets increases threat
- Supersonic aircraft get highest speed bonus
- **Temporal Factors (10% weight)**
- Night operations bonus (reduced defender visibility)
- Weather considerations (framework ready)
- **Electronic Warfare Considerations**
- EW aircraft (EA-, EF-) get high priority
- Stealth aircraft (F-22, F-35) get threat bonus
### 2. Predictive Threat Assessment
- **Future Position Prediction**: Calculates where threats will be in 5 minutes
- **Time-to-Target Analysis**: Determines how long until threat reaches strategic targets
- **Intercept Window Calculation**: Optimizes response timing
- **Response Urgency Classification**:
- EMERGENCY: < 5 minutes to target (150% priority boost)
- URGENT: < 10 minutes to target (130% priority boost)
- HIGH: < 20 minutes to target (110% priority boost)
- STANDARD: > 20 minutes to target
### 3. Enhanced Squadron-Threat Matching
The system now intelligently matches squadrons to threats based on:
- **Distance Factor (40% of match score)**
- Exponential scoring favoring closer squadrons
- 75km ideal response range
- **Threat Type Specialization (30% of match score)**
- Helicopter squadrons perfect for anti-helicopter missions
- Fighter squadrons excel against bombers and attack aircraft
- Type-specific bonuses for optimal pairing
- **Squadron Readiness (20% of match score)**
- More available aircraft = higher score
- Alert level considerations (GREEN/YELLOW/RED)
- Fatigue factor penalties
- **Response Urgency Matching (10% of match score)**
- Emergency responses get priority multipliers
- All squadrons receive urgency bonuses for critical threats
### 4. Strategic Target Protection
The system maintains a database of strategic targets with importance weights:
- **Airbases** (Importance: 85-100)
- Severomorsk-1: 100 (Primary intercept base)
- Olenya: 95 (Northern coverage)
- Murmansk: 90 (Western coverage)
- Afrikanda: 85 (Helicopter base)
- **SAM Sites** (Importance: 60-70)
- SA-10 Sites: 70
- SA-11 Sites: 60
- **Command Centers** (Importance: 80)
### 5. Advanced Logging and Debugging
Enhanced debug output provides detailed information:
```
=== SMART THREAT ASSESSMENT: RED_BORDER ===
1. F/A-18C Hornet (ATTACK x2) Priority:145 TTT:3.2m Response:URGENT
2. F-16C Viper (FIGHTER x1) Priority:89 TTT:8.7m Response:HIGH
3. AH-64D Apache (HELICOPTER x1) Priority:67 TTT:15.1m Response:STANDARD
✓ SMART ASSIGNMENT: FIGHTER_SWEEP_RED_Severomorsk-1 → F/A-18C Hornet (ATTACK x2) Score:87.3 Priority:145 TTT:3.2m URGENT
```
## Configuration Options
### Smart Prioritization Settings
```lua
-- Enhanced threat assessment
enableSmartPrioritization = true, -- Enable smart threat analysis
strategicTargetRadius = 75000, -- Threat radius around strategic targets (75km)
emergencyResponseTime = 300, -- Emergency threshold (5 minutes)
urgentResponseTime = 600, -- Urgent threshold (10 minutes)
-- Squadron matching
enableEnhancedMatching = true, -- Enable smart squadron-threat matching
typeSpecializationWeight = 0.3, -- Weight for type matching (30%)
distanceWeight = 0.4, -- Weight for distance factor (40%)
readinessWeight = 0.2, -- Weight for squadron readiness (20%)
```
### Debug Levels
- **Level 0**: Silent operation
- **Level 1**: Basic threat assessment and assignment logging
- **Level 2**: Detailed scoring breakdown and squadron matching analysis
## Performance Impact
The smart prioritization system adds minimal performance overhead:
- Threat calculations are only performed when threats are detected
- Strategic target coordinates are cached on initialization
- Efficient sorting algorithms used for priority ranking
## Benefits
1. **More Effective Defense**: Highest priority threats are engaged first
2. **Optimal Resource Allocation**: Best-suited squadrons assigned to appropriate threats
3. **Predictive Response**: System anticipates threat movements and timing
4. **Strategic Protection**: Critical assets receive priority defense
5. **Realistic Threat Assessment**: Multi-factor analysis mirrors real-world threat evaluation
## Future Enhancements
- Weather-based threat modifications
- Terrain masking considerations
- Electronic warfare environment effects
- Machine learning threat pattern recognition
- Dynamic strategic target importance based on mission phase
The smart prioritization system transforms the TADC from a reactive system into a truly intelligent air defense controller that can anticipate, prioritize, and respond to threats with military-grade effectiveness.

View File

@ -1,87 +0,0 @@
# TADC Smart Prioritization - Testing Checklist
## Pre-Flight Verification ✅
### 1. Code Structure Fixed
- ✅ `GCI_Config` moved before `validateConfiguration()` function
- ✅ No syntax errors found in VS Code
- ✅ All smart prioritization functions properly implemented
### 2. Smart Prioritization Features Implemented
- ✅ Multi-factor threat priority calculation (6 factors)
- ✅ Strategic target protection system
- ✅ Predictive threat assessment with time-to-target
- ✅ Enhanced squadron-threat matching algorithm
- ✅ Response urgency classification (EMERGENCY/URGENT/HIGH/STANDARD)
### 3. Enhanced Error Recovery
- ✅ Advanced spawn retry system with 3 fallback methods
- ✅ Configuration validation on startup
- ✅ Performance monitoring framework
### 4. Expected Test Results
When you run the mission, you should see:
```
=== INITIALIZING TACTICAL AIR DEFENSE CONTROLLER ===
✓ Configuration loaded and validated:
- Threat Ratio: 1:1
- Max Simultaneous CAP: 12
- Supply Mode: INFINITE
✓ Smart threat prioritization system with multi-factor analysis
✓ Predictive threat assessment and response
✓ Strategic target protection with distance-based prioritization
✓ Enhanced squadron-threat matching algorithm
✓ Tactical Air Defense Controller operational!
```
### 5. Smart Assessment Logs
When threats are detected, look for:
```
=== SMART THREAT ASSESSMENT: RED_BORDER ===
1. F/A-18C Hornet (ATTACK x2) Priority:145 TTT:3.2m Response:URGENT
2. F-16C Viper (FIGHTER x1) Priority:89 TTT:8.7m Response:HIGH
✓ SMART ASSIGNMENT: FIGHTER_SWEEP_RED_Severomorsk-1 → F/A-18C Hornet Score:87.3 Priority:145 TTT:3.2m URGENT
```
### 6. Performance Improvements
- Threats are now processed in priority order (highest first)
- Squadron matching considers distance, type specialization, and readiness
- Response urgency automatically adjusts based on time-to-target
- Strategic assets (airbases) receive priority protection
## Testing Instructions
1. **Launch Mission**: Load Operation Polar Shield in DCS
2. **Check Console**: Verify TADC initialization messages appear
3. **Spawn Blue Threats**: Use mission editor or triggers to spawn enemy aircraft
4. **Monitor F10 Map**: Watch for CAP flights launching and intercepting
5. **Check Logs**: Look for smart prioritization and assignment messages
## Key Differences You'll Notice
- **Faster Response**: High-priority threats get immediate attention
- **Better Matching**: Fighters launch against bombers, helicopters vs helicopters
- **Predictive Behavior**: System anticipates where threats are going
- **Priority Protection**: Closer threats to airbases get higher priority
- **Detailed Logging**: Much more informative debug output
## Troubleshooting
If you see errors:
1. Check that all RED-EWR groups exist in mission
2. Verify RED BORDER and HELO BORDER zones are defined
3. Ensure all squadron template groups are set to "Late Activation"
4. Confirm airbase names match between script and mission
## Ready for Testing! 🚀
The smart prioritization system is fully implemented and ready for combat testing. The system will now intelligently assess threats, predict their movements, and assign the most suitable interceptors based on multiple factors including threat type, distance, urgency, and strategic importance.
Expected benefits:
- 40-60% more effective threat response
- Better resource utilization
- More realistic air defense behavior
- Enhanced protection of critical assets