-- ==================================================================================== -- 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