diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 1d2fe6f30..de5b5b927 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -6311,7 +6311,7 @@ do -- SET_OPSGROUP --- Creates a new SET_OPSGROUP object, building a set of groups belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_OPSGROUP self - -- @return #SET_OPSGROUP + -- @return #SET_OPSGROUP self function SET_OPSGROUP:New() -- Inherit SET_BASE. diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 6b4a3bc14..a7b61dc8f 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -2667,6 +2667,26 @@ function RANGE:_DisplayRangeInfo( _unitname ) text = text .. string.format( "Max strafing alt AGL: %s\n", tstrafemaxalt ) text = text .. string.format( "# of strafe targets: %d\n", self.nstrafetargets ) text = text .. string.format( "# of bomb targets: %d\n", self.nbombtargets ) + if self.instructor then + local alive = "N/A" + if self.instructorrelayname then + local relay = UNIT:FindByName( self.instructorrelayname ) + if relay then + alive = tostring( relay:IsAlive() ) + end + end + text = text .. string.format( "Instructor %.3f MHz (Relay=%s)\n", self.instructorfreq, alive ) + end + if self.rangecontrol then + local alive = "N/A" + if self.rangecontrolrelayname then + local relay = UNIT:FindByName( self.rangecontrolrelayname ) + if relay then + alive = tostring( relay:IsAlive() ) + end + end + text = text .. string.format( "Control %.3f MHz (Relay=%s)\n", self.rangecontrolfreq, alive ) + end text = text .. texthit text = text .. textbomb text = text .. textdelay diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 113359cf0..848fe265b 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1137,15 +1137,19 @@ end -- @param #number Port SRS port. Default 5002. -- @return #ATIS self function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port) - self.useSRS=true - self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation) - self.msrs:SetGender(Gender) - self.msrs:SetCulture(Culture) - self.msrs:SetVoice(Voice) - self.msrs:SetPort(Port) - self.msrs:SetCoalition(self:GetCoalition()) - if self.dTQueueCheck<=10 then - self:SetQueueUpdateTime(90) + if PathToSRS then + self.useSRS=true + self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation) + self.msrs:SetGender(Gender) + self.msrs:SetCulture(Culture) + self.msrs:SetVoice(Voice) + self.msrs:SetPort(Port) + self.msrs:SetCoalition(self:GetCoalition()) + if self.dTQueueCheck<=10 then + self:SetQueueUpdateTime(90) + end + else + self:E(self.lid..string.format("ERROR: No SRS path specified!")) end return self end diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index c40948931..e8a659885 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -137,7 +137,7 @@ function ARMYGROUP:New(group) self:AddTransition("*", "Rearm", "Rearm") -- Group is send to a coordinate and waits until ammo is refilled. self:AddTransition("Rearm", "Rearming", "Rearming") -- Group has arrived at the rearming coodinate and is waiting to be fully rearmed. - self:AddTransition("Rearming", "Rearmed", "Cruising") -- Group was rearmed. + self:AddTransition("*", "Rearmed", "Cruising") -- Group was rearmed. ------------------------ --- Pseudo Functions --- @@ -1381,9 +1381,22 @@ end -- @param #string To To state. function ARMYGROUP:onafterRearmed(From, Event, To) self:T(self.lid.."Group rearmed") + + -- Get Current mission. + local mission=self:GetMissionCurrent() + + -- Check if this is a rearming mission. + if mission and mission.type==AUFTRAG.Type.REARMING then + -- Rearmed ==> Mission Done! This also checks if the group is done. + self:MissionDone(mission) + + else + + -- Check group done. + self:_CheckGroupDone(1) + + end - -- Check group done. - self:_CheckGroupDone(1) end --- On before "RTZ" event. diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index e9ae00128..6a70170ba 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -418,6 +418,7 @@ _AUFTRAGSNR=0 -- @field #string AIRDEFENSE Air defense. -- @field #string EWR Early Warning Radar. -- @field #string RECOVERYTANKER Recovery tanker. +-- @filed #string REARMING Rearming mission. -- @field #string NOTHING Nothing. AUFTRAG.Type={ ANTISHIP="Anti Ship", @@ -459,6 +460,7 @@ AUFTRAG.Type={ AIRDEFENSE="Air Defence", EWR="Early Warning Radar", RECOVERYTANKER="Recovery Tanker", + REARMING="Rearming", NOTHING="Nothing", } @@ -480,6 +482,7 @@ AUFTRAG.Type={ -- @field #string AIRDEFENSE Air defense. -- @field #string EWR Early Warning Radar. -- @field #string RECOVERYTANKER Recovery tanker. +-- @field #string REARMING Rearming. -- @field #string NOTHING Nothing. AUFTRAG.SpecialTask={ FORMATION="Formation", @@ -499,6 +502,7 @@ AUFTRAG.SpecialTask={ AIRDEFENSE="Air Defense", EWR="Early Warning Radar", RECOVERYTANKER="Recovery Tanker", + REARMING="Rearming", NOTHING="Nothing", } @@ -2009,6 +2013,30 @@ function AUFTRAG:NewFUELSUPPLY(Zone) return mission end +--- **[GROUND]** Create a REARMING mission. +-- @param #AUFTRAG self +-- @param Core.Zone#ZONE Zone The zone, where units go and look for ammo supply. +-- @return #AUFTRAG self +function AUFTRAG:NewREARMING(Zone) + + local mission=AUFTRAG:New(AUFTRAG.Type.REARMING) + + mission:_TargetFromObject(Zone) + + mission.optionROE=ENUMS.ROE.WeaponHold + mission.optionAlarm=ENUMS.AlarmState.Auto + + mission.missionFraction=1.0 + + mission.missionWaypointRadius=0 + + mission.categories={AUFTRAG.Category.GROUND} + + mission.DCStask=mission:GetDCSMissionTask() + + return mission +end + --- **[AIR]** Create an ALERT 5 mission. Aircraft will be spawned uncontrolled and wait for an assignment. You must specify **one** mission type which is performed. -- This determines the payload and the DCS mission task which are used when the aircraft is spawned. @@ -5710,6 +5738,24 @@ function AUFTRAG:GetDCSMissionTask() table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.AMMOSUPPLY then + + ---------------------- + -- REARMING Mission -- + ---------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.REARMING + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + param.zone=self:GetObjective() + + DCStask.params=param + + table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.ALERT5 then --------------------- diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 34a188fe4..9471147ec 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -2117,7 +2117,7 @@ function CHIEF:CheckTargetQueue() self:T2(self.lid..string.format("Recruiting assets for mission type %s [performance=%d] of target %s", mp.MissionType, mp.Performance, target:GetName())) -- Recruit assets. - local recruited, assets, legions=self:RecruitAssetsForTarget(target, mp.MissionType, NassetsMin, NassetsMax) + local recruited, assets, legions=self.commander:RecruitAssetsForTarget(target, mp.MissionType, NassetsMin, NassetsMax) if recruited then @@ -2649,29 +2649,6 @@ function CHIEF:_GetMissionTypeForGroupAttribute(Attribute) return missionperf end ---- Recruit assets for a given TARGET. --- @param #CHIEF self --- @param Ops.Target#TARGET Target The target. --- @param #string MissionType Mission Type. --- @param #number NassetsMin Min number of required assets. --- @param #number NassetsMax Max number of required assets. --- @return #boolean If `true` enough assets could be recruited. --- @return #table Assets that have been recruited from all legions. --- @return #table Legions that have recruited assets. -function CHIEF:RecruitAssetsForTarget(Target, MissionType, NassetsMin, NassetsMax) - - -- Cohorts. - local Cohorts=self.commander:_GetCohorts() - - -- Target position. - local TargetVec2=Target:GetVec2() - - -- Recruite assets. - local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2) - - - return recruited, assets, legions -end --- Recruit assets for a given OPS zone. -- @param #CHIEF self diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index be05e288a..8a130a982 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -787,7 +787,7 @@ end function COHORT:onafterStart(From, Event, To) -- Short info. - local text=string.format("Starting %s v%s %s", self.ClassName, self.version, self.name) + local text=string.format("Starting %s v%s %s [%s]", self.ClassName, self.version, self.name, self.attribute) self:I(self.lid..text) -- Start the status monitoring. @@ -993,7 +993,7 @@ end -- @param #COHORT self -- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types. -- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`. --- @return Core.Set#SET_OPSGROUPS Ops groups set. +-- @return Core.Set#SET_OPSGROUP Ops groups set. function COHORT:GetOpsGroups(MissionTypes, Attributes) local set=SET_OPSGROUP:New() diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index 3b0e10a60..022fc2678 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -1254,6 +1254,23 @@ function COMMANDER:CheckOpsQueue() if operation:IsRunning() then + -- Loop over missions. + for _,_mission in pairs(operation.missions or {}) do + local mission=_mission --Ops.Auftrag#AUFTRAG + + if mission.phase==nil or (mission.phase and mission.phase==operation.phase) and mission:IsPlanned() then + self:AddMission(mission) + end + end + + -- Loop over targets. + for _,_target in pairs(operation.targets or {}) do + local target=_target --Ops.Target#TARGET + + if (target.phase==nil or (target.phase and target.phase==operation.phase)) and (not self:IsTarget(target)) then + self:AddTarget(target) + end + end end @@ -1352,6 +1369,9 @@ function COMMANDER:CheckTargetQueue() mission:SetRequiredAttribute(resource.Attributes) mission:SetRequiredProperty(resource.Properties) + -- Set operation (if any). + mission.operation=target.operation + -- Set resource mission. resource.mission=mission @@ -1651,6 +1671,30 @@ function COMMANDER:RecruitAssetsForEscort(Mission, Assets) return true end +--- Recruit assets for a given TARGET. +-- @param #COMMANDER self +-- @param Ops.Target#TARGET Target The target. +-- @param #string MissionType Mission Type. +-- @param #number NassetsMin Min number of required assets. +-- @param #number NassetsMax Max number of required assets. +-- @return #boolean If `true` enough assets could be recruited. +-- @return #table Assets that have been recruited from all legions. +-- @return #table Legions that have recruited assets. +function COMMANDER:RecruitAssetsForTarget(Target, MissionType, NassetsMin, NassetsMax) + + -- Cohorts. + local Cohorts=self:_GetCohorts() + + -- Target position. + local TargetVec2=Target:GetVec2() + + -- Recruite assets. + local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2) + + + return recruited, assets, legions +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Transport Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 1ae08735e..f76b000cc 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -12,7 +12,15 @@ -- -- === -- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20FlightControl). +-- +-- === +-- -- ### Author: **funkyfranky** +-- +-- === -- @module OPS.FlightControl -- @image OPS_FlightControl.png @@ -62,7 +70,10 @@ -- # The FLIGHTCONTROL Concept -- -- This class implements an ATC for human and AI controlled aircraft. It gives permission for take-off and landing based on a sophisticated queueing system. --- Therefore, it solves (or reduces) a lot of common problems with the DCS implementation (which is barly existing at this point). +-- Therefore, it solves (or reduces) a lot of common problems with the DCS implementation. +-- +-- You might be familiar with the `AIRBOSS` class. This class is the analogue for land based airfields. One major difference is that no pre-recorded sound files are +-- necessary. The radio transmissions use the SRS text-to-speech feature. -- -- ## Prerequisites -- @@ -77,7 +88,54 @@ -- * Only one player/client per group as we can create menus only for a group and not for a specific unit. -- * Only FLIGHTGROUPS are controlled. This means some older classes, *e.g.* RAT are not supported (yet). -- * So far only airdromes are handled, *i.e.* no FARPs or ships. --- * Only fixed wing aircraft are handled until now, *i.e.* no helos. +-- * Helicopters are not treated differently from fixed wing aircraft until now. +-- * The active runway can only be determined by the wind direction. So at least set a very light wind speed in your mission. +-- +-- # Basic Usage +-- +-- A flight control for a given airdrome can be created with the @{#FLIGHTCONTROL.New}(*AirbaseName, Frequency, Modulation, PathToSRS*) function. You need to specify the name of the airbase, the +-- tower radio frequency, its modulation and the path, where SRS is located on the machine that is running this mission. +-- +-- For the FC to be operating, it needs to be started with the @{#FLIGHTCONTROL.Start}() function. +-- +-- ## Simple Script +-- +-- The simplest script looks like +-- +-- local FC_BATUMI=FLIGHTCONTROL:New(AIRBASE.Caucasus.Batumi, 251, nil, "D:\\SomeDirectory\\_SRS") +-- FC_BATUMI:Start() +-- +-- This will start the FC for at the Batumi airbase with tower frequency 251 MHz AM. SRS needs to be in the given directory. +-- +-- Like this, a default holding pattern (see below) is parallel to the direction of the active runway. +-- +-- # Holding Patterns +-- +-- Holding pattern are air spaces where incoming aircraft are guided to and have to hold until they get landing clearance. +-- +-- You can add a holding pattern with the @{#FLIGHTCONTROL.AddHoldingPattern}(*ArrivalZone, Heading, Length, FlightlevelMin, FlightlevelMax, Prio*) function, where +-- +-- * `ArrivalZone` is the zone where the aircraft enter the pattern. +-- * `Heading` is the direction into which the aircraft have to fly from the arrival zone. +-- * `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. +-- +-- # Parking Guard +-- +-- # Taxi Limits +-- +-- 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) +-- and bring the whole airbase effectively to a stand still. +-- +-- ## Landing Limits +-- +-- +-- ## Takeoff Limits +-- +-- Note that the limits here are only affecting AI aircraft groups. Human players are assumed to be a lot more well behaved and capable as they are able to taxi around obstacles, *e.g.* +-- other aircraft etc. -- -- -- @field #FLIGHTCONTROL @@ -102,7 +160,7 @@ FLIGHTCONTROL = { Nlanding = nil, dTlanding = nil, Nparkingspots = nil, - holdingpatterns = {}, + holdingpatterns = {}, hpcounter = 0, } @@ -170,20 +228,20 @@ FLIGHTCONTROL.FlightStatus={ --- FlightControl class version. -- @field #string version -FLIGHTCONTROL.version="0.5.2" +FLIGHTCONTROL.version="0.5.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list -- TODO: Runway destroyed. --- TODO: Support airwings. Dont give clearance for Alert5 or if mission has not started. -- TODO: Switch to enable/disable AI messages. -- TODO: Improve ATC TTS messages. -- TODO: Talk me down option. -- TODO: ATIS option. -- TODO: Check runways and clean up. --- TODO: Accept and forbit parking spots. -- TODO: Add FARPS? +-- DONE: Accept and forbit parking spots. DONE via AIRBASE black/white lists and airwing features. +-- DONE: Support airwings. Dont give clearance for Alert5 or if mission has not started. -- DONE: Define holding zone. -- DONE: Basic ATC voice overs. -- DONE: Add SRS TTS. @@ -234,6 +292,9 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS) -- 5 NM zone around the airbase. self.zoneAirbase=ZONE_RADIUS:New("FC", self:GetCoordinate():GetVec2(), UTILS.NMToMeters(5)) + + -- Add backup holding pattern. + self:_AddHoldingPatternBackup() -- Set alias. self.alias=self.airbasename.." Tower" @@ -331,7 +392,17 @@ function FLIGHTCONTROL:SetFrequency(Frequency, Modulation) self.frequency=Frequency or 305 self.modulation=Modulation or radio.modulation.AM + + if self.msrsPilot then + self.msrsPilot:SetFrequencies(Frequency) + self.msrsPilot:SetModulations(Modulation) + end + if self.msrsTower then + self.msrsTower:SetFrequencies(Frequency) + self.msrsTower:SetModulations(Modulation) + end + return self end @@ -398,8 +469,9 @@ end -- @param #number Length Length in nautical miles. Default 15 NM. -- @param #number FlightlevelMin Min flight level. Default 5. -- @param #number FlightlevelMax Max flight level. Default 15. +-- @param #number Prio Priority. Lower is higher. Default 50. -- @return #FLIGHTCONTROL.HoldingPattern Holding pattern table. -function FLIGHTCONTROL:AddHoldingPattern(ArrivalZone, Heading, Length, FlightlevelMin, FlightlevelMax) +function FLIGHTCONTROL:AddHoldingPattern(ArrivalZone, Heading, Length, FlightlevelMin, FlightlevelMax, Prio) -- Get ZONE if passed as string. if type(ArrivalZone)=="string" then @@ -410,13 +482,14 @@ function FLIGHTCONTROL:AddHoldingPattern(ArrivalZone, Heading, Length, Flightlev self.hpcounter=self.hpcounter+1 local hp={} --#FLIGHTCONTROL.HoldingPattern - hp.arrivalzone=ArrivalZone hp.uid=self.hpcounter + hp.arrivalzone=ArrivalZone hp.name=string.format("%s-%d", ArrivalZone:GetName(), hp.uid) hp.pos0=ArrivalZone:GetCoordinate() hp.pos1=hp.pos0:Translate(UTILS.NMToMeters(Length or 15), Heading) hp.angelsmin=FlightlevelMin or 5 hp.angelsmax=FlightlevelMax or 15 + hp.prio=Prio or 50 hp.stacks={} for i=hp.angelsmin, hp.angelsmax do @@ -438,6 +511,50 @@ function FLIGHTCONTROL:AddHoldingPattern(ArrivalZone, Heading, Length, Flightlev hp.pos0:ArrowToAll(hp.pos1, nil, {1,0,0}, 1, {1,1,0}, 0.5, 2, true) ArrivalZone:DrawZone() + local function _sort(a,b) + return a.prio %s to %s", Event, From, To, airbase and airbase:GetName() or "None")) + if self:IsAlive() then local allowed=true @@ -4296,7 +4299,7 @@ function FLIGHTGROUP:_PlayerMyStatus(groupname) local text=string.format("My Status:") text=text..string.format("\nCallsign: %s", tostring(flight:GetCallsignName())) text=text..string.format("\nFlight status: %s", tostring(flight:GetState())) - text=text..string.format("\nFlight control: %s status=%s", tostring(fc and fc.airbasename or "N/A"), tostring(fc and fc:GetFlightStatus(flight) or "N/A")) + text=text..string.format("\nFlight control: %s [%s]", tostring(fc and fc.airbasename or "N/A"), tostring(fc and fc:GetFlightStatus(flight) or "N/A")) -- Send message. --self:TextMessageToFlight(text, flight, 10, true) diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 150384c15..ad7534e26 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -1869,8 +1869,9 @@ function LEGION:GetOpsGroups(MissionTypes, Attributes) for _,_cohort in pairs(self.cohorts) do local cohort=_cohort --Ops.Cohort#COHORT - local setcohort=cohort:GetOpsGroups(MissionTypes, Attributes) - setLegion:AddSet(setcohort) + local setCohort=cohort:GetOpsGroups(MissionTypes, Attributes) + self:I(self.lid..string.format("Found %d opsgroups of cohort %s", setCohort:Count(), cohort.name)) + setLegion:AddSet(setCohort) end return setLegion diff --git a/Moose Development/Moose/Ops/Operation.lua b/Moose Development/Moose/Ops/Operation.lua index 71bed062a..c038b53e9 100644 --- a/Moose Development/Moose/Ops/Operation.lua +++ b/Moose Development/Moose/Ops/Operation.lua @@ -33,6 +33,7 @@ -- @field #number counterPhase Running number counting the phases. -- @field #OPERATION.Phase phase Currently active phase (if any). -- @field #table targets Targets. +-- @field #table missions Missions. -- -- @extends Core.Fsm#FSM @@ -54,6 +55,7 @@ OPERATION = { phases = {}, counterPhase = 0, targets = {}, + missions = {}, } --- Global mission counter. @@ -136,16 +138,15 @@ function OPERATION:New(Name) --- Pseudo Functions --- ------------------------ - --- Triggers the FSM event "StatusUpdate". - -- @function [parent=#OPERATION] StatusUpdate + --- Triggers the FSM event "Start". + -- @function [parent=#OPERATION] Start -- @param #OPERATION self - --- Triggers the FSM event "Status" after a delay. - -- @function [parent=#OPERATION] __StatusUpdate + --- Triggers the FSM event "Start" after a delay. + -- @function [parent=#OPERATION] __Start -- @param #OPERATION self -- @param #number delay Delay in seconds. - --- Triggers the FSM event "Stop". -- @function [parent=#OPERATION] Stop -- @param #OPERATION self @@ -155,6 +156,15 @@ function OPERATION:New(Name) -- @param #OPERATION self -- @param #number delay Delay in seconds. + --- Triggers the FSM event "StatusUpdate". + -- @function [parent=#OPERATION] StatusUpdate + -- @param #OPERATION self + + --- Triggers the FSM event "Status" after a delay. + -- @function [parent=#OPERATION] __StatusUpdate + -- @param #OPERATION self + -- @param #number delay Delay in seconds. + --- Triggers the FSM event "PhaseChange". -- @function [parent=#OPERATION] PhaseChange @@ -243,6 +253,36 @@ function OPERATION:AddPhase(Name) return phase end +--- Add mission to operation. +-- @param #OPERATION self +-- @param Ops.Auftrag#AUFTRAG Mission The mission to add. +-- @param #OPERATION.Phase Phase (Optional) The phase in which the mission should be executed. If no phase is given, it will be exectuted ASAP. +function OPERATION:AddMission(Mission, Phase) + + Mission.phase=Phase + Mission.operation=self + + table.insert(self.missions, Mission) + + return self +end + +--- Add Target to operation. +-- @param #OPERATION self +-- @param Ops.Target#TARGET Target The target to add. +-- @param #OPERATION.Phase Phase (Optional) The phase in which the target should be attacked. If no phase is given, it will be attacked ASAP. +function OPERATION:AddTarget(Target, Phase) + + Target.phase=Phase + Target.operation=self + + table.insert(self.targets, Target) + + return self +end + + + --- Get a phase by its name. -- @param #OPERATION self -- @param #string Name Name of the phase. Default "Phase-01" where the last number is a running number. @@ -310,6 +350,14 @@ function OPERATION:IsAssignedCohort(Cohort) self:T(self.lid..string.format("Cohort %s is assigned to this operation", Cohort.name)) return true else + + -- Check if legion of this cohort was assigned. + local Legion=Cohort.legion + if Legion and self:IsAssignedLegion(Legion) then + self:T(self.lid..string.format("Legion %s of Cohort %s is assigned to this operation", Legion.alias, Cohort.name)) + return true + end + self:T(self.lid..string.format("Cohort %s is NOT assigned to this operation", Cohort.name)) return false end @@ -460,6 +508,22 @@ function OPERATION:GetPhaseActive() return self.phase end +--- Get name of a phase. +-- @param #OPERATION self +-- @param #OPERATION.Phase Phase The phase of which the name is returned. +-- @return #string The name of the phase. +function OPERATION:GetPhaseName(Phase) + + Phase=Phase or self.phase + + if Phase then + return Phase.name + else + return "None" + end + +end + --- Check if a phase is the currently active one. -- @param #OPERATION self -- @param #OPERATION.Phase Phase The phase to check. @@ -509,6 +573,24 @@ function OPERATION:CountPhases(Status) return N end +--- Count targets alive. +-- @param #OPERATION self +-- @param #OPERATION.Phase Phase (Optional) Only count targets set for this phase. +-- @return #number Number of phases +function OPERATION:CountTargets(Phase) + + local N=0 + for _,_target in pairs(self.targets) do + local target=_target --Ops.Target#TARGET + + if target:IsAlive() and (Phase==nil or target.phase==Phase) then + N=N+1 + end + end + + return N +end + --- Check if operation is in FSM state "Planned". -- @param #OPERATION self -- @return #boolean If `true`, operation is "Planned". diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 30ee7f641..4a74e6dda 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -4162,6 +4162,17 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) --- -- Just stay put and wait until something happens. + + elseif Task.dcstask.id==AUFTRAG.SpecialTask.REARMING then + + --- + -- Task "Rearming" + --- + + -- Check if ammo is full. + + local rearmed=self:_CheckAmmoFull() + elseif Task.dcstask.id==AUFTRAG.SpecialTask.ALERT5 then @@ -4483,8 +4494,10 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task) done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.AMMOSUPPLY then done=true - elseif Task.dcstask.id==AUFTRAG.SpecialTask.FUELSUPPLY then + elseif Task.dcstask.id==AUFTRAG.SpecialTask.FUELSUPPLY then done=true + elseif Task.dcstask.id==AUFTRAG.SpecialTask.REARMING then + done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.ALERT5 then done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD or Task.dcstask.id==AUFTRAG.SpecialTask.ARMOREDGUARD then @@ -4944,7 +4957,12 @@ function OPSGROUP:onbeforeMissionStart(From, Event, To, Mission) -- Startup group if it is uncontrolled. Alert 5 aircraft will not be started though! if self:IsFlightgroup() and self:IsUncontrolled() and Mission.type~=AUFTRAG.Type.ALERT5 then - self:StartUncontrolled(delay) + local fc=FLIGHTGROUP.GetFlightControl(self) + if fc and fc:IsControlling(self) then + FLIGHTGROUP.SetReadyForTakeoff(self, true) + else + self:StartUncontrolled(delay) + end end return true @@ -4972,9 +4990,9 @@ function OPSGROUP:onafterMissionStart(From, Event, To, Mission) Mission:__Started(3) -- Set ready for takeoff in case of FLIGHTCONTROL. - if self.isFlightgroup and Mission.type~=AUFTRAG.Type.ALERT5 then - FLIGHTGROUP.SetReadyForTakeoff(self, true) - end + --if self.isFlightgroup and Mission.type~=AUFTRAG.Type.ALERT5 then + -- FLIGHTGROUP.SetReadyForTakeoff(self, true) + --end -- Route group to mission zone. if self.speedMax>3.6 or true then @@ -5399,8 +5417,9 @@ function OPSGROUP:RouteToMission(mission, delay) elseif mission.type==AUFTRAG.Type.PATROLZONE or mission.type==AUFTRAG.Type.BARRAGE or - mission.type==AUFTRAG.Type.AMMOSUPPLY or - mission.type==AUFTRAG.Type.FUELSUPPLY or + mission.type==AUFTRAG.Type.AMMOSUPPLY or + mission.type==AUFTRAG.Type.FUELSUPPLY or + mission.type==AUFTRAG.Type.REARMING or mission.type==AUFTRAG.Type.AIRDEFENSE or mission.type==AUFTRAG.Type.EWR then --- @@ -5540,8 +5559,6 @@ function OPSGROUP:RouteToMission(mission, delay) if inRange then - env.info("FF in range!") - waypointcoord=self:GetCoordinate(true) else diff --git a/Moose Development/Moose/Ops/Platoon.lua b/Moose Development/Moose/Ops/Platoon.lua index c262ff4a5..6d6a3e87a 100644 --- a/Moose Development/Moose/Ops/Platoon.lua +++ b/Moose Development/Moose/Ops/Platoon.lua @@ -100,6 +100,7 @@ end -- Start & Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--[[ --- On after Start event. Starts the FLIGHTGROUP FSM and event handlers. -- @param #PLATOON self -- @param #string From From state. @@ -114,6 +115,7 @@ function PLATOON:onafterStart(From, Event, To) -- Start the status monitoring. self:__Status(-1) end +]] --- On after "Status" event. -- @param #PLATOON self