mirror of
https://github.com/akaAgar/the-universal-mission-for-dcs-world.git
synced 2025-11-25 19:31:01 +00:00
242 lines
9.5 KiB
Lua
242 lines
9.5 KiB
Lua
-- ====================================================================================
|
|
-- TUM.AIRFORCE - HANDLES THE FRIENDLY AND ENEMY COMBAT AIR PATROL
|
|
-- ====================================================================================
|
|
-- ====================================================================================
|
|
|
|
TUM.airForce = {}
|
|
|
|
do
|
|
local SUPPRESSION_INCREASE_ON_KILL = 0.35 -- Value added to enemyCAPSuppression each time an enemy aircraft is shot down
|
|
|
|
local desiredUnitCount = { 4, 4 } -- Desired max number of aircraft in the air at any single time
|
|
local fighterGroups = { {}, {} }
|
|
|
|
local enemyCAPSuppression = 1
|
|
local enemyCAPSuppressionTimer = 1
|
|
local playerCenter = nil
|
|
|
|
local function getSkillLevel(side)
|
|
-- Friendly AI is always excellent
|
|
if side == TUM.settings.getPlayerCoalition() then return "Excellent" end
|
|
|
|
local airForceLevel = TUM.settings.getValue(TUM.settings.id.ENEMY_AIR_FORCE) - 1
|
|
|
|
-- if airForceLevel <= 1 then return "Average"
|
|
-- elseif airForceLevel == 2 then return DCSEx.table.getRandom({"Average", "Good"})
|
|
-- elseif airForceLevel == 3 then return DCSEx.table.getRandom({"Good", "High"})
|
|
-- else return DCSEx.table.getRandom({"High", "Excellent"})
|
|
-- end
|
|
|
|
if airForceLevel <= 2 then return "Average"
|
|
elseif airForceLevel == 3 then return DCSEx.table.getRandom({"Average", "Good"})
|
|
else return DCSEx.table.getRandom({"Good", "High", "Excellent"})
|
|
end
|
|
end
|
|
|
|
local function randomizeDesiredAircraftCount(side)
|
|
local airForceLevel = 0
|
|
|
|
if side == TUM.settings.getPlayerCoalition() then
|
|
if TUM.settings.getValue(TUM.settings.id.AI_CAP) == 1 then
|
|
airForceLevel = 2
|
|
else
|
|
airForceLevel = 0
|
|
end
|
|
else
|
|
airForceLevel = TUM.settings.getValue(TUM.settings.id.ENEMY_AIR_FORCE) - 1
|
|
end
|
|
|
|
if airForceLevel == 0 then
|
|
desiredUnitCount[side] = 0
|
|
else
|
|
desiredUnitCount[side] = math.random(airForceLevel, math.ceil(airForceLevel * 1.5)) + 1
|
|
end
|
|
end
|
|
|
|
local function getAirborneUnitCount(side)
|
|
local count = 0
|
|
|
|
for _,id in ipairs(fighterGroups[side]) do
|
|
local g = DCSEx.world.getGroupByID(id)
|
|
if g then
|
|
count = count + g:getSize()
|
|
end
|
|
end
|
|
|
|
return count
|
|
end
|
|
|
|
local function launchNewAircraftGroup(side, airbases)
|
|
local groupSize = DCSEx.table.getRandom({ 1, 2, 2, 2, 2, 3, 3, 4 })
|
|
groupSize = math.min(groupSize, desiredUnitCount[side] - getAirborneUnitCount(side))
|
|
if groupSize <= 0 then return false end -- No aircraft slots left
|
|
|
|
local faction = TUM.settings.getEnemyFaction()
|
|
if side == TUM.settings.getPlayerCoalition() then faction = TUM.settings.getPlayerFaction() end
|
|
|
|
local units = Library.factions.getUnits(faction, DCSEx.enums.unitFamily.PLANE_FIGHTER, groupSize, true)
|
|
if not units or #units == 0 then return false end -- No aircraft found
|
|
|
|
-- If enemy CAP suppression timer > 0, decrement it by 1 but don't spawn any aircraft
|
|
if side == TUM.settings.getEnemyCoalition() then
|
|
enemyCAPSuppressionTimer = enemyCAPSuppressionTimer - 1
|
|
if enemyCAPSuppressionTimer > 0 then
|
|
TUM.log("Enemy CAP is still suppressed (suppression="..tostring(enemyCAPSuppressionTimer).."), no enemy CAP spawned.")
|
|
return false
|
|
end
|
|
|
|
enemyCAPSuppressionTimer = enemyCAPSuppression
|
|
end
|
|
|
|
local launchAirbase = airbases[DCSEx.math.clamp(math.random(1, math.ceil(math.sqrt(#airbases))), 1, #airbases)]
|
|
local originPt = DCSEx.math.vec3ToVec2(launchAirbase:getPoint())
|
|
|
|
local groupInfo = DCSEx.unitGroupMaker.create(
|
|
side, Group.Category.AIRPLANE,
|
|
originPt, units,
|
|
{
|
|
moveTo = DCSEx.math.randomPointInCircle(TUM.objectives.getCenter(), TUM.objectives.getRadius(), 0),
|
|
silenced = true,
|
|
skill = getSkillLevel(side),
|
|
takeOff = true,
|
|
taskCAP = true,
|
|
unlimitedFuel = true
|
|
})
|
|
|
|
if not groupInfo then return false end
|
|
table.insert(fighterGroups[side], groupInfo.groupID)
|
|
|
|
local players = coalition.getPlayers(TUM.settings.getPlayerCoalition())
|
|
|
|
if side == TUM.settings.getPlayerCoalition() then
|
|
local newUnit = nil
|
|
local newGroup = DCSEx.world.getGroupByID(groupInfo.groupID)
|
|
if newGroup then newUnit = newGroup:getUnit(1) end
|
|
|
|
local callsign = "FRIENDLY CAP"
|
|
local typeName = "Fighter aircraft"
|
|
if newUnit then
|
|
callsign = newUnit:getCallsign()
|
|
typeName = Library.objectNames.get(newUnit)
|
|
end
|
|
|
|
for _,p in ipairs(players) do
|
|
local abInfo = launchAirbase:getName()
|
|
abInfo = abInfo.." ("..DCSEx.dcs.getBRAA(launchAirbase:getPoint(), p:getPoint(), false, false, true).." from you)"
|
|
|
|
TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(p), "pilotNewFriendlyAircraft", { typeName, abInfo }, callsign)
|
|
end
|
|
else
|
|
for _,p in ipairs(players) do
|
|
local abInfo = launchAirbase:getName()
|
|
abInfo = abInfo.." ("..DCSEx.dcs.getBRAA(launchAirbase:getPoint(), p:getPoint(), false, false, true).." from you)"
|
|
|
|
TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(p), "commandNewEnemyAircraft", { DCSEx.string.toStringNumber(groupSize), abInfo }, "Command")
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local function updateAirForce(side)
|
|
if desiredUnitCount[side] <= 0 then return false end -- No airforce
|
|
if not TUM.DEBUG_MODE and #DCSEx.world.getPlayersInAir() == 0 then return false end -- No players currently in the air, don't spawn new AI aircraft (except in debug mode)
|
|
|
|
local validAirbases = {}
|
|
local airbases = coalition.getAirbases(side)
|
|
for _,ab in pairs(airbases) do
|
|
if ab:getDesc().category ~= Airbase.Category.SHIP then -- Ignore ships
|
|
table.insert(validAirbases, ab)
|
|
end
|
|
end
|
|
if not validAirbases or #validAirbases == 0 then return false end -- No airbases found for this coalition, nowhere to take off from
|
|
|
|
if side == TUM.settings.getPlayerCoalition() then
|
|
validAirbases = { DCSEx.dcs.getNearestObject(playerCenter, validAirbases) }
|
|
else
|
|
validAirbases = DCSEx.dcs.getNearestObjects(TUM.objectives.getCenter(), validAirbases)
|
|
end
|
|
|
|
local airborneUnitCount = getAirborneUnitCount(side)
|
|
|
|
if airborneUnitCount < desiredUnitCount[side] then
|
|
if math.random(1, 2 + math.ceil(airborneUnitCount / 2)) == 1 then
|
|
if math.random(1, 4) then
|
|
randomizeDesiredAircraftCount(side)
|
|
end
|
|
|
|
return launchNewAircraftGroup(side, validAirbases)
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
----------------------------------------------------------
|
|
-- Called on every mission update tick (every 10-20 seconds)
|
|
-- @param side The side for which air force must be updated
|
|
-- @return True if something was done this tick, false otherwise
|
|
----------------------------------------------------------
|
|
function TUM.airForce.onClockTick(side)
|
|
if TUM.mission.getStatus() == TUM.mission.status.NONE then return false end -- Not currenly in a mission
|
|
if TUM.objectives.getCount() <= 0 then return false end -- No objectives, nothing to defend for CAP
|
|
|
|
return updateAirForce(side)
|
|
end
|
|
|
|
-------------------------------------
|
|
-- Called when an event is raised
|
|
-- @param event The DCS World event
|
|
-------------------------------------
|
|
function TUM.airForce.onEvent(event)
|
|
if not event.initiator then return end
|
|
if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end
|
|
if event.id ~= world.event.S_EVENT_UNIT_LOST then return end
|
|
|
|
local groupID = DCSEx.dcs.getGroupIDAsNumber(event.initiator:getGroup())
|
|
|
|
if DCSEx.table.contains(fighterGroups[TUM.settings.getEnemyCoalition()], groupID) then
|
|
enemyCAPSuppression = enemyCAPSuppression + SUPPRESSION_INCREASE_ON_KILL
|
|
TUM.log("Enemy CAP suppression increased to "..tostring(enemyCAPSuppression))
|
|
end
|
|
end
|
|
|
|
function TUM.airForce.create()
|
|
TUM.airForce.removeAll()
|
|
TUM.log("Creating friendly and enemy air forces...")
|
|
|
|
for side=1,2 do
|
|
randomizeDesiredAircraftCount(side)
|
|
end
|
|
end
|
|
|
|
function TUM.airForce.removeAll()
|
|
if #fighterGroups[1] > 0 or #fighterGroups[2] > 0 then
|
|
TUM.log("Removing all friendly and enemy air force...")
|
|
end
|
|
|
|
for side=1,2 do
|
|
for _,id in ipairs(fighterGroups[side]) do
|
|
DCSEx.world.destroyGroupByID(id)
|
|
end
|
|
end
|
|
|
|
-- Reset enemy CAP suppression
|
|
enemyCAPSuppression = 1
|
|
enemyCAPSuppressionTimer = 1
|
|
|
|
fighterGroups = { {}, {} }
|
|
end
|
|
|
|
function TUM.airForce.onStartUp()
|
|
playerCenter = DCSEx.envMission.getPlayerGroupsCenterPoint()
|
|
if not playerCenter then
|
|
playerCenter = { x = env.mission.map.centerX, y = env.mission.map.centerY }
|
|
end
|
|
|
|
-- TODO: build list of airbases now instead of of each update() (but what about destroyed airbases?)
|
|
|
|
return true
|
|
end
|
|
end
|