diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
index b4faa62c8..48870d12c 100644
--- a/.github/workflows/gh-pages.yml
+++ b/.github/workflows/gh-pages.yml
@@ -67,3 +67,12 @@ jobs:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
+
+ check:
+ runs-on: ubuntu-latest
+ needs: deploy
+ steps:
+ - name: Setup Node
+ uses: actions/setup-node@v3
+ - run: npm install linkinator
+ - run: npx linkinator https://flightcontrol-master.github.io/MOOSE/ --recurse
diff --git a/.gitignore b/.gitignore
index 64684a062..38430d132 100644
--- a/.gitignore
+++ b/.gitignore
@@ -234,3 +234,5 @@ MooseCodeWS.code-workspace
# Excludes for act (https://github.com/nektos/act)
.secrets
.env
+.actrc
+.vars
diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua
index 267b2f4e2..26a0a99e7 100644
--- a/Moose Development/Moose/DCS.lua
+++ b/Moose Development/Moose/DCS.lua
@@ -135,6 +135,22 @@ do -- env
end -- env
+do -- radio
+
+ ---@type radio
+ -- @field #radio.modulation modulation
+
+ ---
+ -- @type radio.modulation
+ -- @field AM
+ -- @field FM
+
+ radio = {}
+ radio.modulation = {}
+ radio.modulation.AM = 0
+ radio.modulation.FM = 1
+
+end
do -- timer
@@ -329,11 +345,11 @@ end -- country
do -- Command
- --- @type Command
+ -- @type Command
-- @field #string id
-- @field #Command.params params
- --- @type Command.params
+ -- @type Command.params
end -- Command
@@ -374,7 +390,7 @@ end -- coalition
do -- Types
- --- @type Desc
+ -- @type Desc
-- @field #number speedMax0 Max speed in meters/second at zero altitude.
-- @field #number massEmpty Empty mass in kg.
-- @field #number tankerType Type of refueling system: 0=boom, 1=probe.
@@ -471,16 +487,16 @@ do -- Types
-- @type AttributeNameArray
-- @list <#AttributeName>
- --- @type Zone
+ -- @type Zone
-- @field DCSVec3#Vec3 point
-- @field #number radius
Zone = {}
- --- @type ModelTime
+ -- @type ModelTime
-- @extends #number
- --- @type Time
+ -- @type Time
-- @extends #number
--- A task descriptor (internal structure for DCS World). See [https://wiki.hoggitworld.com/view/Category:Tasks](https://wiki.hoggitworld.com/view/Category:Tasks).
@@ -489,7 +505,7 @@ do -- Types
-- @field #string id
-- @field #Task.param param
- --- @type Task.param
+ -- @type Task.param
--- List of @{#Task}
-- @type TaskArray
@@ -536,7 +552,7 @@ do -- Object
-- @field SCENERY
-- @field CARGO
- --- @type Object.Desc
+ -- @type Object.Desc
-- @extends #Desc
-- @field #number life initial life level
-- @field #Box3 box bounding box of collision geometry
@@ -1085,7 +1101,7 @@ end -- Controller
do -- Unit
- --- @type Unit
+ -- @type Unit
-- @extends #CoalitionObject
-- @field ID Identifier of an unit. It assigned to an unit by the Mission Editor automatically.
-- @field #Unit.Category Category
@@ -1200,15 +1216,18 @@ do -- Unit
-- @field #Distance detectionDistanceHRM detection distance for RCS=1m^2 in high-resolution mapping mode, nil if radar has no HRM
-- @field #Unit.Radar.detectionDistanceAir detectionDistanceAir detection distance for RCS=1m^2 airborne target, nil if radar doesn't support air search
- --- @type Unit.Radar.detectionDistanceAir
+ --- A radar.
+ -- @type Unit.Radar.detectionDistanceAir
-- @field #Unit.Radar.detectionDistanceAir.upperHemisphere upperHemisphere
-- @field #Unit.Radar.detectionDistanceAir.lowerHemisphere lowerHemisphere
- --- @type Unit.Radar.detectionDistanceAir.upperHemisphere
+ --- A radar.
+ -- @type Unit.Radar.detectionDistanceAir.upperHemisphere
-- @field #Distance headOn
-- @field #Distance tailOn
- --- @type Unit.Radar.detectionDistanceAir.lowerHemisphere
+ --- A radar.
+ -- @type Unit.Radar.detectionDistanceAir.lowerHemisphere
-- @field #Distance headOn
-- @field #Distance tailOn
@@ -1510,22 +1529,26 @@ do -- AI
-- @field IR_POINTER
-- @field LASER
- --- @type AI.Task.WaypointType
+ ---
+ -- @type AI.Task.WaypointType
-- @field TAKEOFF
-- @field TAKEOFF_PARKING
-- @field TURNING_POINT
-- @field TAKEOFF_PARKING_HOT
-- @field LAND
- --- @type AI.Task.TurnMethod
+ ---
+ -- @type AI.Task.TurnMethod
-- @field FLY_OVER_POINT
-- @field FIN_POINT
- --- @type AI.Task.AltitudeType
+ ---
+ -- @type AI.Task.AltitudeType
-- @field BARO
-- @field RADIO
- --- @type AI.Task.VehicleFormation
+ ---
+ -- @type AI.Task.VehicleFormation
-- @field OFF_ROAD
-- @field ON_ROAD
-- @field RANK
@@ -1535,27 +1558,30 @@ do -- AI
-- @field ECHELON_LEFT
-- @field ECHELON_RIGHT
- --- @type AI.Option
+ ---
+ -- @type AI.Option
-- @field #AI.Option.Air Air
-- @field #AI.Option.Ground Ground
-- @field #AI.Option.Naval Naval
- --- @type AI.Option.Air
+ ---
+ -- @type AI.Option.Air
-- @field #AI.Option.Air.id id
-- @field #AI.Option.Air.val val
- --- @type AI.Option.Ground
+ ---
+ -- @type AI.Option.Ground
-- @field #AI.Option.Ground.id id
-- @field #AI.Option.Ground.val val
-- @field #AI.Option.Ground.mid mid
-- @field #AI.Option.Ground.mval mval
--
- --- @type AI.Option.Naval
+ -- @type AI.Option.Naval
-- @field #AI.Option.Naval.id id
-- @field #AI.Option.Naval.val val
-
- --- @type AI.Option.Air.id
+ ---
+ -- @type AI.Option.Air.id
-- @field NO_OPTION
-- @field ROE
-- @field REACTION_ON_THREAT
@@ -1577,73 +1603,61 @@ do -- AI
-- @field OPTION_RADIO_USAGE_KILL
-- @field JETT_TANKS_IF_EMPTY
-- @field FORCED_ATTACK
-
- --- @type AI.Option.Air.id.FORMATION
- -- @field LINE_ABREAST
- -- @field TRAIL
- -- @field WEDGE
- -- @field ECHELON_RIGHT
- -- @field ECHELON_LEFT
- -- @field FINGER_FOUR
- -- @field SPREAD_FOUR
- -- @field WW2_BOMBER_ELEMENT
- -- @field WW2_BOMBER_ELEMENT_HEIGHT
- -- @field WW2_FIGHTER_VIC
- -- @field HEL_WEDGE
- -- @field HEL_ECHELON
- -- @field HEL_FRONT
- -- @field HEL_COLUMN
- -- @field COMBAT_BOX
- -- @field JAVELIN_DOWN
-
- --- @type AI.Option.Air.val
+ ---
+ -- @type AI.Option.Air.val
-- @field #AI.Option.Air.val.ROE ROE
-- @field #AI.Option.Air.val.REACTION_ON_THREAT REACTION_ON_THREAT
-- @field #AI.Option.Air.val.RADAR_USING RADAR_USING
-- @field #AI.Option.Air.val.FLARE_USING FLARE_USING
- --- @type AI.Option.Air.val.ROE
+ ---
+ -- @type AI.Option.Air.val.ROE
-- @field WEAPON_FREE
-- @field OPEN_FIRE_WEAPON_FREE
-- @field OPEN_FIRE
-- @field RETURN_FIRE
-- @field WEAPON_HOLD
-
- --- @type AI.Option.Air.val.REACTION_ON_THREAT
+
+ ---
+ -- @type AI.Option.Air.val.REACTION_ON_THREAT
-- @field NO_REACTION
-- @field PASSIVE_DEFENCE
-- @field EVADE_FIRE
-- @field BYPASS_AND_ESCAPE
-- @field ALLOW_ABORT_MISSION
- --- @type AI.Option.Air.val.RADAR_USING
+ ---
+ -- @type AI.Option.Air.val.RADAR_USING
-- @field NEVER
-- @field FOR_ATTACK_ONLY
-- @field FOR_SEARCH_IF_REQUIRED
-- @field FOR_CONTINUOUS_SEARCH
- --- @type AI.Option.Air.val.FLARE_USING
+ ---
+ -- @type AI.Option.Air.val.FLARE_USING
-- @field NEVER
-- @field AGAINST_FIRED_MISSILE
-- @field WHEN_FLYING_IN_SAM_WEZ
-- @field WHEN_FLYING_NEAR_ENEMIES
-
- --- @type AI.Option.Air.val.ECM_USING
+
+ ---
+ -- @type AI.Option.Air.val.ECM_USING
-- @field NEVER_USE
-- @field USE_IF_ONLY_LOCK_BY_RADAR
-- @field USE_IF_DETECTED_LOCK_BY_RADAR
-- @field ALWAYS_USE
-
- --- @type AI.Option.Air.val.MISSILE_ATTACK
+
+ ---
+ -- @type AI.Option.Air.val.MISSILE_ATTACK
-- @field MAX_RANGE
-- @field NEZ_RANGE
-- @field HALF_WAY_RMAX_NEZ
-- @field TARGET_THREAT_EST
-- @field RANDOM_RANGE
-
- --- @type AI.Option.Ground.id
+ ---
+ -- @type AI.Option.Ground.id
-- @field NO_OPTION
-- @field ROE @{#AI.Option.Ground.val.ROE}
-- @field FORMATION
@@ -1652,42 +1666,51 @@ do -- AI
-- @field ENGAGE_AIR_WEAPONS
-- @field AC_ENGAGEMENT_RANGE_RESTRICTION
- --- @type AI.Option.Ground.mid -- Moose added
+ ---
+ -- @type AI.Option.Ground.mid -- Moose added
-- @field RESTRICT_AAA_MIN 27
-- @field RESTRICT_AAA_MAX 29
-- @field RESTRICT_TARGETS @{#AI.Option.Ground.mval.ENGAGE_TARGETS} 28
- --- @type AI.Option.Ground.val
+ ---
+ -- @type AI.Option.Ground.val
-- @field #AI.Option.Ground.val.ROE ROE
-- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE
-- @field #AI.Option.Ground.val.ENGAGE_TARGETS RESTRICT_TARGETS
- --- @type AI.Option.Ground.val.ROE
+ ---
+ -- @type AI.Option.Ground.val.ROE
-- @field OPEN_FIRE
-- @field RETURN_FIRE
-- @field WEAPON_HOLD
- --- @type AI.Option.Ground.mval -- Moose added
+ ---
+ -- @type AI.Option.Ground.mval -- Moose added
-- @field #AI.Option.Ground.mval.ENGAGE_TARGETS ENGAGE_TARGETS
- --- @type AI.Option.Ground.mval.ENGAGE_TARGETS -- Moose added
+ ---
+ -- @type AI.Option.Ground.mval.ENGAGE_TARGETS -- Moose added
-- @field ANY_TARGET -- 0
-- @field AIR_UNITS_ONLY -- 1
-- @field GROUND_UNITS_ONLY -- 2
- --- @type AI.Option.Ground.val.ALARM_STATE
+ ---
+ -- @type AI.Option.Ground.val.ALARM_STATE
-- @field AUTO
-- @field GREEN
-- @field RED
- --- @type AI.Option.Naval.id
+ ---
+ -- @type AI.Option.Naval.id
-- @field NO_OPTION
-- @field ROE
- --- @type AI.Option.Naval.val
+ ---
+ -- @type AI.Option.Naval.val
-- @field #AI.Option.Naval.val.ROE ROE
- --- @type AI.Option.Naval.val.ROE
+ ---
+ -- @type AI.Option.Naval.val.ROE
-- @field OPEN_FIRE
-- @field RETURN_FIRE
-- @field WEAPON_HOLD
diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua
index 1c5f02e0f..97222cd6f 100644
--- a/Moose Development/Moose/Functional/Warehouse.lua
+++ b/Moose Development/Moose/Functional/Warehouse.lua
@@ -3616,9 +3616,10 @@ function WAREHOUSE:onafterStatus(From, Event, To)
end
-- Print queue after processing requests.
- self:_PrintQueue(self.queue, "Queue waiting")
- self:_PrintQueue(self.pending, "Queue pending")
-
+ if self.verbosity > 2 then
+ self:_PrintQueue(self.queue, "Queue waiting")
+ self:_PrintQueue(self.pending, "Queue pending")
+ end
-- Check fuel for all assets.
--self:_CheckFuel()
@@ -8234,7 +8235,7 @@ end
-- @return #number Request ID.
function WAREHOUSE:_GetIDsFromGroupName(groupname)
- ---@param #string text The text to analyse.
+ -- @param #string text The text to analyse.
local function analyse(text)
-- Get rid of #0001 tail from spawn.
diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua
index ed166e23d..9d918e5d9 100644
--- a/Moose Development/Moose/Ops/AirWing.lua
+++ b/Moose Development/Moose/Ops/AirWing.lua
@@ -54,7 +54,9 @@
-- @field #string takeoffType Take of type.
-- @field #boolean despawnAfterLanding Aircraft are despawned after landing.
-- @field #boolean despawnAfterHolding Aircraft are despawned after holding.
---
+-- @field #boolean capOptionPatrolRaceTrack Use closer patrol race track or standard orbit auftrag.
+-- @field #number capFormation If capOptionPatrolRaceTrack is true, set the formation, also.
+--
-- @extends Ops.Legion#LEGION
--- *I fly because it releases my mind from the tyranny of petty things.* -- Antoine de Saint-Exupery
@@ -128,6 +130,8 @@ AIRWING = {
pointsAWACS = {},
pointsRecon = {},
markpoints = false,
+ capOptionPatrolRaceTrack = false,
+ capFormation = nil,
}
--- Payload data.
@@ -179,7 +183,7 @@ AIRWING = {
--- AIRWING class version.
-- @field #string version
-AIRWING.version="0.9.3"
+AIRWING.version="0.9.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -699,6 +703,24 @@ function AIRWING:SetNumberCAP(n)
return self
end
+--- Set CAP flight formation.
+-- @param #AIRWING self
+-- @param #number Formation Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation).
+-- @return #AIRWING self
+function AIRWING:SetCAPFormation(Formation)
+ self.capFormation = Formation
+ return self
+end
+
+--- Set CAP close race track.We'll utilize the AUFTRAG PatrolRaceTrack instead of a standard race track orbit task.
+-- @param #AIRWING self
+-- @param #boolean OnOff If true, switch this on, else switch off. Off by default.
+-- @return #AIRWING self
+function AIRWING:SetCapCloseRaceTrack(OnOff)
+ self.capOptionPatrolRaceTrack = OnOff
+ return self
+end
+
--- Set number of TANKER flights with Boom constantly in the air.
-- @param #AIRWING self
-- @param #number Nboom Number of flights. Default 1.
@@ -1112,13 +1134,14 @@ end
-- @return #AIRWING self
function AIRWING:CheckCAP()
- local Ncap=0 --self:CountMissionsInQueue({AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT})
+ local Ncap=0
+
-- Count CAP missions.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
- if mission:IsNotOver() and mission.type==AUFTRAG.Type.GCICAP and mission.patroldata then
+ if mission:IsNotOver() and (mission.type==AUFTRAG.Type.GCICAP or mission.type == AUFTRAG.Type.PATROLRACETRACK) and mission.patroldata then
Ncap=Ncap+1
end
@@ -1130,8 +1153,18 @@ function AIRWING:CheckCAP()
local altitude=patrol.altitude+1000*patrol.noccupied
- local missionCAP=AUFTRAG:NewGCICAP(patrol.coord, altitude, patrol.speed, patrol.heading, patrol.leg)
-
+ local missionCAP = nil -- Ops.Auftrag#AUFTRAG
+
+ if self.capOptionPatrolRaceTrack then
+
+ missionCAP=AUFTRAG:NewPATROL_RACETRACK(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg, self.capFormation)
+
+ else
+
+ missionCAP=AUFTRAG:NewGCICAP(patrol.coord, altitude, patrol.speed, patrol.heading, patrol.leg)
+
+ end
+
missionCAP.patroldata=patrol
patrol.noccupied=patrol.noccupied+1
@@ -1150,7 +1183,7 @@ end
-- @return #AIRWING self
function AIRWING:CheckRECON()
- local Ncap=0 --self:CountMissionsInQueue({AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT})
+ local Ncap=0
-- Count CAP missions.
for _,_mission in pairs(self.missionqueue) do
@@ -1278,7 +1311,6 @@ function AIRWING:CheckAWACS()
end
-
for i=1,self.nflightsAWACS-N do
local patrol=self:_GetPatrolData(self.pointsAWACS)
diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua
index c70ce6377..92511531c 100644
--- a/Moose Development/Moose/Ops/Auftrag.lua
+++ b/Moose Development/Moose/Ops/Auftrag.lua
@@ -443,6 +443,7 @@ _AUFTRAGSNR=0
-- @field #string REARMING Rearming mission.
-- @field #string CAPTUREZONE Capture zone mission.
-- @field #string NOTHING Nothing.
+-- @field #string PATROLRACETRACK Patrol Racetrack.
AUFTRAG.Type={
ANTISHIP="Anti Ship",
AWACS="AWACS",
@@ -484,10 +485,10 @@ AUFTRAG.Type={
RELOCATECOHORT="Relocate Cohort",
AIRDEFENSE="Air Defence",
EWR="Early Warning Radar",
- --RECOVERYTANKER="Recovery Tanker",
REARMING="Rearming",
CAPTUREZONE="Capture Zone",
NOTHING="Nothing",
+ PATROLRACETRACK="Patrol Racetrack",
}
--- Special task description.
@@ -511,6 +512,7 @@ AUFTRAG.Type={
-- @field #string REARMING Rearming.
-- @field #string CAPTUREZONE Capture OPS zone.
-- @field #string NOTHING Nothing.
+-- @field #string PATROLRACETRACK Patrol Racetrack.
AUFTRAG.SpecialTask={
FORMATION="Formation",
PATROLZONE="PatrolZone",
@@ -532,6 +534,7 @@ AUFTRAG.SpecialTask={
REARMING="Rearming",
CAPTUREZONE="Capture Zone",
NOTHING="Nothing",
+ PATROLRACETRACK="Patrol Racetrack",
}
--- Mission status.
@@ -652,7 +655,7 @@ AUFTRAG.Category={
--- AUFTRAG class version.
-- @field #string version
-AUFTRAG.version="1.2.0"
+AUFTRAG.version="1.2.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1017,7 +1020,7 @@ end
-- @param #number Altitude Hover altitude in feet AGL. Default is 50 feet above ground.
-- @param #number Time Time in seconds to hold the hover. Default 300 seconds.
-- @param #number Speed Speed in knots to fly to the target coordinate. Default 150kn.
--- @param #number MissionAlt Altitide to fly towards the mission in feet AGL. Default 1000ft.
+-- @param #number MissionAlt Altitude to fly towards the mission in feet AGL. Default 1000ft.
-- @return #AUFTRAG self
function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt)
@@ -1049,6 +1052,58 @@ function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt)
return mission
end
+--- **[AIR]** Create an enhanced orbit race track mission. Planes will keep closer to the track.
+-- @param #AUFTRAG self
+-- @param Core.Point#COORDINATE Coordinate Where to start the race track.
+-- @param #number Altitude (Optional) Altitude in feet. Defaults to 20,000ft ASL.
+-- @param #number Speed (Optional) Speed in knots. Defaults to 300kn TAS.
+-- @param #number Heading (Optional) Heading in degrees, 0 to 360. Defaults to 90 degree (East).
+-- @param #number Leg (Optional) Leg of the race track in NM. Defaults to 10nm.
+-- @param #number Formation (Optional) Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation).
+-- @return #AUFTRAG self
+function AUFTRAG:NewPATROL_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg,Formation)
+
+ local mission = AUFTRAG:New(AUFTRAG.Type.PATROLRACETRACK)
+
+ -- Target.
+ mission:_TargetFromObject(Coordinate)
+
+ -- Set Altitude.
+ if Altitude then
+ mission.TrackAltitude=UTILS.FeetToMeters(Altitude)
+ else
+ mission.TrackAltitude=UTILS.FeetToMeters(20000)
+ end
+
+ -- Points
+ mission.TrackPoint1 = Coordinate
+
+ local leg = UTILS.NMToMeters(Leg) or UTILS.NMToMeters(10)
+
+ local heading = Heading or 90
+
+ if heading < 0 or heading > 360 then heading = 90 end
+
+ mission.TrackPoint2 = Coordinate:Translate(leg,heading,true)
+
+ -- Orbit speed in m/s TAS.
+ mission.TrackSpeed = UTILS.IasToTas(UTILS.KnotsToKmph(Speed or 300), mission.TrackAltitude)
+
+ -- Mission speed in km/h and altitude
+ mission.missionSpeed = UTILS.KnotsToKmph(Speed or 300)
+ mission.missionAltitude = mission.TrackAltitude * 0.9
+ mission.missionTask=ENUMS.MissionTask.CAP
+ mission.optionROE=ENUMS.ROE.ReturnFire
+ mission.optionROT=ENUMS.ROT.PassiveDefense
+
+ mission.categories={AUFTRAG.Category.AIRCRAFT}
+
+ mission.DCStask=mission:GetDCSMissionTask()
+
+ return mission
+end
+
+
--- **[AIR]** Create an ORBIT mission, which can be either a circular orbit or a race-track pattern.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Where to orbit.
@@ -1181,7 +1236,7 @@ function AUFTRAG:NewORBIT_GROUP(Group, Altitude, Speed, Leg, Heading, OffsetVec2
end
---- **[AIR]** Create a Ground Controlled CAP (GCICAP) mission. Flights with this task are considered for A2A INTERCEPT missions by the CHIEF class. They will perform a compat air patrol but not engage by
+--- **[AIR]** Create a Ground Controlled CAP (GCICAP) mission. Flights with this task are considered for A2A INTERCEPT missions by the CHIEF class. They will perform a combat air patrol but not engage by
-- themselfs. They wait for the CHIEF to tell them whom to engage.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Where to orbit.
@@ -2688,6 +2743,8 @@ function AUFTRAG:NewAUTO(EngageGroup)
mission=AUFTRAG:NewTANKER(Coordinate,Altitude,Speed,Heading,Leg,RefuelSystem)
elseif auftrag==AUFTRAG.Type.TROOPTRANSPORT then
mission=AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet,DropoffCoordinate,PickupCoordinate)
+ elseif auftrag==AUFTRAG.Type.PATROLRACETRACK then
+ mission=AUFTRAG:NewPATROL_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg,Formation)
else
end
@@ -6343,8 +6400,32 @@ function AUFTRAG:GetDCSMissionTask()
DCStask.params=param
- table.insert(DCStasks, DCStask)
+ table.insert(DCStasks, DCStask)
+
+ elseif self.type==AUFTRAG.Type.PATROLRACETRACK then
+ ---------------------
+ -- Enhanced Orbit Racetrack --
+ ---------------------
+
+ local DCStask={}
+ DCStask.id=AUFTRAG.SpecialTask.PATROLRACETRACK
+
+ local param={}
+ -- ONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation, Delay)
+
+ param.TrackAltitude = self.TrackAltitude
+ param.TrackSpeed = self.TrackSpeed
+ param.TrackPoint1 = self.TrackPoint1
+ param.TrackPoint2 = self.TrackPoint2
+ param.missionSpeed = self.missionSpeed
+ param.missionAltitude = self.missionAltitude
+ param.TrackFormation = self.TrackFormation
+
+ DCStask.params=param
+
+ table.insert(DCStasks, DCStask)
+
elseif self.type==AUFTRAG.Type.HOVER then
---------------------
@@ -6619,6 +6700,8 @@ function AUFTRAG:GetMissionTaskforMissionType(MissionType)
mtask=ENUMS.MissionTask.NOTHING
elseif MissionType==AUFTRAG.Type.HOVER then
mtask=ENUMS.MissionTask.NOTHING
+ elseif MissionType==AUFTRAG.Type.PATROLRACETRACK then
+ mtask=ENUMS.MissionTask.CAP
end
return mtask
diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua
index 5ab2e97da..5ed60451c 100644
--- a/Moose Development/Moose/Ops/Cohort.lua
+++ b/Moose Development/Moose/Ops/Cohort.lua
@@ -75,7 +75,7 @@ COHORT = {
livery = nil,
skill = nil,
legion = nil,
- Ngroups = nil,
+ --Ngroups = nil,
Ngroups = 0,
engageRange = nil,
tacanChannel = {},
@@ -1098,7 +1098,7 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
-- Assets on mission NOTHING are considered.
table.insert(assets, asset)
- elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and MissionType==AUFTRAG.Type.INTERCEPT then
+ elseif self.legion:IsAssetOnMission(asset, {AUFTRAG.Type.GCICAP, AUFTRAG.Type.PATROLRACETRACK}) and MissionType==AUFTRAG.Type.INTERCEPT then
-- Check if the payload of this asset is compatible with the mission.
-- Note: we do not check the payload as an asset that is on a GCICAP mission should be able to do an INTERCEPT as well!
@@ -1595,4 +1595,3 @@ end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-
diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua
index 248318fe7..0d2ce7d99 100644
--- a/Moose Development/Moose/Ops/EasyGCICAP.lua
+++ b/Moose Development/Moose/Ops/EasyGCICAP.lua
@@ -64,6 +64,7 @@
-- @field Core.Set#SET_ZONE NoGoZoneSet
-- @field #boolean Monitor
-- @field #boolean TankerInvisible
+-- @field #number CapFormation
-- @extends Core.Fsm#FSM
--- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown.
@@ -207,6 +208,7 @@ EASYGCICAP = {
NoGoZoneSet = nil,
Monitor = false,
TankerInvisible = true,
+ CapFormation = nil,
}
--- Internal Squadron data type
@@ -242,7 +244,7 @@ EASYGCICAP = {
--- EASYGCICAP class version.
-- @field #string version
-EASYGCICAP.version="0.0.8"
+EASYGCICAP.version="0.0.9"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -289,6 +291,7 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
self.repeatsonfailure = 3
self.Monitor = false
self.TankerInvisible = true
+ self.CapFormation = ENUMS.Formation.FixedWing.FingerFour.Group
-- Set some string id for output to DCS.log file.
self.lid=string.format("EASYGCICAP %s | ", self.alias)
@@ -313,6 +316,14 @@ end
-- Functions
-------------------------------------------------------------------------
+--- Set CAP formation.
+-- @param #EASYGCICAP self
+-- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group
+-- @return #EASYGCICAP self
+function EASYGCICAP:SetCAPFormation(Formation)
+ self.CapFormation = Formation
+ return self
+end
--- Set Tanker and AWACS to be invisible to enemy AI eyes
-- @param #EASYGCICAP self
@@ -476,6 +487,8 @@ end
function EASYGCICAP:_AddAirwing(Airbasename, Alias)
self:T(self.lid.."_AddAirwing "..Airbasename)
+ local CapFormation = self.CapFormation
+
-- Create Airwing
local CAP_Wing = AIRWING:New(Airbasename,Alias)
CAP_Wing:SetVerbosityLevel(3)
@@ -484,11 +497,15 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
CAP_Wing:SetAirbase(AIRBASE:FindByName(Airbasename))
CAP_Wing:SetRespawnAfterDestroyed()
CAP_Wing:SetNumberCAP(self.capgrouping)
+ CAP_Wing:SetCapCloseRaceTrack(true)
+ if CapFormation then
+ CAP_Wing:SetCAPFormation(CapFormation)
+ end
if #self.ManagedTK > 0 then
CAP_Wing:SetNumberTankerBoom(1)
CAP_Wing:SetNumberTankerProbe(1)
end
- if #self.ManagedAW > 0 then
+ if #self.ManagedEWR > 0 then
CAP_Wing:SetNumberAWACS(1)
end
if #self.ManagedREC > 0 then
@@ -515,6 +532,9 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
flightgroup:SetDetection(true)
flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet)
flightgroup:SetOutOfAAMRTB()
+ if CapFormation then
+ flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation)
+ end
end
if Mission.type == AUFTRAG.Type.TANKER or Mission.type == AUFTRAG.Type.AWACS or Mission.type == AUFTRAG.Type.RECON then
if TankerInvisible then
@@ -549,8 +569,8 @@ end
-- @param #EASYGCICAP self
-- @param #string AirbaseName Name of the Wing's airbase
-- @param Core.Point#COORDINATE Coordinate.
--- @param #number Altitude Defaults to 25000 feet.
--- @param #number Speed Defaults to 300 knots.
+-- @param #number Altitude Defaults to 25000 feet ASL.
+-- @param #number Speed Defaults to 300 knots TAS.
-- @param #number Heading Defaults to 90 degrees (East).
-- @param #number LegLength Defaults to 15 NM.
-- @return #EASYGCICAP self
@@ -888,7 +908,7 @@ function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames
self:T(self.lid.."_AddSquadron "..SquadName)
-- Add Squadrons
local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName)
- Squadron_One:AddMissionCapability({AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.ALERT5})
+ Squadron_One:AddMissionCapability({AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5})
--Squadron_One:SetFuelLowRefuel(true)
Squadron_One:SetFuelLowThreshold(0.3)
Squadron_One:SetTurnoverTime(10,20)
@@ -900,7 +920,7 @@ function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames
local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING
wing:AddSquadron(Squadron_One)
- wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.ALERT5},75)
+ wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5},75)
return self
end
@@ -1234,12 +1254,16 @@ function EASYGCICAP:onafterStatus(From,Event,To)
local capmission = 0
local interceptmission = 0
local reconmission = 0
+ local awacsmission = 0
+ local tankermission = 0
for _,_wing in pairs(self.wings) do
local count = _wing[1]:CountAssetsOnMission(MissionTypes,Cohort)
local count2 = _wing[1]:CountAssets(true,MissionTypes,Attributes)
- capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.GCICAP})
+ capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.GCICAP,AUFTRAG.Type.PATROLRACETRACK})
interceptmission = interceptmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.INTERCEPT})
reconmission = reconmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.RECON})
+ awacsmission = awacsmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.AWACS})
+ tankermission = tankermission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.TANKER})
assets = assets + count
instock = instock + count2
end
@@ -1251,6 +1275,8 @@ function EASYGCICAP:onafterStatus(From,Event,To)
text = text.."\nMissions: "..capmission+interceptmission
text = text.."\n - CAP: "..capmission
text = text.."\n - Intercept: "..interceptmission
+ text = text.."\n - AWACS: "..awacsmission
+ text = text.."\n - TANKER: "..tankermission
text = text.."\n - Recon: "..reconmission
MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug)
end
diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua
index 54fe4ef31..905b3d3fa 100644
--- a/Moose Development/Moose/Ops/FlightControl.lua
+++ b/Moose Development/Moose/Ops/FlightControl.lua
@@ -4,7 +4,7 @@
--
-- * Manage aircraft departure and arrival
-- * Handles AI and human players
--- * Limit number of AI groups taxiing, taking off and landing simultaniously
+-- * Limit number of AI groups taxiing, taking off and landing simultaneously
-- * Immersive voice overs via SRS text-to-speech
-- * Define holding patterns for airdromes
--
@@ -61,6 +61,7 @@
-- @field #number runwaydestroyed Time stamp (abs), when runway was destroyed. If `nil`, runway is operational.
-- @field #number runwayrepairtime Time in seconds until runway will be repaired after it was destroyed. Default is 3600 sec (one hour).
-- @field #boolean markerParking If `true`, occupied parking spots are marked.
+-- @field #boolean nosubs If `true`, SRS TTS is without subtitles.
-- @extends Core.Fsm#FSM
--- **Ground Control**: Airliner X, Good news, you are clear to taxi to the active.
@@ -122,7 +123,7 @@
-- * `Length` is the length of the pattern.
-- * `FlightLevelMin` is the lowest altitude at which aircraft can hold.
-- * `FlightLevelMax` is the highest altitude at which aircraft can hold.
--- * `Prio` is the priority of this holdig stacks. If multiple patterns are defined, patterns with higher prio will be filled first.
+-- * `Prio` is the priority of this holding stacks. If multiple patterns are defined, patterns with higher prio will be filled first.
--
-- # Parking Guard
--
@@ -137,13 +138,13 @@
--
-- # Limits for Inbound and Outbound Flights
--
--- You can define limits on how many aircraft are simultaniously landing and taking off. This avoids (DCS) problems where taxiing aircraft cause a "traffic jam" on the taxi way(s)
+-- You can define limits on how many aircraft are simultaneously landing and taking off. This avoids (DCS) problems where taxiing aircraft cause a "traffic jam" on the taxi way(s)
-- and bring the whole airbase effectively to a stand still.
--
-- ## Landing Limits
--
-- The number of groups getting landing clearance can be set with the @{#FLIGHTCONTROL.SetLimitLanding}(*Nlanding, Ntakeoff*) function.
--- The first parameter, `Nlanding`, defines how many groups get clearance simultaniously.
+-- The first parameter, `Nlanding`, defines how many groups get clearance simultaneously.
--
-- The second parameter, `Ntakeoff`, sets a limit on how many flights can take off whilst inbound flights still get clearance. By default, this is set to zero because the runway can only be used for takeoff *or*
-- landing. So if you have a flight taking off, inbound fights will have to wait until the runway is clear.
@@ -155,7 +156,7 @@
-- ## Taxiing/Takeoff Limits
--
-- The number of AI flight groups getting clearance to taxi to the runway can be set with the @{#FLIGHTCONTROL.SetLimitTaxi}(*Nlanding, Ntakeoff*) function.
--- The first parameter, `Ntaxi`, defines how many groups are allowed to taxi to the runway simultaniously. Note that once the AI starts to taxi, we loose complete control over it.
+-- The first parameter, `Ntaxi`, defines how many groups are allowed to taxi to the runway simultaneously. Note that once the AI starts to taxi, we loose complete control over it.
-- They will follow their internal logic to get the the runway and take off. Therefore, giving clearance to taxi is equivalent to giving them clearance for takeoff.
--
-- By default, the parameter only counts the number of flights taxiing *to* the runway. If you set the second parameter, `IncludeInbound`, to `true`, this will also count the flights
@@ -237,9 +238,9 @@
-- atcNellis:SetParkingGuardStatic("Static Generator F Template")
-- -- Set taxi speed limit to 25 knots.
-- atcNellis:SetSpeedLimitTaxi(25)
--- -- Set that max 3 groups are allowed to taxi simultaniously.
+-- -- Set that max 3 groups are allowed to taxi simultaneously.
-- atcNellis:SetLimitTaxi(3, false, 1)
--- -- Set that max 2 groups are allowd to land simultaniously and unlimited number (99) groups can land, while other groups are taking off.
+-- -- Set that max 2 groups are allowd to land simultaneously and unlimited number (99) groups can land, while other groups are taking off.
-- atcNellis:SetLimitLanding(2, 99)
-- -- Use Google for text-to-speech.
-- atcNellis:SetSRSTower(nil, nil, "en-AU-Standard-A", nil, nil, "D:\\Path To Google\\GoogleCredentials.json")
@@ -270,6 +271,7 @@ FLIGHTCONTROL = {
Nparkingspots = nil,
holdingpatterns = {},
hpcounter = 0,
+ nosubs = false,
}
--- Holding point. Contains holding stacks.
@@ -327,7 +329,7 @@ FLIGHTCONTROL.FlightStatus={
--- FlightControl class version.
-- @field #string version
-FLIGHTCONTROL.version="0.7.3"
+FLIGHTCONTROL.version="0.7.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -407,6 +409,7 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port,
self:SetFrequency(Frequency, Modulation)
self:SetMarkHoldingPattern(true)
self:SetRunwayRepairtime()
+ self.nosubs = false
-- Set SRS Port
self:SetSRSPort(Port or 5002)
@@ -556,6 +559,22 @@ function FLIGHTCONTROL:SetVerbosity(VerbosityLevel)
return self
end
+--- Set subtitles to appear on SRS TTS messages.
+-- @param #FLIGHTCONTROL self
+-- @return #FLIGHTCONTROL self
+function FLIGHTCONTROL:SwitchSubtitlesOn()
+ self.nosubs = false
+ return self
+end
+
+--- Set subtitles to appear on SRS TTS messages.
+-- @param #FLIGHTCONTROL self
+-- @return #FLIGHTCONTROL self
+function FLIGHTCONTROL:SwitchSubtitlesOff()
+ self.nosubs = true
+ return self
+end
+
--- Set the tower frequency.
-- @param #FLIGHTCONTROL self
-- @param #number Frequency Frequency in MHz. Default 305 MHz.
@@ -595,7 +614,7 @@ end
-- @param #string Culture Culture, e.g. "en-GB" (default).
-- @param #string Voice Specific voice. Overrides `Gender` and `Culture`.
-- @param #number Volume Volume. Default 1.0.
--- @param #string Label Name under which SRS transmitts.
+-- @param #string Label Name under which SRS transmits.
-- @param #string PathToGoogleCredentials Path to google credentials json file.
-- @param #number Port Server port for SRS
-- @return #FLIGHTCONTROL self
@@ -626,7 +645,7 @@ end
-- @param #string Culture Culture, e.g. "en-GB" (default).
-- @param #string Voice Specific voice. Overrides `Gender` and `Culture`. See [Google Voices](https://cloud.google.com/text-to-speech/docs/voices).
-- @param #number Volume Volume. Default 1.0.
--- @param #string Label Name under which SRS transmitts. Default `self.alias`.
+-- @param #string Label Name under which SRS transmits. Default `self.alias`.
-- @param #string PathToGoogleCredentials Path to google credentials json file.
-- @return #FLIGHTCONTROL self
function FLIGHTCONTROL:SetSRSTower(Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials)
@@ -644,7 +663,7 @@ end
-- @param #string Culture Culture, e.g. "en-US" (default).
-- @param #string Voice Specific voice. Overrides `Gender` and `Culture`.
-- @param #number Volume Volume. Default 1.0.
--- @param #string Label Name under which SRS transmitts. Default "Pilot".
+-- @param #string Label Name under which SRS transmits. Default "Pilot".
-- @param #string PathToGoogleCredentials Path to google credentials json file.
-- @return #FLIGHTCONTROL self
function FLIGHTCONTROL:SetSRSPilot(Gender, Culture, Voice, Volume, Label, PathToGoogleCredentials)
@@ -657,17 +676,17 @@ function FLIGHTCONTROL:SetSRSPilot(Gender, Culture, Voice, Volume, Label, PathTo
end
---- Set the number of aircraft groups, that are allowed to land simultaniously.
+--- Set the number of aircraft groups, that are allowed to land simultaneously.
-- Note that this restricts AI and human players.
--
-- By default, up to two groups get landing clearance. They are spaced out in time, i.e. after the first one got cleared, the second has to wait a bit.
-- This
--
-- By default, landing clearance is only given when **no** other flight is taking off. You can adjust this for airports with more than one runway or
--- in cases where simulatious takeoffs and landings are unproblematic. Note that only because there are multiple runways, it does not mean the AI uses them.
+-- in cases where simultaneous takeoffs and landings are unproblematic. Note that only because there are multiple runways, it does not mean the AI uses them.
--
-- @param #FLIGHTCONTROL self
--- @param #number Nlanding Max number of aircraft landing simultaniously. Default 2.
+-- @param #number Nlanding Max number of aircraft landing simultaneously. Default 2.
-- @param #number Ntakeoff Allowed number of aircraft taking off for groups to get landing clearance. Default 0.
-- @return #FLIGHTCONTROL self
function FLIGHTCONTROL:SetLimitLanding(Nlanding, Ntakeoff)
@@ -691,7 +710,7 @@ function FLIGHTCONTROL:SetLandingInterval(dt)
end
---- Set the number of **AI** aircraft groups, that are allowed to taxi simultaniously.
+--- Set the number of **AI** aircraft groups, that are allowed to taxi simultaneously.
-- If the limit is reached, other AI groups not get taxi clearance to taxi to the runway.
--
-- By default, this only counts the number of AI that taxi from their parking position to the runway.
@@ -887,7 +906,7 @@ end
-- Note that this is the time, the DCS engine uses not something we can control on a user level or we could get via scripting.
-- You need to input the value. On the DCS forum it was stated that this is currently one hour. Hence this is the default value.
-- @param #FLIGHTCONTROL self
--- @param #number RepairTime Time in seconds until the runway is repaired. Default 3600 sec (one hour).
+-- @param #number RepairTime Time in seconds until the runway is repaired. Default 3600sec (one hour).
-- @return #FLIGHTCONTROL self
function FLIGHTCONTROL:SetRunwayRepairtime(RepairTime)
self.runwayrepairtime=RepairTime or 3600
@@ -1010,7 +1029,7 @@ function FLIGHTCONTROL:onbeforeStatusUpdate()
if Tqueue>0 then
-- Debug info.
local text=string.format("Still got %d messages in the radio queue. Will call status again in %.1f sec", #self.msrsqueue, Tqueue)
- self:I(self.lid..text)
+ self:T(self.lid..text)
-- Call status again in dt seconds.
self:__StatusUpdate(-Tqueue)
@@ -2753,9 +2772,10 @@ function FLIGHTCONTROL:_PlayerInfoATIS(groupname)
if flight then
local text=string.format("Airbase %s ATIS:", self.airbasename)
-
+ local srstxt = string.format("Airbase %s ", self.airbasename)
if self.atis then
text=text..string.format("\nATIS %.3f MHz %s", self.atis.frequency, UTILS.GetModulationName(self.atis.modulation))
+ srstxt=srstxt..string.format("ATIS %.3f Megahertz %s", self.atis.frequency, UTILS.GetModulationName(self.atis.modulation))
if self.atis.towerfrequency then
local tower=""
for _,freq in pairs(self.atis.towerfrequency) do
@@ -2779,7 +2799,17 @@ function FLIGHTCONTROL:_PlayerInfoATIS(groupname)
end
-- Message to flight
- self:TextMessageToFlight(text, flight, 10, true)
+
+ --self:TextMessageToFlight(text, flight, 10, true)
+ -- Call sign.
+ local callsign=self:_GetCallsignName(flight)
+
+ -- Pilot calls inbound for landing.
+ local rtext=string.format("%s, %s, request ATIS frequency.", self.alias, callsign)
+
+ -- Radio message.
+ self:TransmissionPilot(rtext, flight)
+ self:TransmissionTower(srstxt,flight,10)
else
self:E(self.lid..string.format("Cannot find flight group %s.", tostring(groupname)))
@@ -3390,7 +3420,7 @@ function FLIGHTCONTROL:_PlayerRequestDirectLanding(groupname)
if nTakeoff>self.NlandingTakeoff then
-- Message text.
- local text=string.format("%s, negative! We have currently traffic taking off", callsign)
+ local text=string.format("%s, negative! We have currently traffic taking off!", callsign)
-- Send message.
self:TransmissionTower(text, flight, 10)
@@ -3854,7 +3884,7 @@ function FLIGHTCONTROL:_PlayerArrived(groupname)
else
-- Message text.
- local text=string.format("%s, %s, arrived at parking position", self.alias, callsign)
+ local text=string.format("%s, %s, arrived at parking position.", self.alias, callsign)
-- Transmit message.
self:TransmissionPilot(text, flight)
@@ -4277,7 +4307,7 @@ function FLIGHTCONTROL:TransmissionTower(Text, Flight, Delay)
local subgroups=nil
if Flight and not Flight.isAI then
local playerData=Flight:_GetPlayerData()
- if playerData.subtitles then
+ if playerData.subtitles and (not self.nosubs) then
subgroups=subgroups or {}
table.insert(subgroups, Flight.group)
end
@@ -4324,7 +4354,7 @@ function FLIGHTCONTROL:TransmissionPilot(Text, Flight, Delay)
local subgroups=nil
if Flight and not Flight.isAI then
local playerData=Flight:_GetPlayerData()
- if playerData.subtitles then
+ if playerData.subtitles and (not self.nosubs) then
subgroups=subgroups or {}
table.insert(subgroups, Flight.group)
end
diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua
index 5800625df..1bbd1c8bb 100644
--- a/Moose Development/Moose/Ops/FlightGroup.lua
+++ b/Moose Development/Moose/Ops/FlightGroup.lua
@@ -2605,6 +2605,9 @@ function FLIGHTGROUP:onbeforeUpdateRoute(From, Event, To, n, N)
elseif task.dcstask.id==AUFTRAG.SpecialTask.RECON then
-- For recon missions, we need to allow the update as we insert new waypoints.
self:T2(self.lid.."Allowing update route for Task: ReconMission")
+ elseif task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then
+ -- For recon missions, we need to allow the update as we insert new waypoints.
+ self:T2(self.lid.."Allowing update route for Task: Patrol Race Track")
elseif task.dcstask.id==AUFTRAG.SpecialTask.HOVER then
-- For recon missions, we need to allow the update as we insert new waypoints.
self:T2(self.lid.."Allowing update route for Task: Hover")
diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua
index 7405fc02a..0f6535e99 100644
--- a/Moose Development/Moose/Ops/Legion.lua
+++ b/Moose Development/Moose/Ops/Legion.lua
@@ -965,7 +965,7 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission, Assets)
local pause=false
-- Check if mission is INTERCEPT and asset is currently on GCI mission. If so, GCI is paused.
- if currM.type==AUFTRAG.Type.GCICAP and Mission.type==AUFTRAG.Type.INTERCEPT then
+ if (currM.type==AUFTRAG.Type.GCICAP or currM.type==AUFTRAG.Type.PATROLRACETRACK) and Mission.type==AUFTRAG.Type.INTERCEPT then
pause=true
elseif (currM.type==AUFTRAG.Type.ONGUARD or currM.type==AUFTRAG.Type.PATROLZONE) and (Mission.type==AUFTRAG.Type.ARTY or Mission.type==AUFTRAG.Type.GROUNDATTACK) then
pause=true
@@ -1445,7 +1445,7 @@ end
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned.
function LEGION:onafterLegionAssetReturned(From, Event, To, Cohort, Asset)
-- Debug message.
- self:I(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment)))
+ self:T(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment)))
-- Stop flightgroup.
if Asset.flightgroup and not Asset.flightgroup:IsStopped() then
@@ -2557,9 +2557,10 @@ function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properti
-- Distance to target.
local TargetDistance=TargetVec2 and UTILS.VecDist2D(TargetVec2, cohort.legion:GetVec2()) or 0
-
+
-- Is in range?
local Rmax=cohort:GetMissionRange(WeaponTypes)
+ local RangeMax = RangeMax or 0
local InRange=(RangeMax and math.max(RangeMax, Rmax) or Rmax) >= TargetDistance
return InRange
@@ -2610,6 +2611,7 @@ function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properti
-- Is capable of the mission type?
local can=AUFTRAG.CheckMissionCapability(MissionType, Cohort.missiontypes)
+
if can then
can=CheckCategory(Cohort)
else
@@ -2687,7 +2689,7 @@ function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properti
return nil
end
---- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
+--- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cannot be recruited by anyone else.
-- @param #table Cohorts Cohorts included.
-- @param #string MissionTypeRecruit Mission type for recruiting the cohort assets.
-- @param #string MissionTypeOpt Mission type for which the assets are optimized. Default is the same as `MissionTypeRecruit`.
@@ -3181,7 +3183,7 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
if currmission.type==AUFTRAG.Type.ALERT5 and currmission.alert5MissionType==MissionType then
-- Prefer assets that are on ALERT5 for this mission type.
score=score+25
- elseif currmission.type==AUFTRAG.Type.GCICAP and MissionType==AUFTRAG.Type.INTERCEPT then
+ elseif (currmission.type==AUFTRAG.Type.GCICAP or currmission.type==AUFTRAG.Type.PATROLRACETRACK) and MissionType==AUFTRAG.Type.INTERCEPT then
-- Prefer assets that are on GCICAP to perform INTERCEPTS. We set this even higher than alert5 because they are already in the air.
score=score+35
elseif (currmission.type==AUFTRAG.Type.ONGUARD or currmission.type==AUFTRAG.Type.PATROLZONE) and (MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK) then
diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua
index 2f423a8fa..39487771d 100644
--- a/Moose Development/Moose/Ops/OpsGroup.lua
+++ b/Moose Development/Moose/Ops/OpsGroup.lua
@@ -4467,6 +4467,26 @@ function OPSGROUP:_UpdateTask(Task, Mission)
if target then
self:EngageTarget(target, speed, Task.dcstask.params.formation)
end
+
+ elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then
+
+ ---
+ -- Task "Patrol Race Track" Mission.
+ ---
+
+ if self.isFlightgroup then
+ self:T("We are Special Auftrag Patrol Race Track, starting now ...")
+ --self:I({Task.dcstask.params})
+ --[[
+ Task.dcstask.params.TrackAltitude = self.TrackAltitude
+ Task.dcstask.params.TrackSpeed = self.TrackSpeed
+ Task.dcstask.params.TrackPoint1 = self.TrackPoint1
+ Task.dcstask.params.TrackPoint2 = self.TrackPoint2
+ Task.dcstask.params.TrackFormation = self.TrackFormation
+ --]]
+ local aircraft = self:GetGroup()
+ aircraft:PatrolRaceTrack(Task.dcstask.params.TrackPoint1,Task.dcstask.params.TrackPoint2,Task.dcstask.params.TrackAltitude,Task.dcstask.params.TrackSpeed,Task.dcstask.params.TrackFormation,false,1)
+ end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.HOVER then
diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua
index 15c2581ff..71a680cb4 100644
--- a/Moose Development/Moose/Ops/PlayerTask.lua
+++ b/Moose Development/Moose/Ops/PlayerTask.lua
@@ -21,7 +21,7 @@
-- ===
-- @module Ops.PlayerTask
-- @image OPS_PlayerTask.jpg
--- @date Last Update Sept 2023
+-- @date Last Update Oct 2023
do
@@ -98,7 +98,7 @@ PLAYERTASK = {
--- PLAYERTASK class version.
-- @field #string version
-PLAYERTASK.version="0.1.20"
+PLAYERTASK.version="0.1.21"
--- Generic task condition.
-- @type PLAYERTASK.Condition
@@ -470,10 +470,11 @@ end
--- [User] Remove a client from this task
-- @param #PLAYERTASK self
-- @param Wrapper.Client#CLIENT Client
+-- @param #string Name Name of the client
-- @return #PLAYERTASK self
-function PLAYERTASK:RemoveClient(Client)
+function PLAYERTASK:RemoveClient(Client,Name)
self:T(self.lid.."RemoveClient")
- local name = Client:GetPlayerName()
+ local name = Name or Client:GetPlayerName()
if self.Clients:HasUniqueID(name) then
self.Clients:PullByID(name)
if self.verbose then
@@ -1551,7 +1552,7 @@ PLAYERTASKCONTROLLER.Messages = {
--- PLAYERTASK class version.
-- @field #string version
-PLAYERTASKCONTROLLER.version="0.1.61"
+PLAYERTASKCONTROLLER.version="0.1.62"
--- Create and run a new TASKCONTROLLER instance.
-- @param #PLAYERTASKCONTROLLER self
@@ -2186,6 +2187,10 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
task:RemoveClient(Client)
--text = "Task aborted!"
text = self.gettext:GetEntry("TASKABORT",self.locale)
+ else
+ task:RemoveClient(nil,EventData.IniPlayerName)
+ --text = "Task aborted!"
+ text = self.gettext:GetEntry("TASKABORT",self.locale)
end
else
--text = "No active task!"
diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua
index 9c9a68d58..89e3541f8 100644
--- a/Moose Development/Moose/Wrapper/Controllable.lua
+++ b/Moose Development/Moose/Wrapper/Controllable.lua
@@ -5329,3 +5329,54 @@ function CONTROLLABLE:TaskAerobaticsBarrelRoll(TaskAerobatics,Repeats,InitAltitu
return TaskAerobatics
end
+
+--- [Air] Make an airplane or helicopter patrol between two points in a racetrack - resulting in a much tighter track around the start and end points.
+-- @param #CONTROLLABLE self
+-- @param Core.Point#COORDINATE Point1 Start point.
+-- @param Core.Point#COORDINATE Point2 End point.
+-- @param #number Altitude (Optional) Altitude in meters. Defaults to the altitude of the coordinate.
+-- @param #number Speed (Optional) Speed in kph. Defaults to 500 kph.
+-- @param #number Formation (Optional) Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation).
+-- @param #boolean AGL (Optional) If true, set altitude to above ground level (AGL), not above sea level (ASL).
+-- @param #number Delay (Optional) Set the task after delay seconds only.
+-- @return #CONTROLLABLE self
+function CONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation, AGL, Delay)
+
+ local PatrolGroup = self -- Wrapper.Group#GROUP
+
+ if not self:IsInstanceOf( "GROUP" ) then
+ PatrolGroup = self:GetGroup() -- Wrapper.Group#GROUP
+ end
+
+ local delay = Delay or 1
+
+ self:F( { PatrolGroup = PatrolGroup:GetName() } )
+
+ if PatrolGroup:IsAir() then
+ if Formation then
+ PatrolGroup:SetOption(AI.Option.Air.id.FORMATION,Formation) -- https://wiki.hoggitworld.com/view/DCS_option_formation
+ end
+
+ local FromCoord = PatrolGroup:GetCoordinate()
+ local ToCoord = Point1:GetCoordinate()
+
+ -- Calculate the new Route
+ if Altitude then
+ local asl = true
+ if AGL then asl = false end
+ FromCoord:SetAltitude(Altitude, asl)
+ ToCoord:SetAltitude(Altitude, asl)
+ end
+
+ -- Create a "air waypoint", which is a "point" structure that can be given as a parameter to a Task
+ local Route = {}
+ Route[#Route + 1] = FromCoord:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true, nil, DCSTasks, description, timeReFuAr )
+ Route[#Route + 1] = ToCoord:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true, nil, DCSTasks, description, timeReFuAr )
+
+ local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRaceTrack", Point2, Point1, Altitude, Speed, Formation, Delay )
+ PatrolGroup:SetTaskWaypoint( Route[#Route], TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
+ PatrolGroup:Route( Route, Delay ) -- Move after delay seconds to the Route. See the Route method for details.
+ end
+
+ return self
+end
diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua
index cd6769fc2..0fc411ebd 100644
--- a/Moose Development/Moose/Wrapper/Unit.lua
+++ b/Moose Development/Moose/Wrapper/Unit.lua
@@ -20,8 +20,8 @@
-- @module Wrapper.Unit
-- @image Wrapper_Unit.JPG
-
---- @type UNIT
+---
+-- @type UNIT
-- @field #string ClassName Name of the class.
-- @field #string UnitName Name of the unit.
-- @field #string GroupName Name of the group the unit belongs to.
@@ -561,7 +561,7 @@ end
--- Check if the unit is a tanker. Also retrieves the refuelling system (boom or probe) if applicable.
-- @param #UNIT self
--- @return #boolean If true, unit is refuelable (checks for the attribute "Refuelable").
+-- @return #boolean If true, unit is a tanker (checks for the attribute "Tankers").
-- @return #number Refueling system (if any): 0=boom, 1=probe.
function UNIT:IsTanker()
self:F2( self.UnitName )
@@ -582,7 +582,7 @@ function UNIT:IsTanker()
-- Some hard coded data as this is not in the descriptors...
if typename=="IL-78M" then
system=1 --probe
- elseif typename=="KC130" then
+ elseif typename=="KC130" or typename=="KC130J" then
system=1 --probe
elseif typename=="KC135BDA" then
system=1 --probe
@@ -590,6 +590,10 @@ function UNIT:IsTanker()
system=1 --probe
elseif typename=="S-3B Tanker" then
system=1 --probe
+ elseif typename=="KC_10_Extender" then
+ system=1 --probe
+ elseif typename=="KC_10_Extender_D" then
+ system=0 --boom
end
end
diff --git a/docs/_config.yml b/docs/_config.yml
index c557105bd..f40d6a2bb 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,3 +1,5 @@
+baseurl: ""
+url: "https://flightcontrol-master.github.io"
repository: FlightControl-Master/MOOSE
title: MOOSE
description: MOOSE is a Mission Object Oriented Scripting Environment for mission designers in DCS World
diff --git a/docs/buildsystem/build-docs.md b/docs/buildsystem/build-docs.md
new file mode 100644
index 000000000..3ad52c993
--- /dev/null
+++ b/docs/buildsystem/build-docs.md
@@ -0,0 +1,73 @@
+---
+parent: Build system
+nav_order: 2
+---
+
+# Build class documentation
+
+The documentation of all classes are included in the code as comments.
+This kind of documentation process is called [LuaDoc]. But we build the
+html files for the documentation with [LuaDocumentor]. It is a tool
+similar to [LuaDoc], but with some additional features the Moose team
+decided to use.
+
+{: .important }
+> The team created also some modifications, which are not part of the
+> official [LuaDocumentor] tool. So we use the code in the git repository
+> [Applevangelist/luadocumentor] in the branch `patch-1`.
+
+There are two git repositories which are used to save the generated
+documentation:
+
+- [MOOSE_DOCS] is the repository for the `master` branch of [MOOSE]
+ - A configured GitHub Pages job will deploy the result to:
+
+- [MOOSE_DOCS_DEVELOP] is the repository for the `develop` branch of [MOOSE]
+ - A configured GitHub Pages job will deploy the result to:
+
+
+Main build steps to create the class documentation are defined in [.github/workflows/build-docs.yml]:
+
+- Checkout of the git repository [MOOSE].
+- Create output folders.
+- Checkout of the git repository [Applevangelist/luadocumentor] with
+ branch `patch-1` into a subdirectory.
+- Update the Linux system software.
+- Install needed tools:
+ - [tree] - A tool to output a tree view of a folder structure.
+ - [lua] - Package to run [Lua] scripts. This time [Lua] 5.1,
+ because it matches the DCS environment.
+ - [LuaRocks] - This is the package manager for Lua modules.
+ - [markdown] - Dependency for [LuaDocumentor]
+ - [penlight] - Dependency for [LuaDocumentor]
+ - [metalua-compiler] - Dependency for [LuaDocumentor]
+ - [metalua-parser] - Dependency for [metalua-compiler]
+ - [checks] - Dependency for [metalua-parser]
+
+- Run the build steps:
+ - Run `luadocumentor.lua` to create the html files.
+
+- Deploy build results:
+ - Checkout [MOOSE_DOCS] or [MOOSE_DOCS_DEVELOP] git repository in a subdirectory.
+ - Use the matching git repository for the branch of [MOOSE].
+ - `master` -> [MOOSE_DOCS].
+ - `develop` -> [MOOSE_DOCS_DEVELOP].
+ - Use a `TOKEN` for checkout, so a `push` is possible later on.
+ - Copy build result to `MOOSE_DOCS` folder.
+ - Push results to the target repository.
+
+[tree]: https://wiki.ubuntuusers.de/tree/
+[LuaDoc]: https://keplerproject.github.io/luadoc/
+[LuaDocumentor]: https://luarocks.org/modules/luarocks/luadocumentor
+[Applevangelist/luadocumentor]: https://github.com/Applevangelist/luadocumentor/tree/patch-1
+[markdown]: https://luarocks.org/modules/mpeterv/markdown
+[penlight]: https://luarocks.org/modules/tieske/penlight
+[metalua-compiler]: https://luarocks.org/modules/luarocks/metalua-compiler
+[metalua-parser]: https://luarocks.org/modules/luarocks/metalua-parser
+[checks]: https://luarocks.org/modules/fab13n/checks
+[MOOSE]: https://github.com/FlightControl-Master/MOOSE
+[MOOSE_DOCS]: https://github.com/FlightControl-Master/MOOSE_DOCS
+[MOOSE_DOCS_DEVELOP]: https://github.com/FlightControl-Master/MOOSE_DOCS_DEVELOP
+[Lua]: https://www.lua.org/
+[LuaRocks]: https://luarocks.org/
+[.github/workflows/build-docs.yml]: https://github.com/FlightControl-Master/MOOSE/blob/master/.github/workflows/build-docs.yml
diff --git a/docs/buildsystem/build-includes.md b/docs/buildsystem/build-includes.md
new file mode 100644
index 000000000..bdd683a22
--- /dev/null
+++ b/docs/buildsystem/build-includes.md
@@ -0,0 +1,45 @@
+---
+parent: Build system
+nav_order: 1
+---
+
+# Build include files
+
+Main build steps to create the include files are defined in [.github/workflows/build-includes.yml]:
+
+- Checkout of the git repository [MOOSE].
+- Create output folders.
+- Update the Linux system software.
+- Install needed tools:
+ - [tree] - A tool to output a tree view of a folder structure.
+ - [lua5.3] - Package to run [Lua] scripts. Version 5.3 is needed, because we
+ need liblua5.3-dev for [LuaSrcDiet].
+ - [LuaRocks] - LuaRocks is the package manager for Lua modules.
+ - liblua5.3-dev - Header file of [Lua] needed for [LuaSrcDiet] to work.
+ - [LuaSrcDiet] - To compress the [Lua] code and create `Moose_.lua`.
+ - [LuaCheck] - This is a static code analyzer and a linter for [Lua].
+
+- Run the build steps:
+ - Run `./Moose Setup/Moose_Create.lua` to create `Moose.lua`.
+ - Run `./Moose Setup/Moose_Create.lua` to create dynamic `Moose.lua` to
+ load individual Lua class files used by Moose developers.
+ - Run [LuaSrcDiet] to compress the [Lua] code and create `Moose_.lua`
+ - Run [LuaCheck] to find errors in the code. Warnings are ignored, because
+ there are a lot of warnings, which cannot be resolved by the Moose team.
+
+- Deploy build results:
+ - Checkout [MOOSE_INCLUDE] git repository in a subdirectory.
+ - Use the same branch used to checkout [MOOSE] git repository.
+ - Use a `TOKEN` for checkout, so a `push` is possible later on.
+ - Copy build result to `MOOSE_INCLUDE` folder
+ - Push results to [MOOSE_INCLUDE] repository
+
+[.github/workflows/build-includes.yml]: https://github.com/FlightControl-Master/MOOSE/blob/master/.github/workflows/build-includes.yml
+[tree]: https://wiki.ubuntuusers.de/tree/
+[lua5.3]: https://www.lua.org/manual/5.3/
+[LuaRocks]: https://luarocks.org/
+[LuaCheck]: https://github.com/mpeterv/luacheck
+[MOOSE]: https://github.com/FlightControl-Master/MOOSE
+[MOOSE_INCLUDE]: https://github.com/FlightControl-Master/MOOSE_INCLUDE
+[LuaSrcDiet]: https://github.com/jirutka/luasrcdiet
+[Lua]: https://www.lua.org/
diff --git a/docs/buildsystem/gh-pages.md b/docs/buildsystem/gh-pages.md
new file mode 100644
index 000000000..94ed78fa9
--- /dev/null
+++ b/docs/buildsystem/gh-pages.md
@@ -0,0 +1,51 @@
+---
+parent: Build system
+nav_order: 3
+---
+
+# Build GitHub Pages
+
+This documentation is created by [GitHub Pages]. The source files are
+stored in the repository [MOOSE] in the subfolder `docs`.
+We use [Just the Docs], which is a modern, highly customizable, and responsive
+[Jekyll] theme for documentation.
+
+{: .note }
+> The class documentation is created by its own [build] and is not the scope for this page!
+
+The build steps to create this documentation are defined in [.github/workflows/gh-pages.yml].
+
+It is divided into two jobs:
+- build:
+ - Only changes to in the subfolder `docs` or `gh-pages.yml` will trigger a build.
+ - Checkout of the git repository [MOOSE].
+ - Setup [Ruby] version 3.1, which is needed by [Jekyll].
+ - Run action [configure-pages].
+ - Build with [Jekyll].
+ - Run action [upload-pages-artifact].
+- deploy:
+ - Run action [deploy-pages].
+
+# Preview of this documentation
+
+When enhancing this documentation it is very useful to see a 1on1 preview of the pages.
+This can be displayed as follows:
+
+- You need a working installation of [Docker].
+- Go to the `docs` subfolder.
+- Run `docker compose up`.
+- Open a browser with the following URL: `http://127.0.0.1:4000/`.
+- After a change of the [Markdown] files, wait some seconds and press F5 in the browser.
+
+[GitHub Pages]: https://pages.github.com/
+[MOOSE]: https://github.com/FlightControl-Master/MOOSE
+[Just the Docs]: https://github.com/just-the-docs/just-the-docs
+[Jekyll]: https://jekyllrb.com/
+[Ruby]: https://www.ruby-lang.org/en/
+[build]: build-docs.md
+[.github/workflows/gh-pages.yml]: https://github.com/FlightControl-Master/MOOSE/blob/master/.github/workflows/gh-pages.yml
+[configure-pages]: https://github.com/actions/configure-pages/
+[upload-pages-artifact]: https://github.com/actions/upload-pages-artifact
+[deploy-pages]: https://github.com/actions/deploy-pages/
+[Docker]: https://www.docker.com/
+[Markdown]: https://www.markdownguide.org/
diff --git a/docs/buildsystem/index.md b/docs/buildsystem/index.md
new file mode 100644
index 000000000..1dec95fed
--- /dev/null
+++ b/docs/buildsystem/index.md
@@ -0,0 +1,58 @@
+---
+has_children: true
+nav_order: 3
+---
+
+# Build system
+
+{: .note }
+> This documentation is not needed for end users. Only the people of the
+> development team, who must maintain the build system need to read this.
+
+In this document we want to describe our build system for MOOSE.
+MOOSE consists of multiple [Lua] files. Each class is stored in its own file.
+This is needed for MOOSE developers to maintain clarity.
+For users this is not practical, because they want to include the whole framework
+as a single file into their missions.
+
+Because of this the build will collect all needed files and merge them together
+in one file with the name `Moose.lua`. It includes also all comments and the
+class documentation. Because of this its size is about 6-7 MB.
+
+To reduce the size of the file and make mission files smaller, the Moose team
+decided to create a version without all comments and documentation. This file
+is named `Moose_.lua`. It is created by a tool with the name [LuaSrcDiet].
+
+Both files will be called static includes. In other programming languages includes
+are dependencies. For Moose it is easier to memorize, that these files must be
+included in your mission to use Moose. It is an static approach because you need
+to add it once and it is only read from inside of the mission file after that.
+A dynamic approach is to load all the single class files on each mission start
+from the hard disk. But this is more for advanced Moose users and Moose developers.
+
+## Details
+
+In the past [AppVeyor] was used to run the build on a Windows system.
+We decided to migrate this build to [GitHub Actions]. Installation of
+dependencies was not stable on Windows with [GitHub Actions]. So we switched
+to Ubuntu Linux.
+
+### GitHub Actions yml files
+
+The build configuration is stored in the folder `.github/workflows`. You will find
+multiple files in this directory:
+
+- [build-docs.yml] - Job definition to generate the class documentation
+- [build-includes.yml] - Job definition to build the static includes
+- [gh-pages.yml] - Job to build this documentation page
+
+We decided to use different files for each job for separation of duties and easier
+maintenance.
+
+[Lua]: https://www.lua.org/
+[LuaSrcDiet]: https://github.com/jirutka/luasrcdiet
+[AppVeyor]: https://www.appveyor.com/
+[GitHub Actions]: https://docs.github.com/en/actions
+[build-docs.yml]: build-docs.md
+[build-includes.yml]: build-includes.md
+[gh-pages.yml]: gh-pages.md
diff --git a/docs/buildsystem/local-test.md b/docs/buildsystem/local-test.md
new file mode 100644
index 000000000..98d39fc5a
--- /dev/null
+++ b/docs/buildsystem/local-test.md
@@ -0,0 +1,49 @@
+---
+parent: Build system
+nav_order: 4
+---
+
+# Run builds locally
+
+When creating or enhancing [GitHub Actions] builds it is a problem to test the
+build. After each change you need to commit and check the build result. This
+leads to a lot of unnecessary commits.
+
+Therefor it is needed to run the build locally on the developer PC. The tool
+which enabled this is [act]. It uses [Docker] to create a build runner and
+executes the [GitHub Actions] build with it.
+
+[act] can by installed by [Chocolatey] by this single command: `choco install act-cli`.
+
+We use the `Medium Docker Image` for our MOOSE builds to work properly.
+Unfortunately the docker images used by [act] are not as up to date as the
+images used by [GitHub Actions]. So we needed to add a build step with
+`sudo apt-get -qq update`.
+
+The build jobs needs `TOKENS` to run properly. So you have to create a PAT
+([Personal Access Token]). A classic Token with read rights is enough to run
+the build, as long as don't want to push the results.
+
+{: .important }
+> The push step is only executed if the variable `FORCE_PUSH` with value `true` is set.
+> - This is only needed if the push step itself must be change and tested!
+> - Add parameter `--var FORCE_PUSH=true` to your [act] commando.
+> - You and your PAT needs write access to the target repos, too.
+
+Save your PAT in the file `.secrets` in the main folder
+of the MOOSE repository. This file is added to `.gitignore`, so it is not
+recognized by git for commits. Add the following line to `.secrets`:
+
+```
+BOT_TOKEN=
+```
+
+To run the builds use these commands:
+- `act push -W .github/workflows/build-includes.yml`
+- `act push -W .github/workflows/build-docs.yml`
+
+[GitHub Actions]: https://docs.github.com/en/actions
+[act]: https://github.com/nektos/act
+[Docker]: https://www.docker.com/
+[Chocolatey]: https://community.chocolatey.org/
+[Personal Access Token]: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens