diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index 9eedd29cc..45fef37cf 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -287,15 +287,15 @@ function AI_A2A_PATROL:onafterPatrol( AIPatrol, From, Event, To ) end - ---- @param Wrapper.Group#GROUP AIPatrol --- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol. +--- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol. -- Note that this method is required, as triggers the next route when patrolling for the AIPatrol. +-- @param Wrapper.Group#GROUP AIPatrol The AI group. +-- @param #AI_A2A_PATROL Fsm The FSM. function AI_A2A_PATROL.PatrolRoute( AIPatrol, Fsm ) AIPatrol:F( { "AI_A2A_PATROL.PatrolRoute:", AIPatrol:GetName() } ) - if AIPatrol:IsAlive() then + if AIPatrol and AIPatrol:IsAlive() then Fsm:Route() end @@ -309,7 +309,6 @@ end -- @param #string Event The Event string. -- @param #string To The To State string. function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To ) - self:F2() -- When RTB, don't allow anymore the routing. diff --git a/Moose Development/Moose/AI/AI_Air_Engage.lua b/Moose Development/Moose/AI/AI_Air_Engage.lua index 806376f83..50b2e0988 100644 --- a/Moose Development/Moose/AI/AI_Air_Engage.lua +++ b/Moose Development/Moose/AI/AI_Air_Engage.lua @@ -80,7 +80,8 @@ AI_AIR_ENGAGE = { --- Creates a new AI_AIR_ENGAGE object -- @param #AI_AIR_ENGAGE self --- @param Wrapper.Group#GROUP AIGroup +-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM. +-- @param Wrapper.Group#GROUP AIGroup The AI group. -- @param DCS#Speed EngageMinSpeed (optional, default = 50% of max speed) The minimum speed of the @{Wrapper.Group} in km/h when engaging a target. -- @param DCS#Speed EngageMaxSpeed (optional, default = 75% of max speed) The maximum speed of the @{Wrapper.Group} in km/h when engaging a target. -- @param DCS#Altitude EngageFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the engagement. diff --git a/Moose Development/Moose/AI/AI_Air_Patrol.lua b/Moose Development/Moose/AI/AI_Air_Patrol.lua index 3f133f8fa..6458c80d1 100644 --- a/Moose Development/Moose/AI/AI_Air_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Air_Patrol.lua @@ -99,7 +99,8 @@ AI_AIR_PATROL = { --- Creates a new AI_AIR_PATROL object -- @param #AI_AIR_PATROL self --- @param Wrapper.Group#GROUP AIGroup +-- @param AI.AI_Air#AI_AIR AI_Air The AI_AIR FSM. +-- @param Wrapper.Group#GROUP AIGroup The AI group. -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param DCS#Altitude PatrolFloorAltitude (optional, default = 1000m ) The lowest altitude in meters where to execute the patrol. -- @param DCS#Altitude PatrolCeilingAltitude (optional, default = 1500m ) The highest altitude in meters where to execute the patrol. @@ -270,14 +271,15 @@ function AI_AIR_PATROL:onafterPatrol( AIPatrol, From, Event, To ) ) end ---- @param Wrapper.Group#GROUP AIPatrol --- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol. +--- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol. -- Note that this method is required, as triggers the next route when patrolling for the AIPatrol. +-- @param Wrapper.Group#GROUP AIPatrol The AI group. +-- @param #AI_AIR_PATROL Fsm The FSM. function AI_AIR_PATROL.___PatrolRoute( AIPatrol, Fsm ) AIPatrol:F( { "AI_AIR_PATROL.___PatrolRoute:", AIPatrol:GetName() } ) - if AIPatrol:IsAlive() then + if AIPatrol and AIPatrol:IsAlive() then Fsm:PatrolRoute() end @@ -299,7 +301,7 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To ) end - if AIPatrol:IsAlive() then + if AIPatrol and AIPatrol:IsAlive() then local PatrolRoute = {} @@ -316,13 +318,7 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To ) local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed ) local speedkmh=ToTargetSpeed - local FromWP = CurrentCoord:WaypointAir( - self.PatrolAltType or "RADIO", - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToTargetSpeed, - true - ) + local FromWP = CurrentCoord:WaypointAir(self.PatrolAltType or "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true) PatrolRoute[#PatrolRoute+1] = FromWP if self.racetrack then @@ -360,7 +356,8 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To ) local taskOrbit=AIPatrol:TaskOrbit(c1, altitude, UTILS.KmphToMps(speedkmh), c2) -- Task function to redo the patrol at other random position. - local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute", self) + --local taskPatrol=AIPatrol:TaskFunction("AI_A2A_PATROL.PatrolRoute", self) + local taskPatrol=AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self) -- Controlled task with task condition. local taskCond=AIPatrol:TaskCondition(nil, nil, nil, nil, duration, nil) @@ -372,18 +369,11 @@ function AI_AIR_PATROL:onafterPatrolRoute( AIPatrol, From, Event, To ) else --- Create a route point of type air. - local ToWP = ToTargetCoord:WaypointAir( - self.PatrolAltType, - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - ToTargetSpeed, - true - ) - + local ToWP = ToTargetCoord:WaypointAir(self.PatrolAltType, POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, ToTargetSpeed, true) PatrolRoute[#PatrolRoute+1] = ToWP local Tasks = {} - Tasks[#Tasks+1] = AIPatrol:TaskFunction( "AI_AIR_PATROL.___PatrolRoute", self ) + Tasks[#Tasks+1] = AIPatrol:TaskFunction("AI_AIR_PATROL.___PatrolRoute", self) PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks ) end @@ -401,7 +391,7 @@ end function AI_AIR_PATROL.Resume( AIPatrol, Fsm ) AIPatrol:F( { "AI_AIR_PATROL.Resume:", AIPatrol:GetName() } ) - if AIPatrol:IsAlive() then + if AIPatrol and AIPatrol:IsAlive() then Fsm:__Reset( Fsm.TaskDelay ) Fsm:__PatrolRoute( Fsm.TaskDelay ) end diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 031d9aae8..dcb94b99d 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -636,7 +636,7 @@ function AI_PATROL_ZONE:onafterStart( Controllable, From, Event, To ) self.Controllable:OnReSpawn( function( PatrolGroup ) - self:E( "ReSpawn" ) + self:T( "ReSpawn" ) self:__Reset( 1 ) self:__Route( 5 ) end @@ -741,7 +741,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To ) -- This will make the plane fly immediately to the patrol zone. if self.Controllable:InAir() == false then - self:E( "Not in the air, finding route path within PatrolZone" ) + self:T( "Not in the air, finding route path within PatrolZone" ) local CurrentVec2 = self.Controllable:GetVec2() --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() @@ -756,7 +756,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To ) ) PatrolRoute[#PatrolRoute+1] = CurrentRoutePoint else - self:E( "In the air, finding route path within PatrolZone" ) + self:T( "In the air, finding route path within PatrolZone" ) local CurrentVec2 = self.Controllable:GetVec2() --TODO: Create GetAltitude function for GROUP, and delete GetUnit(1). local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude() diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 1a2af6d62..fa12803fc 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -187,7 +187,7 @@ end function DATABASE:AddUnit( DCSUnitName ) if not self.UNITS[DCSUnitName] then - self:I( { "Add UNIT:", DCSUnitName } ) + self:T( { "Add UNIT:", DCSUnitName } ) local UnitRegister = UNIT:Register( DCSUnitName ) self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName ) @@ -509,7 +509,7 @@ end function DATABASE:AddGroup( GroupName ) if not self.GROUPS[GroupName] then - self:I( { "Add GROUP:", GroupName } ) + self:T( { "Add GROUP:", GroupName } ) self.GROUPS[GroupName] = GROUP:Register( GroupName ) end @@ -521,7 +521,7 @@ end function DATABASE:AddPlayer( UnitName, PlayerName ) if PlayerName then - self:I( { "Add player for unit:", UnitName, PlayerName } ) + self:T( { "Add player for unit:", UnitName, PlayerName } ) self.PLAYERS[PlayerName] = UnitName self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName ) self.PLAYERSJOINED[PlayerName] = PlayerName @@ -533,7 +533,7 @@ end function DATABASE:DeletePlayer( UnitName, PlayerName ) if PlayerName then - self:I( { "Clean player:", PlayerName } ) + self:T( { "Clean player:", PlayerName } ) self.PLAYERS[PlayerName] = nil self.PLAYERUNITS[PlayerName] = nil end @@ -698,11 +698,11 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName end - self:I( { Group = self.Templates.Groups[GroupTemplateName].GroupName, + self:T( { Group = self.Templates.Groups[GroupTemplateName].GroupName, Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID, - Category = self.Templates.Groups[GroupTemplateName].CategoryID, - Country = self.Templates.Groups[GroupTemplateName].CountryID, - Units = UnitNames + Category = self.Templates.Groups[GroupTemplateName].CategoryID, + Country = self.Templates.Groups[GroupTemplateName].CountryID, + Units = UnitNames } ) end @@ -847,9 +847,9 @@ function DATABASE:_RegisterGroupsAndUnits() end end - self:I("Groups:") + self:T("Groups:") for GroupName, Group in pairs( self.GROUPS ) do - self:I( { "Group:", GroupName } ) + self:T( { "Group:", GroupName } ) end return self @@ -861,7 +861,7 @@ end function DATABASE:_RegisterClients() for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do - self:I( { "Register Client:", ClientName } ) + self:T( { "Register Client:", ClientName } ) self:AddClient( ClientName ) end @@ -879,7 +879,7 @@ function DATABASE:_RegisterStatics() if DCSStatic:isExist() then local DCSStaticName = DCSStatic:getName() - self:I( { "Register Static:", DCSStaticName } ) + self:T( { "Register Static:", DCSStaticName } ) self:AddStatic( DCSStaticName ) else self:E( { "Static does not exist: ", DCSStatic } ) @@ -899,7 +899,7 @@ function DATABASE:_RegisterAirbases() local DCSAirbaseName = DCSAirbase:getName() - self:I( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } ) + self:T( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } ) self:AddAirbase( DCSAirbaseName ) end end diff --git a/Moose Development/Moose/Core/Goal.lua b/Moose Development/Moose/Core/Goal.lua index ac1f616b9..c145d6de4 100644 --- a/Moose Development/Moose/Core/Goal.lua +++ b/Moose Development/Moose/Core/Goal.lua @@ -15,6 +15,7 @@ -- === -- -- ### Author: **FlightControl** +-- ### Contributions: **funkyfranky** -- -- === -- diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index c85943d75..01b8a7000 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -53,6 +53,19 @@ SCHEDULEDISPATCHER = { Schedule = nil, } +--- Player data table holding all important parameters of each player. +-- @type SCHEDULEDISPATCHER.ScheduleData +-- @field #function Function The schedule function to be called. +-- @field #table Arguments Schedule function arguments. +-- @field #number Start Start time in seconds. +-- @field #number Repeat Repeat time intervall in seconds. +-- @field #number Randomize Randomization factor [0,1]. +-- @field #number Stop Stop time in seconds. +-- @field #number StartTime Time in seconds when the scheduler is created. +-- @field #number ScheduleID Schedule ID. +-- @field #function CallHandler Function to be passed to the DCS timer.scheduleFunction(). +-- @field #boolean ShowTrace If true, show tracing info. + --- Create a new schedule dispatcher object. -- @param #SCHEDULEDISPATCHER self -- @return #SCHEDULEDISPATCHER self @@ -76,7 +89,7 @@ end -- @param #number Stop Stop time in seconds. -- @param #number TraceLevel Trace level [0,3]. -- @param Core.Fsm#FSM Fsm Finite state model. --- @return #table Call ID or nil. +-- @return #string Call ID or nil. function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel, Fsm ) self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop, TraceLevel, Fsm } ) @@ -85,9 +98,10 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr -- Create ID. local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or "" + + self:T2(string.format("Adding schedule #%d CallID=%s", self.CallID, CallID)) - -- Initialize the ObjectSchedulers array, which is a weakly coupled table. - -- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array. + -- Initialize PersistentSchedulers self.PersistentSchedulers = self.PersistentSchedulers or {} -- Initialize the ObjectSchedulers array, which is a weakly coupled table. @@ -104,11 +118,11 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } ) self.Schedule[Scheduler] = self.Schedule[Scheduler] or {} - self.Schedule[Scheduler][CallID] = {} + self.Schedule[Scheduler][CallID] = {} --#SCHEDULEDISPATCHER.ScheduleData 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 + .1 + self.Schedule[Scheduler][CallID].Start = Start + 0.1 self.Schedule[Scheduler][CallID].Repeat = Repeat or 0 self.Schedule[Scheduler][CallID].Randomize = Randomize or 0 self.Schedule[Scheduler][CallID].Stop = Stop @@ -150,6 +164,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr self:T3( self.Schedule[Scheduler][CallID] ) + --- Function passed to the DCS timer.scheduleFunction() self.Schedule[Scheduler][CallID].CallHandler = function( Params ) local CallID = Params.CallID @@ -166,7 +181,8 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr return errmsg end - local Scheduler = self.ObjectSchedulers[CallID] + -- Get object or persistant scheduler object. + local Scheduler = self.ObjectSchedulers[CallID] --Core.Scheduler#SCHEDULER if not Scheduler then Scheduler = self.PersistentSchedulers[CallID] end @@ -175,20 +191,24 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr if Scheduler then - local MasterObject = tostring(Scheduler.MasterObject) - local Schedule = self.Schedule[Scheduler][CallID] + local MasterObject = tostring(Scheduler.MasterObject) + + -- Schedule object. + local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData --self:T3( { Schedule = Schedule } ) local SchedulerObject = Scheduler.MasterObject --Scheduler.SchedulerObject Now is this the Maste or Scheduler object? - local ScheduleFunction = Schedule.Function + local ShowTrace = Scheduler.ShowTrace + + local ScheduleFunction = Schedule.Function local ScheduleArguments = Schedule.Arguments or {} - local Start = Schedule.Start - local Repeat = Schedule.Repeat or 0 - local Randomize = Schedule.Randomize or 0 - local Stop = Schedule.Stop or 0 - local ScheduleID = Schedule.ScheduleID - local ShowTrace = Scheduler.ShowTrace + local Start = Schedule.Start + local Repeat = Schedule.Repeat or 0 + local Randomize = Schedule.Randomize or 0 + local Stop = Schedule.Stop or 0 + local ScheduleID = Schedule.ScheduleID + local Prefix = ( Repeat == 0 ) and "--->" or "+++>" @@ -215,24 +235,20 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr local CurrentTime = timer.getTime() local StartTime = Schedule.StartTime - self:F3( { Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } ) + -- Debug info. + self:F3( { CallID=CallID, ScheduleID=ScheduleID, Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } ) if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then + if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then - local ScheduleTime = - CurrentTime + - Repeat + - math.random( - - ( Randomize * Repeat / 2 ), - ( Randomize * Repeat / 2 ) - ) + - 0.0001 -- Accuracy + local ScheduleTime = CurrentTime + Repeat + math.random(- ( Randomize * Repeat / 2 ), ( Randomize * Repeat / 2 )) + 0.0001 -- Accuracy --self:T3( { Repeat = CallID, CurrentTime, ScheduleTime, ScheduleArguments } ) return ScheduleTime -- returns the next time the function needs to be called. else self:Stop( Scheduler, CallID ) end + else self:Stop( Scheduler, CallID ) end @@ -265,24 +281,27 @@ end -- @param #SCHEDULEDISPATCHER self -- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object. -- @param #table CallID (Optional) Call ID. --- @param #table CallID Call ID. -- @param #string Info (Optional) Debug info. function SCHEDULEDISPATCHER:Start( Scheduler, CallID, Info ) self:F2( { Start = CallID, Scheduler = Scheduler } ) if CallID then - local Schedule = self.Schedule[Scheduler] + local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData -- Only start when there is no ScheduleID defined! -- This prevents to "Start" the scheduler twice with the same CallID... - if not Schedule[CallID].ScheduleID then - Schedule[CallID].StartTime = timer.getTime() -- Set the StartTime field to indicate when the scheduler started. - Schedule[CallID].ScheduleID = timer.scheduleFunction( - Schedule[CallID].CallHandler, - { CallID = CallID, Info = Info }, - timer.getTime() + Schedule[CallID].Start - ) + if not Schedule.ScheduleID then + + -- Current time in seconds. + local Tnow=timer.getTime() + + Schedule.StartTime = Tnow -- Set the StartTime field to indicate when the scheduler started. + + -- Start DCS schedule function https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction + Schedule.ScheduleID = timer.scheduleFunction(Schedule.CallHandler, { CallID = CallID, Info = Info }, Tnow + Schedule.Start) + + self:T(string.format("Starting scheduledispatcher Call ID=%s ==> Schedule ID=%s", tostring(CallID), tostring(Schedule.ScheduleID))) end else @@ -304,12 +323,20 @@ function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) if CallID then - local Schedule = self.Schedule[Scheduler] + local Schedule = self.Schedule[Scheduler][CallID] --#SCHEDULEDISPATCHER.ScheduleData -- Only stop when there is a ScheduleID defined for the CallID. So, when the scheduler was stopped before, do nothing. - if Schedule[CallID].ScheduleID then - timer.removeFunction( Schedule[CallID].ScheduleID ) - Schedule[CallID].ScheduleID = nil + if Schedule.ScheduleID then + + self:T(string.format("scheduledispatcher stopping scheduler CallID=%s, ScheduleID=%s", tostring(CallID), tostring(Schedule.ScheduleID))) + + -- Remove schedule function https://wiki.hoggitworld.com/view/DCS_func_removeFunction + timer.removeFunction(Schedule.ScheduleID) + + Schedule.ScheduleID = nil + + else + self:E(string.format("Error no ScheduleID for CallID=%s", tostring(CallID))) end else diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index e5ef5960f..781b90ebe 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -221,7 +221,7 @@ function SCHEDULER:New( MasterObject, SchedulerFunction, SchedulerArguments, Sta self.ShowTrace = false if SchedulerFunction then - ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 4 ) + ScheduleID = self:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, 3 ) end return self, ScheduleID @@ -243,13 +243,17 @@ function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments self:F2( { Start, Repeat, RandomizeFactor, Stop } ) self:T3( { SchedulerArguments } ) + -- Debug info. local ObjectName = "-" if MasterObject and MasterObject.ClassName and MasterObject.ClassID then ObjectName = MasterObject.ClassName .. MasterObject.ClassID end self:F3( { "Schedule :", ObjectName, tostring( MasterObject ), Start, Repeat, RandomizeFactor, Stop } ) + + -- Set master object. self.MasterObject = MasterObject + -- Add schedule. local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( self, SchedulerFunction, @@ -269,32 +273,36 @@ end --- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided. -- @param #SCHEDULER self --- @param #table ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule. +-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule. function SCHEDULER:Start( ScheduleID ) self:F3( { ScheduleID } ) + self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID))) _SCHEDULEDISPATCHER:Start( self, ScheduleID ) end --- Stops the schedules or a specific schedule if a valid ScheduleID is provided. -- @param #SCHEDULER self --- @param #table ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule. +-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule. function SCHEDULER:Stop( ScheduleID ) self:F3( { ScheduleID } ) + self:T(string.format("Stopping scheduler ID=%s", tostring(ScheduleID))) _SCHEDULEDISPATCHER:Stop( self, ScheduleID ) end --- Removes a specific schedule if a valid ScheduleID is provided. -- @param #SCHEDULER self --- @param #number ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. +-- @param #string ScheduleID (optional) The ScheduleID of the planned (repeating) schedule. function SCHEDULER:Remove( ScheduleID ) self:F3( { ScheduleID } ) - _SCHEDULEDISPATCHER:Remove( self, ScheduleID ) + self:T(string.format("Removing scheduler ID=%s", tostring(ScheduleID))) + _SCHEDULEDISPATCHER:RemoveSchedule( self, ScheduleID ) end --- Clears all pending schedules. -- @param #SCHEDULER self function SCHEDULER:Clear() self:F3( ) + self:T(string.format("Clearing scheduler")) _SCHEDULEDISPATCHER:Clear( self ) end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 9dedcb35a..a1db93d70 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -56,7 +56,7 @@ --- @type ZONE_BASE -- @field #string ZoneName Name of the zone. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. --- @extends Core.Base#BASE +-- @extends Core.Fsm#FSM --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. @@ -685,30 +685,44 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) local function EvaluateZone( ZoneObject ) --if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5 - if ZoneObject then + if ZoneObject then + local ObjectCategory = ZoneObject:getCategory() - if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or - (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then + + if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then + local CoalitionDCSUnit = ZoneObject:getCoalition() + local Include = false if not UnitCategories then + -- Anythink found is included. Include = true else + -- Check if found object is in specified categories. local CategoryDCSUnit = ZoneObject:getDesc().category + for UnitCategoryID, UnitCategory in pairs( UnitCategories ) do if UnitCategory == CategoryDCSUnit then Include = true break end end + end + if Include then + local CoalitionDCSUnit = ZoneObject:getCoalition() + + -- This coalition is inside the zone. self.ScanData.Coalitions[CoalitionDCSUnit] = true + self.ScanData.Units[ZoneObject] = ZoneObject + self:F2( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } ) end end + if ObjectCategory == Object.Category.SCENERY then local SceneryType = ZoneObject:getTypeName() local SceneryName = ZoneObject:getName() @@ -716,21 +730,29 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject ) self:F2( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } ) end + end + return true end + -- Search objects. world.searchObjects( ObjectCategories, SphereSearch, EvaluateZone ) end - +--- Count the number of different coalitions inside the zone. +-- @param #ZONE_RADIUS self +-- @return #table Table of DCS units and DCS statics inside the zone. function ZONE_RADIUS:GetScannedUnits() return self.ScanData.Units end +--- Count the number of different coalitions inside the zone. +-- @param #ZONE_RADIUS self +-- @return Core.Set#SET_UNIT Set of units and statics inside the zone. function ZONE_RADIUS:GetScannedSetUnit() local SetUnit = SET_UNIT:New() @@ -756,6 +778,9 @@ function ZONE_RADIUS:GetScannedSetUnit() end +--- Count the number of different coalitions inside the zone. +-- @param #ZONE_RADIUS self +-- @return #number Counted coalitions. function ZONE_RADIUS:CountScannedCoalitions() local Count = 0 @@ -763,14 +788,25 @@ function ZONE_RADIUS:CountScannedCoalitions() for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do Count = Count + 1 end + return Count end +--- Check if a certain coalition is inside a scanned zone. +-- @param #ZONE_RADIUS self +-- @param #number Coalition The coalition id, e.g. coalition.side.BLUE. +-- @return #boolean If true, the coalition is inside the zone. +function ZONE_RADIUS:CheckScannedCoalition( Coalition ) + if Coalition then + return self.ScanData.Coalitions[Coalition] + end + return nil +end --- Get Coalitions of the units in the Zone, or Check if there are units of the given Coalition in the Zone. --- Returns nil if there are none ot two Coalitions in the zone! +-- Returns nil if there are none to two Coalitions in the zone! -- Returns one Coalition if there are only Units of one Coalition in the Zone. --- Returns the Coalition for the given Coalition if there are units of the Coalition in the Zone +-- Returns the Coalition for the given Coalition if there are units of the Coalition in the Zone. -- @param #ZONE_RADIUS self -- @return #table function ZONE_RADIUS:GetScannedCoalition( Coalition ) @@ -795,20 +831,27 @@ function ZONE_RADIUS:GetScannedCoalition( Coalition ) end +--- Get scanned scenery type +-- @param #ZONE_RADIUS self +-- @return #table Table of DCS scenery type objects. function ZONE_RADIUS:GetScannedSceneryType( SceneryType ) return self.ScanData.Scenery[SceneryType] end +--- Get scanned scenery table +-- @param #ZONE_RADIUS self +-- @return #table Table of DCS scenery objects. function ZONE_RADIUS:GetScannedScenery() return self.ScanData.Scenery end --- Is All in Zone of Coalition? +-- Check if only the specifed coalition is inside the zone and noone else. -- @param #ZONE_RADIUS self --- @param Coalition --- @return #boolean +-- @param #number Coalition Coalition ID of the coalition which is checked to be the only one in the zone. +-- @return #boolean True, if **only** that coalition is inside the zone and no one else. -- @usage -- self.Zone:Scan() -- local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition ) @@ -820,11 +863,12 @@ end --- Is All in Zone of Other Coalition? +-- Check if only one coalition is inside the zone and the specified coalition is not the one. -- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated! -- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set. -- @param #ZONE_RADIUS self --- @param Coalition --- @return #boolean +-- @param #number Coalition Coalition ID of the coalition which is not supposed to be in the zone. +-- @return #boolean True, if and only if only one coalition is inside the zone and the specified coalition is not it. -- @usage -- self.Zone:Scan() -- local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition ) @@ -836,11 +880,12 @@ end --- Is Some in Zone of Coalition? +-- Check if more than one coaltion is inside the zone and the specifed coalition is one of them. -- You first need to use the @{#ZONE_RADIUS.Scan} method to scan the zone before it can be evaluated! -- Note that once a zone has been scanned, multiple evaluations can be done on the scan result set. -- @param #ZONE_RADIUS self --- @param Coalition --- @return #boolean +-- @param #number Coalition ID of the coaliton which is checked to be inside the zone. +-- @return #boolean True if more than one coalition is inside the zone and the specified coalition is one of them. -- @usage -- self.Zone:Scan() -- local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition ) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 21af54d2e..7492a5d4d 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -550,7 +550,7 @@ RAT.id="RAT | " --- RAT version. -- @list version RAT.version={ - version = "2.3.8", + version = "2.3.9", print = true, } @@ -5173,6 +5173,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take if self.uncontrolled then -- This is used in the SPAWN:SpawnWithIndex() function. Some values are overwritten there! self.SpawnUnControlled=true + SpawnTemplate.uncontrolled=true end -- Number of units in the group. With grouping this can actually differ from the template group size! diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index d3fc4d1aa..d181ebbec 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -516,7 +516,7 @@ RANGE.MenuF10Root=nil --- Range script version. -- @field #string version -RANGE.version="2.2.0" +RANGE.version="2.2.1" --TODO list: --TODO: Verbosity level for messages. @@ -2521,10 +2521,17 @@ function RANGE:_DisplayBombTargets(_unitname) local coord=self:_GetBombTargetCoordinate(bombtarget) if coord then + + -- Get elevation + local elevation=coord:GetLandHeight() + local eltxt=string.format("%d m", elevation) + if _settings:IsImperial() then + elevation=UTILS.MetersToFeet(elevation) + eltxt=string.format("%d ft", elevation) + end local ca2g=coord:ToStringA2G(_unit,_settings) - --local lldms=coord:ToStringLLDMS(_settings) - _text=_text..string.format("\n- %s:\n%s", bombtarget.name or "unknown", ca2g) + _text=_text..string.format("\n- %s:\n%s @ %s", bombtarget.name or "unknown", ca2g, eltxt) end end diff --git a/Moose Development/Moose/Functional/Suppression.lua b/Moose Development/Moose/Functional/Suppression.lua index 1c8c56092..098efeb20 100644 --- a/Moose Development/Moose/Functional/Suppression.lua +++ b/Moose Development/Moose/Functional/Suppression.lua @@ -868,13 +868,15 @@ end -- @param #boolean message Send message to all players. function SUPPRESSION:StatusReport(message) - local name=self.Controllable:GetName() - local nunits=#self.Controllable:GetUnits() + local group=self.Controllable --Wrapper.Group#GROUP + + local nunits=group:CountAliveUnits() local roe=self.CurrentROE local state=self.CurrentAlarmState local life_min, life_max, life_ave, life_ave0, groupstrength=self:_GetLife() - local at=self.Controllable:GetAmmunition() + local ammotot=self.Controllable:GetAmmunition() + --[[ local text=string.format("Status of group %s\n", name) text=text..string.format("Number of units: %d of %d\n", nunits, self.IniGroupStrength) text=text..string.format("Current state: %s\n", self:GetState()) @@ -887,6 +889,10 @@ function SUPPRESSION:StatusReport(message) text=text..string.format("Life ave0: %3.0f\n", life_ave0) text=text..string.format("Ammo tot: %d\n", at) text=text..string.format("Group strength: %3.0f", groupstrength) + ]] + + local text=string.format("State %s, Units=%d/%d, ROE=%s Alarm State=%s, Hits=%d, Life=%d/%d/%d/%d, Ammo=%d", + self:GetState(), nunits, self.IniGroupStrength, self.CurrentROE, self.CurrentAlarmState, self.Nhit, life_min, life_max, life_ave, life_ave0, ammotot) MESSAGE:New(text, 10):ToAllIf(message or self.Debug) self:I(self.lid.."\n"..text) diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index 4ee91c93f..81c2aefd1 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -39,7 +39,7 @@ -- === -- -- ### Author: **FlightControl** --- ### Contributions: **Millertime** - Concept +-- ### Contributions: **Millertime** - Concept, **funkyfranky** -- -- === -- @@ -49,6 +49,15 @@ do -- ZONE_CAPTURE_COALITION --- @type ZONE_CAPTURE_COALITION + -- @field #string ClassName Name of the class. + -- @field #number MarkBlue ID of blue F10 mark. + -- @field #number MarkRed ID of red F10 mark. + -- @field #number StartInterval Time in seconds after the status monitor is started. + -- @field #number RepeatInterval Time in seconds after which the zone status is updated. + -- @field #boolean HitsOn If true, hit events are monitored and trigger the "Attack" event when a defending unit is hit. + -- @field #number HitTimeLast Time stamp in seconds when the last unit inside the zone was hit. + -- @field #number HitTimeAttackOver Time interval in seconds before the zone goes from "Attacked" to "Guarded" state after the last hit. + -- @field #boolean MarkOn If true, create marks of zone status on F10 map. -- @extends Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION @@ -197,8 +206,7 @@ do -- ZONE_CAPTURE_COALITION -- -- ### IMPORTANT -- - -- **Each capture zone object must have the monitoring process started specifically. - -- The monitoring process is NOT started by default!!!** + -- **Each capture zone object must have the monitoring process started specifically. The monitoring process is NOT started by default!** -- -- -- # Full Example @@ -338,29 +346,48 @@ do -- ZONE_CAPTURE_COALITION -- -- @field #ZONE_CAPTURE_COALITION ZONE_CAPTURE_COALITION = { - ClassName = "ZONE_CAPTURE_COALITION", + ClassName = "ZONE_CAPTURE_COALITION", + MarkBlue = nil, + MarkRed = nil, + StartInterval = nil, + RepeatInterval = nil, + HitsOn = nil, + HitTimeLast = nil, + HitTimeAttackOver = nil, + MarkOn = nil, } - - --- @field #table ZONE_CAPTURE_COALITION.States - ZONE_CAPTURE_COALITION.States = {} - + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor and Start/Stop Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- ZONE_CAPTURE_COALITION Constructor. -- @param #ZONE_CAPTURE_COALITION self -- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved. -- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. + -- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}. + -- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS. -- @return #ZONE_CAPTURE_COALITION -- @usage -- -- AttackZone = ZONE:New( "AttackZone" ) -- - -- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition. + -- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED, {UNITS ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition. -- ZoneCaptureCoalition:__Guard( 1 ) -- Start the Guarding of the AttackZone. -- - function ZONE_CAPTURE_COALITION:New( Zone, Coalition, UnitCategories ) + function ZONE_CAPTURE_COALITION:New( Zone, Coalition, UnitCategories, ObjectCategories ) local self = BASE:Inherit( self, ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories ) ) -- #ZONE_CAPTURE_COALITION - - self:F( { Zone = Zone, Coalition = Coalition, UnitCategories = UnitCategories } ) + self:F( { Zone = Zone, Coalition = Coalition, UnitCategories = UnitCategories, ObjectCategories = ObjectCategories } ) + + self:SetObjectCategories(ObjectCategories) + + -- Default is no smoke. + self:SetSmokeZone(false) + -- Default is F10 marks ON. + self:SetMarkZone(true) + -- Start in state "Empty". + self:SetStartState("Empty") do @@ -545,184 +572,12 @@ do -- ZONE_CAPTURE_COALITION -- @param #ZONE_CAPTURE_COALITION self -- @param #number Delay - -- We check if a unit within the zone is hit. - -- If it is, then we must move the zone to attack state. - self:HandleEvent( EVENTS.Hit, self.OnEventHit ) - -- ZoneGoal objects are added to the _DATABASE.ZONES_GOAL and SET_ZONE_GOAL sets. - _EVENTDISPATCHER:CreateEventNewZoneGoal( self ) + _EVENTDISPATCHER:CreateEventNewZoneGoal(self) return self end - - --- @param #ZONE_CAPTURE_COALITION self - function ZONE_CAPTURE_COALITION:onenterCaptured() - - self:F({"hello"}) - - self:GetParent( self, ZONE_CAPTURE_COALITION ).onenterCaptured( self ) - - self.Goal:Achieved() - end - - - function ZONE_CAPTURE_COALITION:IsGuarded() - - local IsGuarded = self:IsAllInZoneOfCoalition( self.Coalition ) - self:F( { IsGuarded = IsGuarded } ) - return IsGuarded - end - - - function ZONE_CAPTURE_COALITION:IsEmpty() - - local IsEmpty = self:IsNoneInZone() - self:F( { IsEmpty = IsEmpty } ) - return IsEmpty - end - - - function ZONE_CAPTURE_COALITION:IsCaptured() - - local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition ) - self:F( { IsCaptured = IsCaptured } ) - return IsCaptured - end - - - function ZONE_CAPTURE_COALITION:IsAttacked() - - local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition ) - self:F( { IsAttacked = IsAttacked } ) - return IsAttacked - end - - - - --- Mark. - -- @param #ZONE_CAPTURE_COALITION self - function ZONE_CAPTURE_COALITION:Mark() - - local Coord = self:GetCoordinate() - local ZoneName = self:GetZoneName() - local State = self:GetState() - - if self.MarkRed and self.MarkBlue then - self:F( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } ) - Coord:RemoveMark( self.MarkRed ) - Coord:RemoveMark( self.MarkBlue ) - end - - if self.Coalition == coalition.side.BLUE then - self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State ) - self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State ) - else - self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State ) - self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State ) - end - end - - --- Bound. - -- @param #ZONE_CAPTURE_COALITION self - function ZONE_CAPTURE_COALITION:onenterGuarded() - - --self:GetParent( self ):onenterGuarded() - - if self.Coalition == coalition.side.BLUE then - --elf.ProtectZone:BoundZone( 12, country.id.USA ) - else - --self.ProtectZone:BoundZone( 12, country.id.RUSSIA ) - end - - self:Mark() - - end - - function ZONE_CAPTURE_COALITION:onenterCaptured() - - --self:GetParent( self ):onenterCaptured() - - local NewCoalition = self:GetScannedCoalition() - self:F( { NewCoalition = NewCoalition } ) - self:SetCoalition( NewCoalition ) - - self:Mark() - self.Goal:Achieved() - end - - - function ZONE_CAPTURE_COALITION:onenterEmpty() - - --self:GetParent( self ):onenterEmpty() - - self:Mark() - end - - - function ZONE_CAPTURE_COALITION:onenterAttacked() - - --self:GetParent( self ):onenterAttacked() - - self:Mark() - end - - - --- When started, check the Coalition status. - -- @param #ZONE_CAPTURE_COALITION self - function ZONE_CAPTURE_COALITION:onafterGuard() - - --self:F({BASE:GetParent( self )}) - --BASE:GetParent( self ).onafterGuard( self ) - - if not self.SmokeScheduler then - self.SmokeScheduler = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusSmoke, self ) - end - end - - - function ZONE_CAPTURE_COALITION:IsCaptured() - - local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition ) - self:F( { IsCaptured = IsCaptured } ) - return IsCaptured - end - - - function ZONE_CAPTURE_COALITION:IsAttacked() - - local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition ) - self:F( { IsAttacked = IsAttacked } ) - return IsAttacked - end - - - --- Check status Coalition ownership. - -- @param #ZONE_CAPTURE_COALITION self - function ZONE_CAPTURE_COALITION:StatusZone() - - local State = self:GetState() - self:F( { State = self:GetState() } ) - - self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self ) - - if State ~= "Guarded" and self:IsGuarded() then - self:Guard() - end - - if State ~= "Empty" and self:IsEmpty() then - self:Empty() - end - - if State ~= "Attacked" and self:IsAttacked() then - self:Attack() - end - - if State ~= "Captured" and self:IsCaptured() then - self:Capture() - end - - end --- Starts the zone capturing monitoring process. -- This process can be CPU intensive, ensure that you specify reasonable time intervals for the monitoring process. @@ -733,6 +588,7 @@ do -- ZONE_CAPTURE_COALITION -- @param #ZONE_CAPTURE_COALITION self -- @param #number StartInterval (optional) Specifies the start time interval in seconds when the zone state will be checked for the first time. -- @param #number RepeatInterval (optional) Specifies the repeat time interval in seconds when the zone state will be checked repeatedly. + -- @return #ZONE_CAPTURE_COALITION self -- @usage -- -- -- Setup the zone. @@ -747,13 +603,23 @@ do -- ZONE_CAPTURE_COALITION -- function ZONE_CAPTURE_COALITION:Start( StartInterval, RepeatInterval ) - self.StartInterval = StartInterval or 15 + self.StartInterval = StartInterval or 1 self.RepeatInterval = RepeatInterval or 15 if self.ScheduleStatusZone then self:ScheduleStop( self.ScheduleStatusZone ) end - self.ScheduleStatusZone = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 1.5, nil, self.StatusZone, self ) + + -- Start Status scheduler. + self.ScheduleStatusZone = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusZone, self ) + + -- We check if a unit within the zone is hit. If it is, then we must move the zone to attack state. + self:HandleEvent(EVENTS.Hit, self.OnEventHit) + + -- Create mark on F10 map. + self:Mark() + + return self end @@ -795,24 +661,266 @@ do -- ZONE_CAPTURE_COALITION function ZONE_CAPTURE_COALITION:Stop() if self.ScheduleStatusZone then - self:ScheduleStop( self.ScheduleStatusZone ) + self:ScheduleStop(self.ScheduleStatusZone) end + + if self.SmokeScheduler then + self:ScheduleStop(self.SmokeScheduler) + end + + self:UnHandleEvent(EVENTS.Hit) + end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User API Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + --- Set whether hit events of defending units are monitored and trigger "Attack" events. + -- @param #ZONE_CAPTURE_COALITION self + -- @param #boolean Switch If *true*, hit events are monitored. If *false* or *nil*, hit events are not monitored. + -- @param #number TimeAttackOver (Optional) Time in seconds after an attack is over after the last hit and the zone state goes to "Guarded". Default is 300 sec = 5 min. + -- @return #ZONE_CAPTURE_COALITION self + function ZONE_CAPTURE_COALITION:SetMonitorHits(Switch, TimeAttackOver) + self.HitsOn=Switch + self.HitTimeAttackOver=TimeAttackOver or 5*60 + return self + end + + --- Set whether marks on the F10 map are shown, which display the current zone status. + -- @param #ZONE_CAPTURE_COALITION self + -- @param #boolean Switch If *true* or *nil*, marks are shown. If *false*, marks are not displayed. + -- @return #ZONE_CAPTURE_COALITION self + function ZONE_CAPTURE_COALITION:SetMarkZone(Switch) + if Switch==nil or Switch==true then + self.MarkOn=true + else + self.MarkOn=false + end + return self + end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- DCS Event Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + - --- @param #ZONE_CAPTURE_COALITION self + --- Monitor hit events. + -- @param #ZONE_CAPTURE_COALITION self -- @param Core.Event#EVENTDATA EventData The event data. function ZONE_CAPTURE_COALITION:OnEventHit( EventData ) - local UnitHit = EventData.TgtUnit - - if UnitHit then - if UnitHit:IsInZone( self ) then - self:Attack() + if self.HitsOn then + + local UnitHit = EventData.TgtUnit + + -- Check if unit is inside the capture zone and that it is of the defending coalition. + if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then + + -- Update last hit time. + self.HitTimeLast=timer.getTime() + + -- Only trigger attacked event if not already in state "Attacked". + if self:GetState()~="Attacked" then + self:F2("Hit ==> Attack") + self:Attack() + end + end + end end - - -end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- FSM Event Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + --- On after "Guard" event. + -- @param #ZONE_CAPTURE_COALITION self + function ZONE_CAPTURE_COALITION:onafterGuard() + self:F2("After Guard") + + if self.SmokeZone and not self.SmokeScheduler then + self.SmokeScheduler = self:ScheduleRepeat( self.StartInterval, self.RepeatInterval, 0.1, nil, self.StatusSmoke, self ) + end + + end + + --- On enter "Guarded" state. + -- @param #ZONE_CAPTURE_COALITION self + function ZONE_CAPTURE_COALITION:onenterGuarded() + self:F2("Enter Guarded") + self:Mark() + end + + --- On enter "Captured" state. + -- @param #ZONE_CAPTURE_COALITION self + function ZONE_CAPTURE_COALITION:onenterCaptured() + self:F2("Enter Captured") + + -- Get new coalition. + local NewCoalition = self:GetScannedCoalition() + self:F( { NewCoalition = NewCoalition } ) + + -- Set new owner of zone. + self:SetCoalition(NewCoalition) + + -- Update mark. + self:Mark() + + -- Goal achieved. + self.Goal:Achieved() + end + + --- On enter "Empty" state. + -- @param #ZONE_CAPTURE_COALITION self + function ZONE_CAPTURE_COALITION:onenterEmpty() + self:F2("Enter Empty") + self:Mark() + end + + --- On enter "Attacked" state. + -- @param #ZONE_CAPTURE_COALITION self + function ZONE_CAPTURE_COALITION:onenterAttacked() + self:F2("Enter Attacked") + self:Mark() + end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Status Check Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + --- Check if zone is "Empty". + -- @param #ZONE_CAPTURE_COALITION self + -- @return #boolean self:IsNoneInZone() + function ZONE_CAPTURE_COALITION:IsEmpty() + + local IsEmpty = self:IsNoneInZone() + self:F( { IsEmpty = IsEmpty } ) + + return IsEmpty + end + + --- Check if zone is "Guarded", i.e. only one (the defending) coaliton is present inside the zone. + -- @param #ZONE_CAPTURE_COALITION self + -- @return #boolean self:IsAllInZoneOfCoalition( self.Coalition ) + function ZONE_CAPTURE_COALITION:IsGuarded() + + local IsGuarded = self:IsAllInZoneOfCoalition( self.Coalition ) + self:F( { IsGuarded = IsGuarded } ) + + return IsGuarded + end + + --- Check if zone is "Captured", i.e. another coalition took control over the zone and is the only one present. + -- @param #ZONE_CAPTURE_COALITION self + -- @return #boolean self:IsAllInZoneOfOtherCoalition( self.Coalition ) + function ZONE_CAPTURE_COALITION:IsCaptured() + + local IsCaptured = self:IsAllInZoneOfOtherCoalition( self.Coalition ) + self:F( { IsCaptured = IsCaptured } ) + + return IsCaptured + end + + --- Check if zone is "Attacked", i.e. another coaliton entered the zone. + -- @param #ZONE_CAPTURE_COALITION self + -- @return #boolean self:IsSomeInZoneOfCoalition( self.Coalition ) + function ZONE_CAPTURE_COALITION:IsAttacked() + + local IsAttacked = self:IsSomeInZoneOfCoalition( self.Coalition ) + self:F( { IsAttacked = IsAttacked } ) + + return IsAttacked + end + + + --- Check status Coalition ownership. + -- @param #ZONE_CAPTURE_COALITION self + function ZONE_CAPTURE_COALITION:StatusZone() + + -- Get FSM state. + local State = self:GetState() + + -- Scan zone in parent class ZONE_GOAL_COALITION + self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self ) + + local Tnow=timer.getTime() + + -- Check if zone is guarded. + if State ~= "Guarded" and self:IsGuarded() then + + -- Check that there was a sufficient amount of time after the last hit before going back to "Guarded". + if self.HitTimeLast==nil or Tnow>=self.HitTimeLast+self.HitTimeAttackOver then + self:Guard() + self.HitTimeLast=nil + end + end + + -- Check if zone is empty. + if State ~= "Empty" and self:IsEmpty() then + self:Empty() + end + + -- Check if zone is attacked. + if State ~= "Attacked" and self:IsAttacked() then + self:Attack() + end + + -- Check if zone is captured. + if State ~= "Captured" and self:IsCaptured() then + self:Capture() + end + + -- Status text. + local text=string.format("CAPTURE ZONE %s: Owner=%s (Previous=%s): Status %s", self:GetZoneName(), self:GetCoalitionName(), UTILS.GetCoalitionName(self:GetPreviousCoalition()), State) + local NewState = self:GetState() + if NewState~=State then + text=text..string.format(" --> %s", NewState) + end + self:I(text) + + end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Misc Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + + --- Update Mark on F10 map. + -- @param #ZONE_CAPTURE_COALITION self + function ZONE_CAPTURE_COALITION:Mark() + + if self.MarkOn then + + local Coord = self:GetCoordinate() + local ZoneName = self:GetZoneName() + local State = self:GetState() + + -- Remove marks. + if self.MarkRed then + Coord:RemoveMark(self.MarkRed) + end + if self.MarkBlue then + Coord:RemoveMark(self.MarkBlue) + end + + -- Create new marks for each coaliton. + if self.Coalition == coalition.side.BLUE then + self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State ) + self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State ) + elseif self.Coalition == coalition.side.RED then + self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State ) + self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State ) + else + self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State ) + self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Neutral\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State ) + end + + end + + end + +end diff --git a/Moose Development/Moose/Functional/ZoneGoal.lua b/Moose Development/Moose/Functional/ZoneGoal.lua index 67f6a872b..cd55b2603 100644 --- a/Moose Development/Moose/Functional/ZoneGoal.lua +++ b/Moose Development/Moose/Functional/ZoneGoal.lua @@ -8,6 +8,7 @@ -- === -- -- ### Author: **FlightControl** +-- ### Contributions: **funkyfranky** -- -- === -- @@ -17,10 +18,16 @@ do -- Zone --- @type ZONE_GOAL - -- @extends Core.Fsm#FSM + -- @field #string ClassName Name of the class. + -- @field Core.Goal#GOAL Goal The goal object. + -- @field #number SmokeTime Time stamp in seconds when the last smoke of the zone was triggered. + -- @field Core.Scheduler#SCHEDULER SmokeScheduler Scheduler responsible for smoking the zone. + -- @field #number SmokeColor Color of the smoke. + -- @field #boolean SmokeZone If true, smoke zone. + -- @extends Core.Zone#ZONE_RADIUS - -- Models processes that have a Goal with a defined achievement involving a Zone. + --- Models processes that have a Goal with a defined achievement involving a Zone. -- Derived classes implement the ways how the achievements can be realized. -- -- ## 1. ZONE_GOAL constructor @@ -39,7 +46,12 @@ do -- Zone -- -- @field #ZONE_GOAL ZONE_GOAL = { - ClassName = "ZONE_GOAL", + ClassName = "ZONE_GOAL", + Goal = nil, + SmokeTime = nil, + SmokeScheduler = nil, + SmokeColor = nil, + SmokeZone = nil, } --- ZONE_GOAL Constructor. @@ -51,11 +63,24 @@ do -- Zone local self = BASE:Inherit( self, ZONE_RADIUS:New( Zone:GetName(), Zone:GetVec2(), Zone:GetRadius() ) ) -- #ZONE_GOAL self:F( { Zone = Zone } ) + -- Goal object. self.Goal = GOAL:New() self.SmokeTime = nil + + -- Set smoke ON. + self:SetSmokeZone(true) self:AddTransition( "*", "DestroyedUnit", "*" ) + + --- DestroyedUnit event. + -- @function [parent=#ZONE_GOAL] DestroyedUnit + -- @param #ZONE_GOAL self + + --- DestroyedUnit delayed event + -- @function [parent=#ZONE_GOAL] __DestroyedUnit + -- @param #ZONE_GOAL self + -- @param #number delay Delay in seconds. --- DestroyedUnit Handler OnAfter for ZONE_GOAL -- @function [parent=#ZONE_GOAL] OnAfterDestroyedUnit @@ -66,19 +91,18 @@ do -- Zone -- @param Wrapper.Unit#UNIT DestroyedUnit The destroyed unit. -- @param #string PlayerName The name of the player. - return self end - --- Get the Zone + --- Get the Zone. -- @param #ZONE_GOAL self - -- @return Core.Zone#ZONE_BASE + -- @return #ZONE_GOAL function ZONE_GOAL:GetZone() return self end - --- Get the name of the ProtectZone + --- Get the name of the Zone. -- @param #ZONE_GOAL self -- @return #string function ZONE_GOAL:GetZoneName() @@ -86,36 +110,48 @@ do -- Zone end - --- Smoke the center of theh zone. + --- Activate smoking of zone with the color or the current owner. -- @param #ZONE_GOAL self - -- @param #SMOKECOLOR.Color SmokeColor - function ZONE_GOAL:Smoke( SmokeColor ) - + -- @param #boolean switch If *true* or *nil* activate smoke. If *false* or *nil*, no smoke. + -- @return #ZONE_GOAL + function ZONE_GOAL:SetSmokeZone(switch) + self.SmokeZone=switch + --[[ + if switch==nil or switch==true then + self.SmokeZone=true + else + self.SmokeZone=false + end + ]] + return self + end + + --- Set the smoke color. + -- @param #ZONE_GOAL self + -- @param DCS#SMOKECOLOR.Color SmokeColor + function ZONE_GOAL:Smoke( SmokeColor ) self:F( { SmokeColor = SmokeColor} ) self.SmokeColor = SmokeColor end - --- Flare the center of the zone. + --- Flare the zone boundary. -- @param #ZONE_GOAL self - -- @param #SMOKECOLOR.Color FlareColor + -- @param DCS#SMOKECOLOR.Color FlareColor function ZONE_GOAL:Flare( FlareColor ) - self:FlareZone( FlareColor, math.random( 1, 360 ) ) + self:FlareZone( FlareColor, 30) end --- When started, check the Smoke and the Zone status. -- @param #ZONE_GOAL self function ZONE_GOAL:onafterGuard() - - --self:GetParent( self ):onafterStart() - self:F("Guard") - - --self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self ) - if not self.SmokeScheduler then - self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self ) + + -- Start smoke + if self.SmokeZone and not self.SmokeScheduler then + self.SmokeScheduler = self:ScheduleRepeat(1, 1, 0.1, nil, self.StatusSmoke, self) end end @@ -123,42 +159,54 @@ do -- Zone --- Check status Smoke. -- @param #ZONE_GOAL self function ZONE_GOAL:StatusSmoke() - self:F({self.SmokeTime, self.SmokeColor}) - local CurrentTime = timer.getTime() - - if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then - if self.SmokeColor then - self:GetCoordinate():Smoke( self.SmokeColor ) - --self.SmokeColor = nil - self.SmokeTime = CurrentTime + if self.SmokeZone then + + -- Current time. + local CurrentTime = timer.getTime() + + -- Restart smoke every 5 min. + if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then + if self.SmokeColor then + self:GetCoordinate():Smoke( self.SmokeColor ) + self.SmokeTime = CurrentTime + end end + end + end --- @param #ZONE_GOAL self - -- @param Core.Event#EVENTDATA EventData + -- @param Core.Event#EVENTDATA EventData Event data table. function ZONE_GOAL:__Destroyed( EventData ) self:F( { "EventDead", EventData } ) self:F( { EventData.IniUnit } ) - local Vec3 = EventData.IniDCSUnit:getPosition().p - self:F( { Vec3 = Vec3 } ) - if EventData.IniDCSUnit then - if self:IsVec3InZone(Vec3) then + + local Vec3 = EventData.IniDCSUnit:getPosition().p + self:F( { Vec3 = Vec3 } ) + + if Vec3 and self:IsVec3InZone(Vec3) then + local PlayerHits = _DATABASE.HITS[EventData.IniUnitName] + if PlayerHits then + for PlayerName, PlayerHit in pairs( PlayerHits.Players or {} ) do self.Goal:AddPlayerContribution( PlayerName ) self:DestroyedUnit( EventData.IniUnitName, PlayerName ) end + end + end end + end diff --git a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua index 1de5218f3..6296b4835 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua @@ -17,6 +17,11 @@ do -- ZoneGoal --- @type ZONE_GOAL_COALITION + -- @field #string ClassName Name of the Class. + -- @field #number Coalition The current coalition ID of the zone owner. + -- @field #number PreviousCoalition The previous owner of the zone. + -- @field #table UnitCategories Table of unit categories that are able to capture and hold the zone. Default is only GROUND units. + -- @field #table ObjectCategories Table of object categories that are able to hold a zone. Default is UNITS and STATICS. -- @extends Functional.ZoneGoal#ZONE_GOAL @@ -37,7 +42,11 @@ do -- ZoneGoal -- -- @field #ZONE_GOAL_COALITION ZONE_GOAL_COALITION = { - ClassName = "ZONE_GOAL_COALITION", + ClassName = "ZONE_GOAL_COALITION", + Coalition = nil, + PreviousCoaliton = nil, + UnitCategories = nil, + ObjectCategories = nil, } --- @field #table ZONE_GOAL_COALITION.States @@ -46,27 +55,70 @@ do -- ZoneGoal --- ZONE_GOAL_COALITION Constructor. -- @param #ZONE_GOAL_COALITION self -- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved. - -- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. + -- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone. Default coalition.side.NEUTRAL. + -- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}. -- @return #ZONE_GOAL_COALITION - function ZONE_GOAL_COALITION:New( Zone, Coalition ) + function ZONE_GOAL_COALITION:New( Zone, Coalition, UnitCategories ) + if not Zone then + BASE:E("ERROR: No Zone specified in ZONE_GOAL_COALITON!") + return nil + end + + -- Inherit ZONE_GOAL. local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION self:F( { Zone = Zone, Coalition = Coalition } ) - self:SetCoalition( Coalition ) - - + -- Set initial owner. + self:SetCoalition( Coalition or coalition.side.NEUTRAL) + + -- Set default unit and object categories for the zone scan. + self:SetUnitCategories(UnitCategories) + self:SetObjectCategories() + return self end --- Set the owning coalition of the zone. -- @param #ZONE_GOAL_COALITION self - -- @param DCSCoalition.DCSCoalition#coalition Coalition + -- @param DCSCoalition.DCSCoalition#coalition Coalition The coalition ID, e.g. *coalition.side.RED*. + -- @return #ZONE_GOAL_COALITION function ZONE_GOAL_COALITION:SetCoalition( Coalition ) + self.PreviousCoalition=self.Coalition or Coalition self.Coalition = Coalition + return self + end + + --- Set the owning coalition of the zone. + -- @param #ZONE_GOAL_COALITION self + -- @param #table UnitCategories Table of unit categories. See [DCS Class Unit](https://wiki.hoggitworld.com/view/DCS_Class_Unit). Default {Unit.Category.GROUND_UNIT}. + -- @return #ZONE_GOAL_COALITION + function ZONE_GOAL_COALITION:SetUnitCategories( UnitCategories ) + + if UnitCategories and type(UnitCategories)~="table" then + UnitCategories={UnitCategories} + end + + self.UnitCategories=UnitCategories or {Unit.Category.GROUND_UNIT} + + return self end + --- Set the owning coalition of the zone. + -- @param #ZONE_GOAL_COALITION self + -- @param #table ObjectCategories Table of unit categories. See [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object). Default {Object.Category.UNIT, Object.Category.STATIC}, i.e. all UNITS and STATICS. + -- @return #ZONE_GOAL_COALITION + function ZONE_GOAL_COALITION:SetObjectCategories( ObjectCategories ) + + if ObjectCategories and type(ObjectCategories)~="table" then + ObjectCategories={ObjectCategories} + end + + self.ObjectCategories=ObjectCategories or {Object.Category.UNIT, Object.Category.STATIC} + + return self + end --- Get the owning coalition of the zone. -- @param #ZONE_GOAL_COALITION self @@ -75,37 +127,38 @@ do -- ZoneGoal return self.Coalition end + --- Get the previous coaliton, i.e. the one owning the zone before the current one. + -- @param #ZONE_GOAL_COALITION self + -- @return DCSCoalition.DCSCoalition#coalition Coalition. + function ZONE_GOAL_COALITION:GetPreviousCoalition() + return self.PreviousCoalition + end + --- Get the owning coalition name of the zone. -- @param #ZONE_GOAL_COALITION self -- @return #string Coalition name. function ZONE_GOAL_COALITION:GetCoalitionName() - - if self.Coalition == coalition.side.BLUE then - return "Blue" - end - - if self.Coalition == coalition.side.RED then - return "Red" - end - - if self.Coalition == coalition.side.NEUTRAL then - return "Neutral" - end - - return "" + return UTILS.GetCoalitionName(self.Coalition) end --- Check status Coalition ownership. -- @param #ZONE_GOAL_COALITION self + -- @return #ZONE_GOAL_COALITION function ZONE_GOAL_COALITION:StatusZone() + -- Get current state. local State = self:GetState() - self:F( { State = self:GetState() } ) - self:Scan( { Object.Category.UNIT, Object.Category.STATIC } ) + -- Debug text. + local text=string.format("Zone state=%s, Owner=%s, Scanning...", State, self:GetCoalitionName()) + self:F(text) + + -- Scan zone. + self:Scan( self.ObjectCategories, self.UnitCategories ) + return self end end diff --git a/Moose Development/Moose/Ops/RecoveryTanker.lua b/Moose Development/Moose/Ops/RecoveryTanker.lua index dae5f2ab4..b779f8efc 100644 --- a/Moose Development/Moose/Ops/RecoveryTanker.lua +++ b/Moose Development/Moose/Ops/RecoveryTanker.lua @@ -62,6 +62,7 @@ -- @field #string modex Tail number of the tanker. -- @field #boolean eplrs If true, enable data link, e.g. if used as AWACS. -- @field #boolean recovery If true, tanker will recover using the AIRBOSS marshal pattern. +-- @field #number terminaltype Terminal type of used parking spots on airbases. -- @extends Core.Fsm#FSM --- Recovery Tanker. @@ -298,6 +299,7 @@ RECOVERYTANKER = { modex = nil, eplrs = nil, recovery = nil, + terminaltype = nil, } --- Unique ID (global). @@ -306,7 +308,7 @@ _RECOVERYTANKERID=0 --- Class version. -- @field #string version -RECOVERYTANKER.version="1.0.8" +RECOVERYTANKER.version="1.0.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -381,6 +383,7 @@ function RECOVERYTANKER:New(carrierunit, tankergroupname) self:SetPatternUpdateInterval() self:SetAWACS(false) self:SetRecoveryAirboss(false) + self.terminaltype=AIRBASE.TerminalType.OpenMedOrBig -- Debug trace. if false then @@ -615,8 +618,9 @@ end --- Set home airbase of the tanker. This is the airbase where the tanker will go when it is out of fuel. -- @param #RECOVERYTANKER self -- @param Wrapper.Airbase#AIRBASE airbase The home airbase. Can be the airbase name or a Moose AIRBASE object. +-- @param #number terminaltype (Optional) The terminal type of parking spots used for spawning at airbases. Default AIRBASE.TerminalType.OpenMedOrBig. -- @return #RECOVERYTANKER self -function RECOVERYTANKER:SetHomeBase(airbase) +function RECOVERYTANKER:SetHomeBase(airbase, terminaltype) if type(airbase)=="string" then self.airbase=AIRBASE:FindByName(airbase) else @@ -625,6 +629,9 @@ function RECOVERYTANKER:SetHomeBase(airbase) if not self.airbase then self:E(self.lid.."ERROR: Airbase is nil!") end + if termialtype then + self.terminaltype=terminaltype + end return self end @@ -937,7 +944,7 @@ function RECOVERYTANKER:onafterStart(From, Event, To) else -- Spawn tanker at airbase. - self.tanker=Spawn:SpawnAtAirbase(self.airbase, self.takeoff, nil, AIRBASE.TerminalType.OpenMedOrBig) + self.tanker=Spawn:SpawnAtAirbase(self.airbase, self.takeoff, nil, self.terminaltype) end diff --git a/Moose Development/Moose/Ops/RescueHelo.lua b/Moose Development/Moose/Ops/RescueHelo.lua index b3b0fc69f..8e979ccf9 100644 --- a/Moose Development/Moose/Ops/RescueHelo.lua +++ b/Moose Development/Moose/Ops/RescueHelo.lua @@ -237,7 +237,7 @@ _RESCUEHELOID=0 --- Class version. -- @field #string version -RESCUEHELO.version="1.0.9" +RESCUEHELO.version="1.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -902,7 +902,7 @@ function RESCUEHELO:onafterStart(From, Event, To) else -- Check if an uncontrolled helo group was requested. - if self.useuncontrolled then + if self.uncontrolledac then -- Use an uncontrolled aircraft group. self.helo=GROUP:FindByName(self.helogroupname) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 683252718..acfc30323 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -1037,3 +1037,26 @@ function UTILS.CheckMemory(output) end return mem end + + +--- Get the coalition name from its numerical ID, e.g. coaliton.side.RED. +-- @param #number Coalition The coalition ID. +-- @return #string The coalition name, i.e. "Neutral", "Red" or "Blue" (or "Unknown"). +function UTILS.GetCoalitionName(Coalition) + + if Coalition then + if Coalition==coalition.side.BLUE then + return "Blue" + elseif Coalition==coalition.side.RED then + return "Red" + elseif Coalition==coalition.side.NEUTRAL then + return "Neutral" + else + return "Unknown" + end + else + return "Unknown" + end + +end + diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 13a5b3234..7943fe049 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -168,20 +168,13 @@ function IDENTIFIABLE:GetCoalitionName() local DCSIdentifiable = self:GetDCSObject() if DCSIdentifiable then + + -- Get coaliton ID. local IdentifiableCoalition = DCSIdentifiable:getCoalition() self:T3( IdentifiableCoalition ) - if IdentifiableCoalition == coalition.side.BLUE then - return "Blue" - end + return UTILS.GetCoalitionName(IdentifiableCoalition) - if IdentifiableCoalition == coalition.side.RED then - return "Red" - end - - if IdentifiableCoalition == coalition.side.NEUTRAL then - return "Neutral" - end end self:F( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )