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