documentation update and multi zone per stage support
This commit is contained in:
Binary file not shown.
Binary file not shown.
+30
-100
@@ -1,68 +1,4 @@
|
||||
--[[
|
||||
|
||||
#### Why?
|
||||
For Spearhead there's a lot of stages and states the mission can be in. <br/>
|
||||
To make sure CAP units will be at the place where the mission maker expects them to be there's a naming convention that should help. <br/>
|
||||
You as a mission maker will have full controll over where they are supposed to be, the script will take care of getting them there.
|
||||
The CAP manager's goal is to provide dedicated aircraft scheduling that doesn't reset every stage reset.
|
||||
|
||||
Naming: CAP\_\<"A" | B"\>\<Config\>_\<Free form name\>
|
||||
#### Config:
|
||||
```
|
||||
1 at x: [<activeStage>]<capStage>
|
||||
n and n at x: [<activeStage>,<activeStage>]<capStage>
|
||||
n till n at x: [<activeStage>-<activeStage>]<capStage>
|
||||
n till n and n at x: [<activeStage>-<activeStage>,<activeStage>]<capStage>
|
||||
n till n at Active: [<activeStage>-<activeStage>]A
|
||||
|
||||
divider: |
|
||||
|
||||
examples:
|
||||
|
||||
CAP_A[1-4,6]7|[5,7]8_SomeName => Will fly CAP at stage 7 when stages 1 through 4 and 6 are active and will fly CAP at 8 when 5 and 7 are active
|
||||
CAP_A[2-5]5|[6]6_SomeName => Will fly CAP at stage 5 when stages 2 through 5 active and will fly CAP at 6 when 6 is active
|
||||
CAP_A[1-5]A|[6]7_SomeName => Will fly CAP at the ACTIVE stage if Stages 1-5 are active. Basically following the active stages. Then when 6 is active it will fly in 7
|
||||
|
||||
CAP_B[1-5]A|[6]7_SomeName => Will fly BACKUP CAP for the active zones 1 through 5 and back up for 7 when 6 is active.
|
||||
|
||||
```
|
||||
|
||||
### How many? And how to add backups?
|
||||
|
||||
To fascilitate a nice flow of the mission and also make sure it doesn't oversaturate the zones with aircraft the script works with a Active/Backup system in the naming. <br/>
|
||||
This really doesn't mean much per se once the mission runs, but most importantly is that the A units define how many groups there should be max in a zone at a time. <br/>
|
||||
The B units will simply be used to fill that amount if the A units can't due to RTB, Death, Rearming etc. <br/>
|
||||
|
||||
#### Example
|
||||
|
||||
Take the units:
|
||||
```
|
||||
CAP_A[1-5]A_SomeName1
|
||||
CAP_A[1-5]A_SomeName2
|
||||
CAP_B[1-3,5]A_SomeName
|
||||
```
|
||||
`CAP_A...` units are primary units where the `CAP_B...` units are the backups. <br/>
|
||||
In this case the CAP manager sees that for stages 1 through 5 this configuration requires 2 groups in the active zone. <br/>
|
||||
If one of those 2 groups dies or is going back to base the B group will be used to top up the CAP units at that zone. <br/>
|
||||
After scheduling the B units the A units that are back at base ready on the ramp will also not be scheduled until the CAP units that are active in the zone (inlcuding B units) drop below the required CAP unit (of 2 in this example)
|
||||
|
||||
In this example there is no Backup unit for zone 4. This might quiet down the CAP a little as the Active groups will have to rearm and refuel without there being any backup.
|
||||
|
||||
### What the cap manager does:
|
||||
- Spawn aircraft on the ramp (or despawn when they are not needed anymore for culling)
|
||||
- Send out aircraft based on where they are supposed to be
|
||||
- Send Aircraft RTB after X time. <br/>
|
||||
RTB in this sense means back to its base of origin. Not the closest friendly base like DCS does.
|
||||
- Simulates Rearming and then sending them out when needed.
|
||||
- Delays aircraft for X amount of time before spawning and rearming after a groups demise.
|
||||
- Aircraft are spawned on the ramp so OCA does have effect. (Be sure to also take a look at the Airbase and SAM spawning for defences)
|
||||
|
||||
### Future Ideas
|
||||
|
||||
- Aircraft rearm hubs with finite spawns on other airbases that get replenished by aircraft flying in.
|
||||
|
||||
|
||||
]] --
|
||||
|
||||
local CapHelper = {}
|
||||
do
|
||||
@@ -193,7 +129,6 @@ function CapGroup:new(groupName, airbaseId, logger, database, capConfig)
|
||||
o.isBackup = parsed.isBackup
|
||||
|
||||
--vars
|
||||
o.assignedStageName = nil
|
||||
o.assignedStageNumber = nil
|
||||
|
||||
o.state = CapGroup.GroupState.UNSPAWNED
|
||||
@@ -294,47 +229,42 @@ function CapGroup:new(groupName, airbaseId, logger, database, capConfig)
|
||||
return --Can't task a unit that's dead or RTB
|
||||
end
|
||||
|
||||
local stageZoneName = self.database:getStageZoneByStageNumber(stageZoneNumber)
|
||||
self.assignedStageNumber = stageZoneNumber
|
||||
self.assignedStageName = stageZoneName
|
||||
local group = Group.getByName(self.groupName)
|
||||
if group and group:isExist() then
|
||||
local zone = Spearhead.DcsUtil.getZoneByName(stageZoneName)
|
||||
if zone then
|
||||
self.logger:debug("Sending group out " .. self.groupName)
|
||||
local controller = group:getController()
|
||||
local capPoints = database:getCapRouteInZone(stageZoneName, self.airbaseId) or { point1 = { x = zone.x, z = zone.z }, point2 = nil }
|
||||
self.logger:debug("Sending group out " .. self.groupName)
|
||||
local controller = group:getController()
|
||||
local capPoints = database:getCapRouteInZone(stageZoneNumber, self.airbaseId)
|
||||
|
||||
local altitude = math.random(self.capConfig.minAlt, self.capConfig.maxAlt)
|
||||
local speed = math.random(self.capConfig.minSpeed, self.capConfig.maxSpeed)
|
||||
local attackHelos = false
|
||||
local deviationDistance = self.capConfig.maxDeviationRange
|
||||
local capTask
|
||||
if self.state == CapGroup.GroupState.ONRAMP or self.onStationSince == 0 then
|
||||
controller:setCommand({
|
||||
id = 'Start',
|
||||
params = {}
|
||||
})
|
||||
local duration = math.random(self.capConfig.minDurationOnStation, self.capConfig
|
||||
.maxDurationOnstation)
|
||||
self.logger:debug("random schedule min: " ..
|
||||
tostring(self.capConfig.minDurationOnStation or "nil") ..
|
||||
" max: " .. tostring(self.capConfig.maxDurationOnstation or "nil") .. " actual " .. duration)
|
||||
self.currentCapTaskingDuration = duration
|
||||
local altitude = math.random(self.capConfig.minAlt, self.capConfig.maxAlt)
|
||||
local speed = math.random(self.capConfig.minSpeed, self.capConfig.maxSpeed)
|
||||
local attackHelos = false
|
||||
local deviationDistance = self.capConfig.maxDeviationRange
|
||||
local capTask
|
||||
if self.state == CapGroup.GroupState.ONRAMP or self.onStationSince == 0 then
|
||||
controller:setCommand({
|
||||
id = 'Start',
|
||||
params = {}
|
||||
})
|
||||
local duration = math.random(self.capConfig.minDurationOnStation, self.capConfig
|
||||
.maxDurationOnstation)
|
||||
self.logger:debug("random schedule min: " ..
|
||||
tostring(self.capConfig.minDurationOnStation or "nil") ..
|
||||
" max: " .. tostring(self.capConfig.maxDurationOnstation or "nil") .. " actual " .. duration)
|
||||
self.currentCapTaskingDuration = duration
|
||||
|
||||
|
||||
capTask = Spearhead.RouteUtil.createCapMission(self.groupName, self.airbaseId, capPoints.point1, capPoints.point2, altitude, speed, duration, attackHelos, deviationDistance)
|
||||
else
|
||||
local duration = self.currentCapTaskingDuration - (timer.getTime() - o.onStationSince)
|
||||
capTask = Spearhead.RouteUtil.createCapMission(self.groupName, self.airbaseId, capPoints.point1, capPoints.point2, altitude, speed, duration, attackHelos, deviationDistance)
|
||||
end
|
||||
|
||||
if capTask then
|
||||
timer.scheduleFunction(setTaskAsync,
|
||||
{ task = capTask, groupName = self.groupName, logger = self.logger }, timer.getTime() + 3)
|
||||
end
|
||||
self:SetState(CapGroup.GroupState.INTRANSIT)
|
||||
|
||||
capTask = Spearhead.RouteUtil.createCapMission(self.groupName, self.airbaseId, capPoints.point1, capPoints.point2, altitude, speed, duration, attackHelos, deviationDistance)
|
||||
else
|
||||
local duration = self.currentCapTaskingDuration - (timer.getTime() - o.onStationSince)
|
||||
capTask = Spearhead.RouteUtil.createCapMission(self.groupName, self.airbaseId, capPoints.point1, capPoints.point2, altitude, speed, duration, attackHelos, deviationDistance)
|
||||
end
|
||||
|
||||
if capTask then
|
||||
timer.scheduleFunction(setTaskAsync,
|
||||
{ task = capTask, groupName = self.groupName, logger = self.logger }, timer.getTime() + 3)
|
||||
end
|
||||
self:SetState(CapGroup.GroupState.INTRANSIT)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+27
-3
@@ -42,9 +42,33 @@ A CAP group needs to follow the following naming convention: `CAP_<A|B><CONFIG>_
|
||||
|
||||
For details on config read this: [CAP Group Config](./Reference.html#cap-group-config)
|
||||
|
||||
<img src="img/starting_stages.png" alt="drawing" width="35%"/>
|
||||
<img src="img/starting_stages.png" alt="drawing" width="35%"/>
|
||||
<br/>
|
||||
For now I set up 3 groups with the following names. `CAP_A[1]1_Rota1` , `CAP_A[1]1_Rota1-1` , `CAP_B[1]1_Rota1` <br/>
|
||||
The first two are marked with `A` and will therefore be primary CAP units. They will be scheduled and make up for the total count. <br/>
|
||||
Meaning that for this airbase there is 2 CAP units max at a time flying out. <br/>
|
||||
In this case all groups have `[1]1` in the name, (This would be the same as `[1]A`) which means that when stage 1 is active the groups will activate and fly out to stage 1.
|
||||
|
||||
I also set up a few groups further back. One example: `CAP_A[1-3]3_Group1`. This group will protect zone 3 when zones 1 through 3 are active.
|
||||
|
||||
CAP units fly out, fly their CAP zone for x amount of minutes and will then RTB. <br/>
|
||||
Before they actually RTB an event is triggered 10 minutes before the actual RTB task. This event will trigger a backup unit to startup and fly out to take over. <br/>
|
||||
|
||||
Best is to test it out and see for yourself. <br/>
|
||||
|
||||
### Creating CAP routes
|
||||
|
||||
Creating cap routes is not needed per se, but with a multi-stage stage (we have 2 stages with `_1_`) it is recommended. <br/>
|
||||
Similarly with huge stages. <br/>
|
||||
If there is multiple zones is will "round-robin" over them. <br/>
|
||||
|
||||
If no CAP route is present the unit will fly a route generated differently per zone: <br/>
|
||||
`quad zone` => race-track between the corner closest to the origin airbase to the center point of the zone <br/>
|
||||
`circle zone` => race-track between the closest point on circle to the origin airbase to the center <br/>
|
||||
|
||||
If you want to create you own CAP Routes you can! <br/>
|
||||
For this example I created 2 CAP routes inside of the 2 `_1_` stages. <br/>
|
||||
|
||||
As you can see below there's a nice quick you can exploit. As long as the `X` of the zone is inside of the the `CAPROUTE` will be used!
|
||||
|
||||

|
||||
|
||||
## Setting up the missions
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 296 KiB |
+22
-18
@@ -30,6 +30,7 @@ do -- DB
|
||||
o.tables.cap_route_zones = {}
|
||||
|
||||
o.tables.stage_zonesByNumer = {}
|
||||
o.tables.stage_numberPerzone = {}
|
||||
|
||||
do -- INIT ZONE TABLES
|
||||
if env.mission.triggers and env.mission.triggers.zones then
|
||||
@@ -43,11 +44,10 @@ do -- DB
|
||||
if split_string[2] then
|
||||
local stringified = tostring(split_string[2]) or "unknown"
|
||||
if o.tables.stage_zonesByNumer[stringified] == nil then
|
||||
o.tables.stage_zonesByNumer[stringified] = zone_name
|
||||
else
|
||||
table.insert(Spearhead.MissionEditingWarnings, "Duplicate Stage Order number found. This zone will work, but will not be part of the CAP script")
|
||||
o.tables.stage_zonesByNumer[stringified] = {}
|
||||
end
|
||||
|
||||
table.insert(o.tables.stage_zonesByNumer[stringified], zone_name)
|
||||
o.tables.stage_numberPerzone[zone_name] = stringified
|
||||
end
|
||||
end
|
||||
|
||||
@@ -384,19 +384,23 @@ do -- DB
|
||||
cleanup()
|
||||
|
||||
--- key: zoneName value: { current, routes = [ { point1, point2 } ] }
|
||||
o.tables.capRoutes = {}
|
||||
o.tables.capRoutesPerStageNumber = {}
|
||||
for _, zoneName in pairs(o.tables.stage_zones) do
|
||||
local number = tostring(o.tables.stage_numberPerzone[zoneName] or "unknown")
|
||||
|
||||
if o.tables.capRoutesPerStageNumber[number] == nil then
|
||||
o.tables.capRoutesPerStageNumber[number] = {
|
||||
current = 0,
|
||||
routes = {}
|
||||
}
|
||||
end
|
||||
|
||||
local configValue = {
|
||||
current = 0,
|
||||
routes = {}
|
||||
}
|
||||
for _, cap_route_zone in pairs(o.tables.cap_route_zones) do
|
||||
if Spearhead.DcsUtil.isZoneInZone(cap_route_zone, zoneName) == true then
|
||||
local zone = Spearhead.DcsUtil.getZoneByName(cap_route_zone)
|
||||
if zone then
|
||||
if zone.zone_type == Spearhead.DcsUtil.ZoneType.Cilinder then
|
||||
table.insert(configValue.routes, { point1 = { x = zone.x , z = zone.z }, point2 = nil } )
|
||||
table.insert(o.tables.capRoutesPerStageNumber[number].routes, { point1 = { x = zone.x , z = zone.z }, point2 = nil } )
|
||||
else
|
||||
local function getDist(a, b)
|
||||
return math.sqrt((b.x - a.x) ^ 2 + (b.z - a.z) ^ 2)
|
||||
@@ -422,7 +426,7 @@ do -- DB
|
||||
end
|
||||
|
||||
if biggestA and biggestB then
|
||||
table.insert(configValue.routes,
|
||||
table.insert(o.tables.capRoutesPerStageNumber[number].routes,
|
||||
{
|
||||
point1 = { x = biggestA.x , z = biggestA.z },
|
||||
point2 = { x = biggestB.x , z = biggestB.z }
|
||||
@@ -432,8 +436,8 @@ do -- DB
|
||||
end
|
||||
end
|
||||
end
|
||||
o.tables.capRoutes[zoneName] = configValue
|
||||
end
|
||||
o.Logger:debug(o.tables.capRoutesPerStageNumber)
|
||||
|
||||
o.tables.missionCodes = {}
|
||||
end
|
||||
@@ -442,9 +446,9 @@ do -- DB
|
||||
return self.tables.descriptions[missionZoneName]
|
||||
end
|
||||
|
||||
o.getCapRouteInZone = function(self, targetZone, baseId)
|
||||
|
||||
local routeData = self.tables.capRoutes[targetZone]
|
||||
o.getCapRouteInZone = function(self, stageNumber, baseId)
|
||||
local stageNumber = tostring(stageNumber) or "nothing"
|
||||
local routeData = self.tables.capRoutesPerStageNumber[stageNumber]
|
||||
if routeData then
|
||||
local count = Spearhead.Util.tableLength(routeData.routes)
|
||||
if count > 0 then
|
||||
@@ -452,7 +456,6 @@ do -- DB
|
||||
if count < routeData.current then
|
||||
routeData.current = 1
|
||||
end
|
||||
|
||||
return routeData.routes[routeData.current]
|
||||
end
|
||||
end
|
||||
@@ -465,7 +468,8 @@ do -- DB
|
||||
local aY = pC.z + vY / magV * radius;
|
||||
return { x = aX, z = aY }
|
||||
end
|
||||
local stagezone = Spearhead.DcsUtil.getZoneByName(targetZone)
|
||||
local stageZoneName = Spearhead.Util.randomFromList(self.tables.stage_zonesByNumer[stageNumber]) or "none"
|
||||
local stagezone = Spearhead.DcsUtil.getZoneByName(stageZoneName)
|
||||
if stagezone then
|
||||
local base = Spearhead.DcsUtil.getAirbaseById(baseId)
|
||||
if base then
|
||||
@@ -501,7 +505,7 @@ do -- DB
|
||||
---@param self table
|
||||
---@param number number
|
||||
---@return string zoneName
|
||||
o.getStageZoneByStageNumber = function (self, number)
|
||||
o.getStageZonesByStageNumber = function (self, number)
|
||||
local numberString = tostring(number)
|
||||
return self.tables.stage_zonesByNumer[numberString]
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user