diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index 17c1b5345..aaf4fbe54 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -1140,8 +1140,8 @@ end -- @param DCS#Vec3 CV2 Vec3 function AI_FORMATION:FollowMe(FollowGroup, ClientUnit, CT1, CV1, CT2, CV2) - if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation then - + if FollowGroup:GetState( FollowGroup, "Mode" ) == self.__Enum.Mode.Formation and not self:Is("Stopped") then + self:T({Mode=FollowGroup:GetState( FollowGroup, "Mode" )}) FollowGroup:OptionROTEvadeFire() diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index bfd1dabb4..db04a1500 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -122,7 +122,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr self.Schedule[Scheduler][CallID].Function = ScheduleFunction self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 ) - self.Schedule[Scheduler][CallID].Start = Start + 0.1 + self.Schedule[Scheduler][CallID].Start = Start + 0.001 self.Schedule[Scheduler][CallID].Repeat = Repeat or 0 self.Schedule[Scheduler][CallID].Randomize = Randomize or 0 self.Schedule[Scheduler][CallID].Stop = Stop diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 41261abcc..87bcf9e4a 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -5411,10 +5411,12 @@ do -- SET_ZONE --- Get a random zone from the set. -- @param #SET_ZONE self + -- @param #number margin Number of tries to find a zone -- @return Core.Zone#ZONE_BASE The random Zone. -- @return #nil if no zone in the collection. - function SET_ZONE:GetRandomZone() - + function SET_ZONE:GetRandomZone(margin) + + local margin = margin or 100 if self:Count() ~= 0 then local Index = self.Index @@ -5423,9 +5425,11 @@ do -- SET_ZONE -- Loop until a zone has been found. -- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected. -- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues! - while not ZoneFound do + local counter = 0 + while (not ZoneFound) or (counter < margin) do local ZoneRandom = math.random( 1, #Index ) ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe() + counter = counter + 1 end return ZoneFound diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index a4ca58c19..a06b5ccc2 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -1,4 +1,4 @@ - --- **Functional** -- Modular, Automatic and Network capable Targeting and Interception System for Air Defenses +--- **Functional** -- Modular, Automatic and Network capable Targeting and Interception System for Air Defenses -- -- === -- @@ -37,7 +37,7 @@ -- @field #table SAM_Table Table of SAM sites -- @field #string lid Prefix for logging -- @field @{#Functional.Detection#DETECTION_AREAS} Detection The #DETECTION_AREAS object for EWR --- @field @{Functional.Detection#DETECTION_AREAS} AWACS_Detection The #DETECTION_AREAS object for AWACS +-- @field @{#Functional.Detection#DETECTION_AREAS} AWACS_Detection The #DETECTION_AREAS object for AWACS -- @field #boolean debug Switch on extra messages -- @field #boolean verbose Switch on extra logging -- @field #number checkradius Radius of the SAM sites @@ -96,7 +96,7 @@ -- -- # 2. Start up your MANTIS with a basic setting -- --- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` +-- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` -- `myredmantis:Start()` -- -- [optional] Use @@ -111,7 +111,7 @@ -- -- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup: -- --- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` +-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- `mybluemantis:Start()` -- -- # 3. Default settings @@ -181,6 +181,26 @@ do --@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional) --@param #string awacs Group name of your Awacs (optional) --@return #MANTIS self + --@usage Start up your MANTIS with a basic setting + -- + -- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` + -- `myredmantis:Start()` + -- + -- [optional] Use + -- + -- * `MANTIS:SetEWRGrouping(radius)` + -- * `MANTIS:SetEWRRange(radius)` + -- * `MANTIS:SetSAMRadius(radius)` + -- * `MANTIS:SetDetectInterval(interval)` + -- * `MANTIS:SetAutoRelocate(hq, ewr)` + -- + -- before starting #MANTIS to fine-tune your setup. + -- + -- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup: + -- + -- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` + -- `mybluemantis:Start()` + -- function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs) -- DONE: Create some user functions for these @@ -307,7 +327,7 @@ do self.engagerange = range end - --- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night + --- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night -- @param #MANTIS self -- @param #number range Percent of the max fire range function MANTIS:SetNewSAMRangeWhileRunning(range) @@ -535,7 +555,7 @@ do end end - --- Function to check if any object is in the given SAM zone + --- (Internal) Function to check if any object is in the given SAM zone -- @param #MANTIS self -- @param #table dectset Table of coordinates of detected items -- @param samcoordinate Core.Point#COORDINATE Coordinate object. @@ -562,7 +582,7 @@ do return false end - --- Function to start the detection via EWR groups + --- (Internal) Function to start the detection via EWR groups -- @param #MANTIS self -- @return Functional.Detection #DETECTION_AREAS The running detection set function MANTIS:StartDetection() @@ -593,7 +613,7 @@ do return _MANTISdetection end - --- Function to start the detection via AWACS if defined as separate + --- (Internal) Function to start the detection via AWACS if defined as separate -- @param #MANTIS self -- @return Functional.Detection #DETECTION_AREAS The running detection set function MANTIS:StartAwacsDetection() @@ -625,7 +645,7 @@ do return _MANTISAwacs end - --- Function to set the SAM start state + --- (Internal) Function to set the SAM start state -- @param #MANTIS self -- @return #MANTIS self function MANTIS:SetSAMStartState() diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 29b2918b2..044963a28 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -1318,6 +1318,10 @@ function ATIS:onafterBroadcast(From, Event, To) time=time-UTILS.GMTToLocalTimeDifference()*60*60 end + if time < 0 then + time = 24*60*60 + time --avoid negative time around midnight + end + local clock=UTILS.SecondsToClock(time) local zulu=UTILS.Split(clock, ":") local ZULU=string.format("%s%s", zulu[1], zulu[2]) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 0fd9130d6..7417a35cc 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -17,13 +17,13 @@ -- === -- -- ## Example Missions: --- +-- -- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20Flightgroup). --- +-- -- === -- -- ### Author: **funkyfranky** --- +-- -- === -- @module Ops.FlightGroup -- @image OPS_FlightGroup.png @@ -234,7 +234,7 @@ function FLIGHTGROUP:New(group) self.lid=string.format("FLIGHTGROUP %s | ", self.groupname) -- Defaults - self.isFlightgroup=true + self.isFlightgroup=true self:SetFuelLowThreshold() self:SetFuelLowRTB() self:SetFuelCriticalThreshold() @@ -325,10 +325,10 @@ function FLIGHTGROUP:New(group) -- Start the status monitoring. self:__Status(-1) - + -- Start queue update timer. self.timerQueueUpdate=TIMER:New(self._QueueUpdate, self):Start(2, 5) - + -- Start check zone timer. self.timerCheckZone=TIMER:New(self._CheckInZones, self):Start(3, 10) @@ -491,7 +491,7 @@ function FLIGHTGROUP:SetFuelCriticalRTB(switch) return self end ---- Enable to automatically engage detected targets. +--- Enable to automatically engage detected targets. -- @param #FLIGHTGROUP self -- @param #number RangeMax Max range in NM. Only detected targets within this radius from the group will be engaged. Default is 25 NM. -- @param #table TargetTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default "All". @@ -508,7 +508,7 @@ function FLIGHTGROUP:SetEngageDetectedOn(RangeMax, TargetTypes, EngageZoneSet, N else TargetTypes={"All"} end - + -- Ensure SET_ZONE if ZONE is provided. if EngageZoneSet and EngageZoneSet:IsInstanceOf("ZONE_BASE") then local zoneset=SET_ZONE:New():AddZone(EngageZoneSet) @@ -532,7 +532,7 @@ function FLIGHTGROUP:SetEngageDetectedOn(RangeMax, TargetTypes, EngageZoneSet, N return self end ---- Disable to automatically engage detected targets. +--- Disable to automatically engage detected targets. -- @param #FLIGHTGROUP self -- @return #FLIGHTGROUP self function FLIGHTGROUP:SetEngageDetectedOff() @@ -762,45 +762,45 @@ function FLIGHTGROUP:onbeforeStatus(From, Event, To) -- First we check if elements are still alive. Could be that they were despawned without notice, e.g. when landing on a too small airbase. for i,_element in pairs(self.elements) do local element=_element --#FLIGHTGROUP.Element - + -- Check that element is not already dead or not yet alive. if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then - + -- Unit shortcut. local unit=element.unit - - local isdead=false + + local isdead=false if unit and unit:IsAlive() then - + -- Get life points. local life=unit:GetLife() or 0 - + -- Units with life <=1 are dead. if life<=1 then --env.info(string.format("FF unit %s: live<=1 in status at T=%.3f", unit:GetName(), timer.getTime())) isdead=true end - + else -- Not alive any more. --env.info(string.format("FF unit %s: NOT alive in status at T=%.3f", unit:GetName(), timer.getTime())) isdead=true end - + -- This one is dead. if isdead then - local text=string.format("Element %s is dead at t=%.3f! Maybe despawned without notice or landed at a too small airbase. Calling ElementDead in 60 sec to give other events a chance", + local text=string.format("Element %s is dead at t=%.3f! Maybe despawned without notice or landed at a too small airbase. Calling ElementDead in 60 sec to give other events a chance", tostring(element.name), timer.getTime()) self:E(self.lid..text) self:__ElementDead(60, element) end - - end + + end end - if self:IsDead() then + if self:IsDead() then self:T(self.lid..string.format("Onbefore Status DEAD ==> false")) - return false + return false elseif self:IsStopped() then self:T(self.lid..string.format("Onbefore Status STOPPED ==> false")) return false @@ -818,7 +818,7 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) -- FSM state. local fsmstate=self:GetState() - + -- Update position. self:_UpdatePosition() @@ -865,22 +865,22 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) -- Short info. if self.verbose>=1 then - + local nTaskTot, nTaskSched, nTaskWP=self:CountRemainingTasks() local nMissions=self:CountRemainingMissison() - - + + local text=string.format("Status %s [%d/%d]: Tasks=%d (%d,%d) Curr=%d, Missions=%s, Waypoint=%d/%d, Detected=%d, Home=%s, Destination=%s", fsmstate, #self.elements, #self.elements, nTaskTot, nTaskSched, nTaskWP, self.taskcurrent, nMissions, self.currentwp or 0, self.waypoints and #self.waypoints or 0, self.detectedunits:Count(), self.homebase and self.homebase:GetName() or "unknown", self.destbase and self.destbase:GetName() or "unknown") self:I(self.lid..text) - + end --- -- Elements --- - + if self.verbose>=2 then local text="Elements:" for i,_element in pairs(self.elements) do @@ -958,7 +958,7 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) -- Log outut. self:I(self.lid..string.format("Travelled ds=%.1f km dt=%.1f s ==> v=%.1f knots. Fuel left for %.1f min", self.traveldist/1000, dt, UTILS.MpsToKnots(v), TmaxFuel/60)) - + end --- @@ -1008,34 +1008,34 @@ function FLIGHTGROUP:onafterStatus(From, Event, To) --- -- Engage Detected Targets - --- + --- if self:IsAirborne() and self.detectionOn and self.engagedetectedOn and not (self.fuellow or self.fuelcritical) then env.info("FF 100") - + -- Target. local targetgroup=nil --Wrapper.Group#GROUP local targetdist=math.huge - + -- Loop over detected groups. for _,_group in pairs(self.detectedgroups:GetSet()) do local group=_group --Wrapper.Group#GROUP - + env.info("FF 200") - + if group and group:IsAlive() then - + env.info("FF 300") - + -- Get 3D vector of target. local targetVec3=group:GetVec3() - -- Distance to target. + -- Distance to target. local distance=UTILS.VecDist3D(self.position, targetVec3) - + if distance<=self.engagedetectedRmax and distance destroyed", element.name)) + self:T(self.lid..string.format("EVENT: Element %s crashed ==> destroyed", element.name)) self:ElementDestroyed(element) end @@ -1271,7 +1271,7 @@ function FLIGHTGROUP:OnEventUnitLost(EventData) -- Check that this is the right group. if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then self:T2(self.lid..string.format("EVENT: Unit %s lost at t=%.3f", EventData.IniUnitName, timer.getTime())) - + local unit=EventData.IniUnit local group=EventData.IniGroup local unitname=EventData.IniUnitName @@ -1283,7 +1283,7 @@ function FLIGHTGROUP:OnEventUnitLost(EventData) self:T(self.lid..string.format("EVENT: Element %s unit lost ==> destroyed t=%.3f", element.name, timer.getTime())) self:ElementDestroyed(element) end - + end end @@ -1306,7 +1306,7 @@ function FLIGHTGROUP:onafterElementSpawned(From, Event, To, Element) -- Set element status. self:_UpdateStatus(Element, OPSGROUP.ElementStatus.SPAWNED) - if Element.unit:InAir() then + if Element.unit:InAir(true) then -- Trigger ElementAirborne event. Add a little delay because spawn is also delayed! self:__ElementAirborne(0.11, Element) else @@ -1433,26 +1433,26 @@ end -- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil. function FLIGHTGROUP:onafterElementLanded(From, Event, To, Element, airbase) self:T2(self.lid..string.format("Element landed %s at %s airbase", Element.name, airbase and airbase:GetName() or "unknown")) - + if self.despawnAfterLanding then - + -- Despawn the element. self:DespawnElement(Element) - + else -- Helos with skids land directly on parking spots. if self.ishelo then - + local Spot=self:GetParkingSpot(Element, 10, airbase) - + self:_SetElementParkingAt(Element, Spot) - + end - + -- Set element status. self:_UpdateStatus(Element, OPSGROUP.ElementStatus.LANDED, airbase) - + end end @@ -1483,7 +1483,7 @@ function FLIGHTGROUP:onafterElementDestroyed(From, Event, To, Element) -- Call OPSGROUP function. self:GetParent(self).onafterElementDestroyed(self, From, Event, To, Element) - + end --- On after "ElementDead" event. @@ -1503,7 +1503,7 @@ function FLIGHTGROUP:onafterElementDead(From, Event, To, Element) -- Not parking any more. Element.parking=nil - + end @@ -1515,7 +1515,7 @@ end function FLIGHTGROUP:onafterSpawned(From, Event, To) self:T(self.lid..string.format("Flight spawned")) - -- Update position. + -- Update position. self:_UpdatePosition() if self.isAI then @@ -1525,32 +1525,32 @@ function FLIGHTGROUP:onafterSpawned(From, Event, To) -- Set ROT. self:SwitchROT(self.option.ROT) - + -- Set Formation self:SwitchFormation(self.option.Formation) - + -- Set TACAN beacon. self:_SwitchTACAN() - + -- Set radio freq and modu. if self.radioDefault then self:SwitchRadio() else self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On) end - + -- Set callsign. if self.callsignDefault then self:SwitchCallsign(self.callsignDefault.NumberSquad, self.callsignDefault.NumberGroup) else self:SetDefaultCallsign(self.callsign.NumberSquad, self.callsign.NumberGroup) end - + -- TODO: make this input. self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT, true) self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB, true) -- Does not seem to work. AI still used the after burner. self:GetGroup():SetOption(AI.Option.Air.id.RTB_ON_BINGO, false) - --self.group:SetOption(AI.Option.Air.id.RADAR_USING, AI.Option.Air.val.RADAR_USING.FOR_CONTINUOUS_SEARCH) + --self.group:SetOption(AI.Option.Air.id.RADAR_USING, AI.Option.Air.val.RADAR_USING.FOR_CONTINUOUS_SEARCH) -- Update route. self:__UpdateRoute(-0.5) @@ -1690,7 +1690,7 @@ function FLIGHTGROUP:onafterLanded(From, Event, To, airbase) -- Add flight to taxiinb queue. self.flightcontrol:SetFlightStatus(self, FLIGHTCONTROL.FlightStatus.TAXIINB) end - + end --- On after "LandedAt" event. @@ -1699,7 +1699,7 @@ end -- @param #string Event Event. -- @param #string To To state. function FLIGHTGROUP:onafterLandedAt(From, Event, To) - self:T(self.lid..string.format("Flight landed at")) + self:T(self.lid..string.format("Flight landed at")) end @@ -1735,22 +1735,22 @@ function FLIGHTGROUP:onafterDead(From, Event, To) self.flightcontrol:_RemoveFlight(self) self.flightcontrol=nil end - + if self.Ndestroyed==#self.elements then if self.squadron then -- All elements were destroyed ==> Asset group is gone. self.squadron:DelGroup(self.groupname) - end + end else if self.airwing then -- Not all assets were destroyed (despawn) ==> Add asset back to airwing. - self.airwing:AddAsset(self.group, 1) + self.airwing:AddAsset(self.group, 1) end - end + end -- Call OPSGROUP function. self:GetParent(self).onafterDead(self, From, Event, To) - + end @@ -1799,10 +1799,10 @@ function FLIGHTGROUP:onbeforeUpdateRoute(From, Event, To, n) end if self.taskcurrent>0 then - + --local task=self:GetTaskCurrent() local task=self:GetTaskByID(self.taskcurrent) - + if task then if task.dcstask.id=="PatrolZone" then -- For patrol zone, we need to allow the update. @@ -1861,7 +1861,7 @@ function FLIGHTGROUP:onafterUpdateRoute(From, Event, To, n) -- Set current waypoint or we get problem that the _PassingWaypoint function is triggered too early, i.e. right now and not when passing the next WP. local current=self.group:GetCoordinate():WaypointAir(COORDINATE.WaypointAltType.BARO, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, speed, true, nil, {}, "Current") table.insert(wp, current) - + local Nwp=self.waypoints and #self.waypoints or 0 -- Add remaining waypoints to route. @@ -1885,7 +1885,7 @@ function FLIGHTGROUP:onafterUpdateRoute(From, Event, To, n) --- -- No waypoints left --- - + if self:IsAirborne() then self:T(self.lid.."No waypoints left ==> CheckGroupDone") self:_CheckGroupDone() @@ -1919,7 +1919,7 @@ function FLIGHTGROUP:_CheckGroupDone(delay) self:UnpauseMission() return end - + -- Group is currently engaging. if self:IsEngaging() then return @@ -1939,7 +1939,7 @@ function FLIGHTGROUP:_CheckGroupDone(delay) -- Number of remaining tasks/missions? if nTasks==0 and nMissions==0 then - + local destbase=self.destbase or self.homebase local destzone=self.destzone or self.homezone @@ -2062,19 +2062,19 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp -- Clear holding time in any case. self.Tholding=nil - + -- Cancel all missions. for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG local mystatus=mission:GetGroupStatus(self) - + -- Check if mission is already over! if not (mystatus==AUFTRAG.GroupStatus.DONE or mystatus==AUFTRAG.GroupStatus.CANCELLED) then local text=string.format("Canceling mission %s in state=%s", mission.name, mission.status) self:T(self.lid..text) self:MissionCancel(mission) end - + end -- Defaults: @@ -2176,7 +2176,7 @@ function FLIGHTGROUP:onafterRTB(From, Event, To, airbase, SpeedTo, SpeedHold, Sp -- Clear all tasks. -- Warning, looks like this can make DCS CRASH! Had this after calling RTB once passed the final waypoint. --self:ClearTasks() - + -- Just route the group. Respawn might happen when going from holding to final. self:Route(wp, 1) @@ -2374,39 +2374,39 @@ function FLIGHTGROUP:onafterEngageTarget(From, Event, To, Target) -- Check target object. if Target:IsInstanceOf("UNIT") or Target:IsInstanceOf("STATIC") then - + DCStask=self:GetGroup():TaskAttackUnit(Target, true) - + elseif Target:IsInstanceOf("GROUP") then DCStask=self:GetGroup():TaskAttackGroup(Target, nil, nil, nil, nil, nil, nil, true) - + elseif Target:IsInstanceOf("SET_UNIT") then local DCSTasks={} - + for _,_unit in pairs(Target:GetSet()) do --detected by =HRP= Zero local unit=_unit --Wrapper.Unit#UNIT local task=self:GetGroup():TaskAttackUnit(unit, true) table.insert(DCSTasks) end - + -- Task combo. DCStask=self:GetGroup():TaskCombo(DCSTasks) elseif Target:IsInstanceOf("SET_GROUP") then local DCSTasks={} - + for _,_unit in pairs(Target:GetSet()) do --detected by =HRP= Zero local unit=_unit --Wrapper.Unit#UNIT local task=self:GetGroup():TaskAttackGroup(Target, nil, nil, nil, nil, nil, nil, true) table.insert(DCSTasks) end - + -- Task combo. DCStask=self:GetGroup():TaskCombo(DCSTasks) - + else self:E("ERROR: unknown Target in EngageTarget! Needs to be a UNIT, STATIC, GROUP, SET_UNIT or SET_GROUP") return @@ -2414,10 +2414,10 @@ function FLIGHTGROUP:onafterEngageTarget(From, Event, To, Target) -- Create new task.The description "Engage_Target" is checked so do not change that lightly. local Task=self:NewTaskScheduled(DCStask, 1, "Engage_Target", 0) - + -- Backup ROE setting. Task.backupROE=self:GetROE() - + -- Switch ROE to open fire self:SwitchROE(ENUMS.ROE.OpenFire) @@ -2428,7 +2428,7 @@ function FLIGHTGROUP:onafterEngageTarget(From, Event, To, Target) end -- Execute task. - self:TaskExecute(Task) + self:TaskExecute(Task) end @@ -2501,9 +2501,9 @@ function FLIGHTGROUP:onafterFuelLow(From, Event, To) local tanker=self.airwing:GetTankerForFlight(self) if tanker then - + self:I(self.lid..string.format("Send to refuel at tanker %s", tanker.flightgroup:GetName())) - + -- Get a coordinate towards the tanker. local coordinate=self:GetCoordinate():GetIntermediateCoordinate(tanker.flightgroup:GetCoordinate(), 0.75) @@ -2600,12 +2600,12 @@ function FLIGHTGROUP:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.Ejection) self:UnHandleEvent(EVENTS.Crash) self:UnHandleEvent(EVENTS.RemoveUnit) - + -- Call OPSGROUP function. self:GetParent(self).onafterStop(self, From, Event, To) - + -- Remove flight from data base. - _DATABASE.FLIGHTGROUPS[self.groupname]=nil + _DATABASE.FLIGHTGROUPS[self.groupname]=nil end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2668,7 +2668,7 @@ function FLIGHTGROUP:_InitGroup() self:E(self.lid.."WARNING: Group was already initialized!") return end - + -- Group object. local group=self.group --Wrapper.Group#GROUP @@ -2705,7 +2705,7 @@ function FLIGHTGROUP:_InitGroup() self.radio.Freq=tonumber(self.template.frequency) self.radio.Modu=tonumber(self.template.modulation) self.radio.On=self.template.communication - + -- Set callsign. Default is set on spawn if not modified by user. local callsign=self.template.units[1].callsign if type(callsign)=="number" then -- Sometimes callsign is just "101". @@ -2726,7 +2726,7 @@ function FLIGHTGROUP:_InitGroup() else self.optionDefault.Formation=ENUMS.Formation.FixedWing.EchelonLeft.Group end - + -- Default TACAN off. self:SetDefaultTACAN(nil, nil, nil, nil, true) self.tacan=UTILS.DeepCopy(self.tacanDefault) @@ -2870,7 +2870,7 @@ function FLIGHTGROUP:GetHomebaseFromWaypoints() -- Get airbase ID depending on airbase category. local airbaseID=nil - + if wp.airdromeId then airbaseID=wp.airdromeId else @@ -2878,7 +2878,7 @@ function FLIGHTGROUP:GetHomebaseFromWaypoints() end local airbase=AIRBASE:FindByID(airbaseID) - + return airbase end @@ -3126,14 +3126,14 @@ function FLIGHTGROUP:InitWaypoints() -- Template waypoints. self.waypoints0=self.group:GetTemplateRoutePoints() - -- Waypoints + -- Waypoints self.waypoints={} - + for index,wp in pairs(self.waypoints0) do - - local waypoint=self:_CreateWaypoint(wp) + + local waypoint=self:_CreateWaypoint(wp) self:_AddWaypoint(waypoint) - + end -- Get home and destination airbases from waypoints. @@ -3188,7 +3188,7 @@ function FLIGHTGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Altitud -- Create waypoint data table. local waypoint=self:_CreateWaypoint(wp) - + -- Set altitude. if Altitude then waypoint.alt=UTILS.FeetToMeters(Altitude) @@ -3426,12 +3426,12 @@ end function FLIGHTGROUP:GetClosestAirbase() local group=self.group --Wrapper.Group#GROUP - + local coord=group:GetCoordinate() local coalition=self:GetCoalition() - + local airbase=coord:GetClosestAirbase() --(nil, coalition) - + return airbase end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 9341875a9..8b13d0d86 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -2646,7 +2646,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) local TaskFinal=self.group:TaskCombo({TaskControlled, TaskDone}) -- Set task for group. - self:SetTask(TaskFinal, 1) + self:SetTask(TaskFinal) end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 182537ce6..2ca10e6f9 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1500,4 +1500,24 @@ function UTILS.GetOSTime() end return nil -end \ No newline at end of file +end + +--- Shuffle a table accoring to Fisher Yeates algorithm +--@param #table table to be shuffled +--@return #table +function UTILS.ShuffleTable(t) + if t == nil or type(t) ~= "table" then + BASE:I("Error in ShuffleTable: Missing or wrong tyƄe of Argument") + return + end + math.random() + math.random() + math.random() + local TempTable = {} + for i = 1, #t do + local r = math.random(1,#t) + TempTable[i] = t[r] + table.remove(t,r) + end + return TempTable +end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 5a61ebb1f..595b0e473 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1894,7 +1894,7 @@ do -- Patrol methods local ToCoord = COORDINATE:NewFromVec2( { x = Waypoint.x, y = Waypoint.y } ) -- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task local Route = {} - Route[#Route+1] = FromCoord:WaypointGround( 0 ) + Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation ) Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation ) @@ -1947,7 +1947,7 @@ do -- Patrol methods -- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task local Route = {} - Route[#Route+1] = FromCoord:WaypointGround( 20 ) + Route[#Route+1] = FromCoord:WaypointGround( Speed, Formation ) Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation ) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 705e29303..beb43fc4c 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -1174,8 +1174,9 @@ end --- Returns true if the UNIT is in the air. -- @param #UNIT self +-- @param #boolean NoHeloCheck If true, no additonal checks for helos are performed. -- @return #boolean Return true if in the air or #nil if the UNIT is not existing or alive. -function UNIT:InAir() +function UNIT:InAir(NoHeloCheck) self:F2( self.UnitName ) -- Get DCS unit object. @@ -1185,14 +1186,14 @@ function UNIT:InAir() -- Get DCS result of whether unit is in air or not. local UnitInAir = DCSUnit:inAir() - + -- Get unit category. local UnitCategory = DCSUnit:getDesc().category -- If DCS says that it is in air, check if this is really the case, since we might have landed on a building where inAir()=true but actually is not. -- This is a workaround since DCS currently does not acknoledge that helos land on buildings. -- Note however, that the velocity check will fail if the ground is moving, e.g. on an aircraft carrier! - if UnitInAir==true and UnitCategory == Unit.Category.HELICOPTER then + if UnitInAir==true and UnitCategory == Unit.Category.HELICOPTER and (not NoHeloCheck) then local VelocityVec3 = DCSUnit:getVelocity() local Velocity = UTILS.VecNorm(VelocityVec3) local Coordinate = DCSUnit:getPoint()