2025-09-18 15:02:34 +02:00

247 lines
12 KiB
Lua

-- ====================================================================================
-- TUM.OBJECTIVESMAKER - CREATE MISSION OBJECTIVES
-- ====================================================================================
-- ====================================================================================
TUM.objectivesMaker = {}
do
local usedParkingSpots = {}
local function pickRandomTask()
local taskFamily = TUM.settings.getValue(TUM.settings.id.TASKING)
local validTaskIDs = {}
for k,t in pairs(Library.tasks) do
if t.taskFamily == taskFamily then
table.insert(validTaskIDs, k)
end
end
if #validTaskIDs == 0 then return nil end
return DCSEx.table.getRandom(validTaskIDs)
end
local function pickWaterPoint(nearThisPoint)
local waterZones = TUM.territories.getWaterZones()
if not waterZones or #waterZones == 0 then return nil end -- No "water" zones on this map
local possiblePoints = {}
for _=1,24 do
local point = DCSEx.zones.getRandomPointInside(DCSEx.table.getRandom(waterZones), land.SurfaceType.WATER)
if point then
table.insert(possiblePoints, point)
end
end
if #possiblePoints == 0 then return nil end
possiblePoints = DCSEx.dcs.getNearestPoints(nearThisPoint, possiblePoints, 1)
return possiblePoints[1]
end
function TUM.objectivesMaker.clear()
usedParkingSpots = {}
end
function TUM.objectivesMaker.create()
local zone = DCSEx.zones.getByName(TUM.settings.getValue(TUM.settings.id.TARGET_LOCATION, true))
local taskID = pickRandomTask()
if not taskID then
TUM.log("Failed to find a valid task.", TUM.logger.logLevel.WARNING)
return nil
end
local objectiveDB = Library.tasks[taskID]
local parkingInfo = nil
local spawnPoint2 = nil
local spawnPoint3 = nil
local isAirbaseTarget = false
local isSceneryTarget = false
if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.SCENERY_TARGET) then
local validSceneries = DCSEx.world.getSceneriesInZone(zone, DCSEx.zones.getRadius(zone), 250)
if not validSceneries or #validSceneries == 0 then
TUM.log("Failed to find a valid scenery object to use as target.", TUM.logger.logLevel.WARNING)
return nil
end
local pickedScenery = DCSEx.table.getRandom(validSceneries)
spawnPoint3 = DCSEx.table.deepCopy(pickedScenery:getPoint())
spawnPoint2 = DCSEx.math.vec3ToVec2(spawnPoint3)
isSceneryTarget = true
elseif DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.AIRBASE_TARGET) then
local validAirbases = DCSEx.zones.getAirbases(zone, TUM.settings.getEnemyCoalition())
if #validAirbases == 0 then
TUM.log("Failed to find a valid airbase to use as target.", TUM.logger.logLevel.WARNING)
return nil
end
local pickedAirbase = DCSEx.table.getRandom(validAirbases)
spawnPoint3 = DCSEx.table.deepCopy(pickedAirbase:getPoint())
spawnPoint2 = DCSEx.math.vec3ToVec2(spawnPoint3)
isAirbaseTarget = true
elseif DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.PARKED_AIRCRAFT_TARGET) then
local validAirbases = DCSEx.zones.getAirbases(zone, TUM.settings.getEnemyCoalition())
if #validAirbases == 0 then
TUM.log("Failed to find a valid airbase to use as target.", TUM.logger.logLevel.WARNING)
return nil
end
local pickedAirbase = DCSEx.table.getRandom(validAirbases)
local parkings = pickedAirbase:getParking()
local validParkings = {}
for _,p in pairs(parkings) do
local parkingUniqueID = pickedAirbase:getID() * 10000 + p.Term_Index
if p.Term_Type == 104 and not DCSEx.table.contains(usedParkingSpots, parkingUniqueID) then
table.insert(validParkings, p)
end
end
if #validParkings == 0 then
TUM.log("Failed to find a valid airbase parking to spawn a target.", TUM.logger.logLevel.WARNING)
return nil
end
local pickedParking = DCSEx.table.getRandom(validParkings)
table.insert(usedParkingSpots, pickedAirbase:getID() * 10000 + pickedParking.Term_Index) -- Mark parking spot as used so it won't be taken by another objective
parkingInfo = { airbaseID = pickedAirbase:getID(), parkingID = pickedParking.Term_Index }
spawnPoint3 = pickedParking.vTerminalPos
spawnPoint2 = DCSEx.math.vec3ToVec2(spawnPoint3)
elseif objectiveDB.surfaceType == land.SurfaceType.WATER then
spawnPoint2 = pickWaterPoint(zone)
if not spawnPoint2 then
spawnPoint2 = DCSEx.world.getSpawnPoint(zone, objectiveDB.surfaceType, objectiveDB.safeRadius)
end
else
spawnPoint2 = DCSEx.world.getSpawnPoint(zone, objectiveDB.surfaceType, objectiveDB.safeRadius)
end
if not spawnPoint2 then
TUM.log("Failed to find a spawn point for objective.", TUM.logger.logLevel.WARNING)
return nil
end
if not spawnPoint3 then spawnPoint3 = DCSEx.math.vec2ToVec3(spawnPoint2, "land") end
if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.ON_ROADS) then
spawnPoint2 = DCSEx.world.getClosestPointOnRoadsVec2(spawnPoint2)
end
local objective = {
completed = false,
completedUnitsID = {},
isAirbaseTarget = isAirbaseTarget,
isSceneryTarget = isSceneryTarget,
markerID = DCSEx.world.getNextMarkerID(),
markerTextID = DCSEx.world.getNextMarkerID(),
name = Library.objectiveNames.get():upper(),
parkingInfo = parkingInfo,
point2 = DCSEx.table.deepCopy(spawnPoint2),
point3 = DCSEx.table.deepCopy(spawnPoint3),
preciseCoordinates = objectiveDB.waypointInaccuracy <= 0,
taskID = taskID,
unitsID = {}
}
if objectiveDB.waypointInaccuracy <= 0 then -- Exact coordinates are available
objective.waypoint2 = DCSEx.table.deepCopy(objective.point2)
objective.waypoint3 = DCSEx.table.deepCopy(objective.point3)
else -- No exact coordinates available, create the waypoint near the target
objective.waypoint2 = DCSEx.math.randomPointInCircle(objective.point2, objectiveDB.waypointInaccuracy)
objective.waypoint3 = DCSEx.math.vec2ToVec3(objective.waypoint2, "land")
end
if not isAirbaseTarget and not isSceneryTarget then
-- Check group options
local groupOptions = {}
if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.MOVING) then
local destPoint = DCSEx.math.randomPointInCircle(objective.point2, 5000, 2500, land.SurfaceType.LAND)
if destPoint then
groupOptions.isMoving = true
if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.ON_ROADS) then
groupOptions.onRoad = true
destPoint = DCSEx.world.getClosestPointOnRoadsVec2(destPoint)
end
groupOptions.moveTo = destPoint
end
end
-- Parked aircraft only
if parkingInfo then
groupOptions.airbaseID = parkingInfo.airbaseID
groupOptions.invisible = true -- Not ideal because wingmen can't be tasked with attacking targets, but only way I've found to prevent friendly CAP from attacking parked aircraft
groupOptions.parkingID = parkingInfo.parkingID
end
-- Target group belongs to the enemy coalition, unless DCSEx.enums.taskFlag.FRIENDLY_TARGET is set
local groupCoalition = TUM.settings.getEnemyCoalition()
local groupFaction = TUM.settings.getEnemyFaction()
if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.FRIENDLY_TARGET) then
groupCoalition = TUM.settings.getPlayerCoalition()
groupFaction = TUM.settings.getPlayerFaction()
-- Friendly target groups are immortal and invisible, so AI won't kill them before the player got a chance to interact with them
groupOptions.immortal = true
groupOptions.invisible = true
end
local units = Library.factions.getUnits(groupFaction, objectiveDB.targetFamilies, math.random(objectiveDB.targetCount[1], objectiveDB.targetCount[2]))
local groupInfo = nil
if objectiveDB.targetFamilies[1] == DCSEx.enums.unitFamily.STATIC_STRUCTURE then
if units and #units >= 1 then
groupInfo = {}
groupInfo.unitsID = { DCSEx.unitGroupMaker.createStatic(TUM.settings.getEnemyCoalition(), objective.point2, units[1], "") }
end
else
groupInfo = DCSEx.unitGroupMaker.create(groupCoalition, DCSEx.dcs.getUnitCategoryFromFamily(objectiveDB.targetFamilies[1]), objective.point2, units, groupOptions)
end
if not groupInfo then
TUM.log("Failed to spawn a group for objective.", TUM.logger.logLevel.WARNING)
return nil
end
objective.groupID = groupInfo.groupID
if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.DESTROY_TRACK_RADARS_ONLY) then
objective.unitsID = {}
for i=1,#groupInfo.unitTypeNames do
if Unit.getDescByName(groupInfo.unitTypeNames[i]).attributes["SAM TR"] then
table.insert(objective.unitsID, groupInfo.unitsID[i])
end
end
if #objective.unitsID == 0 then
objective.unitsID = DCSEx.table.deepCopy(groupInfo.unitsID)
end
else
objective.unitsID = DCSEx.table.deepCopy(groupInfo.unitsID)
end
end
---------------------------------------------------------------------
-- Create dot marker (accurate WPs) or circle marker (inaccurate WPs)
---------------------------------------------------------------------
if objectiveDB.waypointInaccuracy <= 0 then
trigger.action.markToAll(objective.markerID, "Objective "..objective.name.."\n\n"..DCSEx.world.getCoordinatesAsString(objective.point3, false), objective.point3, true)
else
local circleRadius = math.max(objectiveDB.waypointInaccuracy, 1000)
trigger.action.circleToAll(
-1, objective.markerID,
objective.waypoint3, circleRadius,
{ 1, 1, 1, 1 }, { 1, 0, 0, 0.25 } , 2, true)
end
---------------------
-- Create text marker
---------------------
local textPoint3 = DCSEx.table.deepCopy(objective.waypoint3)
textPoint3.x = textPoint3.x + 224
textPoint3.z = textPoint3.z + 224
-- Text marker is created with an empty string, its content will be updated by TUM.MissionObjectives when it's added
trigger.action.textToAll(-1, objective.markerTextID, textPoint3, { 1, 1, 1, 1 }, { 0, 0, 0, .5 }, 12, true, "")
return objective
end
end