Merge pull request #1627 from FlightControl-Master/FF/Ops

Ff/ops
This commit is contained in:
Frank
2021-11-14 23:31:03 +01:00
committed by GitHub
42 changed files with 24481 additions and 5959 deletions
+20 -12
View File
@@ -234,7 +234,8 @@ FORMATION = {
-- @param #BASE self -- @param #BASE self
-- @return #BASE -- @return #BASE
function BASE:New() function BASE:New()
local self = routines.utils.deepCopy( self ) -- Create a new self instance --local self = routines.utils.deepCopy( self ) -- Create a new self instance
local self = UTILS.DeepCopy(self)
_ClassID = _ClassID + 1 _ClassID = _ClassID + 1
self.ClassID = _ClassID self.ClassID = _ClassID
@@ -873,20 +874,22 @@ do -- Scheduling
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called. -- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. -- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. -- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @return #number The ScheduleID of the planned schedule. -- @return #string The Schedule ID of the planned schedule.
function BASE:ScheduleOnce( Start, SchedulerFunction, ... ) function BASE:ScheduleOnce( Start, SchedulerFunction, ... )
self:F2( { Start } )
self:T3( { ... } )
-- Object name.
local ObjectName = "-" local ObjectName = "-"
ObjectName = self.ClassName .. self.ClassID ObjectName = self.ClassName .. self.ClassID
-- Debug info.
self:F3( { "ScheduleOnce: ", ObjectName, Start } ) self:F3( { "ScheduleOnce: ", ObjectName, Start } )
if not self.Scheduler then if not self.Scheduler then
self.Scheduler = SCHEDULER:New( self ) self.Scheduler = SCHEDULER:New( self )
end end
-- FF this was wrong!
--[[
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule( local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
self, self,
SchedulerFunction, SchedulerFunction,
@@ -896,6 +899,10 @@ do -- Scheduling
nil, nil,
nil nil
) )
]]
-- NOTE: MasterObject (first parameter) needs to be nil or it will be the first argument passed to the SchedulerFunction!
local ScheduleID = self.Scheduler:Schedule(nil, SchedulerFunction, {...}, Start, nil, nil, nil)
self._.Schedules[#self._.Schedules+1] = ScheduleID self._.Schedules[#self._.Schedules+1] = ScheduleID
@@ -910,7 +917,7 @@ do -- Scheduling
-- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped. -- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped.
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. -- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. -- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
-- @return #number The ScheduleID of the planned schedule. -- @return #string The Schedule ID of the planned schedule.
function BASE:ScheduleRepeat( Start, Repeat, RandomizeFactor, Stop, SchedulerFunction, ... ) function BASE:ScheduleRepeat( Start, Repeat, RandomizeFactor, Stop, SchedulerFunction, ... )
self:F2( { Start } ) self:F2( { Start } )
self:T3( { ... } ) self:T3( { ... } )
@@ -924,6 +931,7 @@ do -- Scheduling
self.Scheduler = SCHEDULER:New( self ) self.Scheduler = SCHEDULER:New( self )
end end
-- NOTE: MasterObject (first parameter) should(!) be nil as it will be the first argument passed to the SchedulerFunction!s
local ScheduleID = self.Scheduler:Schedule( local ScheduleID = self.Scheduler:Schedule(
self, self,
SchedulerFunction, SchedulerFunction,
@@ -942,13 +950,13 @@ do -- Scheduling
--- Stops the Schedule. --- Stops the Schedule.
-- @param #BASE self -- @param #BASE self
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments. -- @param #string SchedulerID (Optional) Scheduler ID to be stopped. If nil, all pending schedules are stopped.
function BASE:ScheduleStop( SchedulerFunction ) function BASE:ScheduleStop( SchedulerID )
self:F3( { "ScheduleStop:" } ) self:F3( { "ScheduleStop:" } )
if self.Scheduler then if self.Scheduler then
_SCHEDULEDISPATCHER:Stop( self.Scheduler, self._.Schedules[SchedulerFunction] ) --_SCHEDULEDISPATCHER:Stop( self.Scheduler, self._.Schedules[SchedulerFunction] )
_SCHEDULEDISPATCHER:Stop(self.Scheduler, SchedulerID)
end end
end end
@@ -1092,7 +1100,7 @@ end
--- Set tracing for a class --- Set tracing for a class
-- @param #BASE self -- @param #BASE self
-- @param #string Class -- @param #string Class Class name.
function BASE:TraceClass( Class ) function BASE:TraceClass( Class )
_TraceClass[Class] = true _TraceClass[Class] = true
_TraceClassMethod[Class] = {} _TraceClassMethod[Class] = {}
@@ -1101,8 +1109,8 @@ end
--- Set tracing for a specific method of class --- Set tracing for a specific method of class
-- @param #BASE self -- @param #BASE self
-- @param #string Class -- @param #string Class Class name.
-- @param #string Method -- @param #string Method Method.
function BASE:TraceClassMethod( Class, Method ) function BASE:TraceClassMethod( Class, Method )
if not _TraceClassMethod[Class] then if not _TraceClassMethod[Class] then
_TraceClassMethod[Class] = {} _TraceClassMethod[Class] = {}
+41 -10
View File
@@ -860,6 +860,20 @@ function DATABASE:GetGroupTemplateFromUnitName( UnitName )
end end
end end
--- Get group template from unit name.
-- @param #DATABASE self
-- @param #string UnitName Name of the unit.
-- @return #table Group template.
function DATABASE:GetUnitTemplateFromUnitName( UnitName )
if self.Templates.Units[UnitName] then
return self.Templates.Units[UnitName]
else
self:E("ERROR: Unit template does not exist for unit "..tostring(UnitName))
return nil
end
end
--- Get coalition ID from client name. --- Get coalition ID from client name.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string ClientName Name of the Client. -- @param #string ClientName Name of the Client.
@@ -1474,19 +1488,19 @@ function DATABASE:SetPlayerSettings( PlayerName, Settings )
self.PLAYERSETTINGS[PlayerName] = Settings self.PLAYERSETTINGS[PlayerName] = Settings
end end
--- Add a flight group to the data base. --- Add an OPS group (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) to the data base.
-- @param #DATABASE self -- @param #DATABASE self
-- @param Ops.FlightGroup#FLIGHTGROUP flightgroup -- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group added to the DB.
function DATABASE:AddFlightGroup(flightgroup) function DATABASE:AddOpsGroup(opsgroup)
self:T({NewFlightGroup=flightgroup.groupname}) --env.info("Adding OPSGROUP "..tostring(opsgroup.groupname))
self.FLIGHTGROUPS[flightgroup.groupname]=flightgroup self.FLIGHTGROUPS[opsgroup.groupname]=opsgroup
end end
--- Get a flight group from the data base. --- Get an OPS group (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) from the data base.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string groupname Group name of the flight group. Can also be passed as GROUP object. -- @param #string groupname Group name of the group. Can also be passed as GROUP object.
-- @return Ops.FlightGroup#FLIGHTGROUP Flight group object. -- @return Ops.OpsGroup#OPSGROUP OPS group object.
function DATABASE:GetFlightGroup(groupname) function DATABASE:GetOpsGroup(groupname)
-- Get group and group name. -- Get group and group name.
if type(groupname)=="string" then if type(groupname)=="string" then
@@ -1494,6 +1508,23 @@ function DATABASE:GetFlightGroup(groupname)
groupname=groupname:GetName() groupname=groupname:GetName()
end end
--env.info("Getting OPSGROUP "..tostring(groupname))
return self.FLIGHTGROUPS[groupname]
end
--- Find an OPSGROUP (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) in the data base.
-- @param #DATABASE self
-- @param #string groupname Group name of the group. Can also be passed as GROUP object.
-- @return Ops.OpsGroup#OPSGROUP OPS group object.
function DATABASE:FindOpsGroup(groupname)
-- Get group and group name.
if type(groupname)=="string" then
else
groupname=groupname:GetName()
end
--env.info("Getting OPSGROUP "..tostring(groupname))
return self.FLIGHTGROUPS[groupname] return self.FLIGHTGROUPS[groupname]
end end
@@ -1581,7 +1612,7 @@ function DATABASE:_RegisterTemplates()
if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group if obj_type_name ~= "static" and Template and Template.units and type(Template.units) == 'table' then --making sure again- this is a valid group
self:_RegisterGroupTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID) self:_RegisterGroupTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID)
else else
+15 -15
View File
@@ -410,7 +410,7 @@ do -- FSM
Transition.To = To Transition.To = To
-- Debug message. -- Debug message.
self:T2( Transition ) --self:T3( Transition )
self._Transitions[Transition] = Transition self._Transitions[Transition] = Transition
self:_eventmap( self.Events, Transition ) self:_eventmap( self.Events, Transition )
@@ -432,7 +432,7 @@ do -- FSM
-- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM. -- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM.
-- @return Core.Fsm#FSM_PROCESS The SubFSM. -- @return Core.Fsm#FSM_PROCESS The SubFSM.
function FSM:AddProcess( From, Event, Process, ReturnEvents ) function FSM:AddProcess( From, Event, Process, ReturnEvents )
self:T( { From, Event } ) --self:T3( { From, Event } )
local Sub = {} local Sub = {}
Sub.From = From Sub.From = From
@@ -533,7 +533,7 @@ do -- FSM
Process._Scores[State].ScoreText = ScoreText Process._Scores[State].ScoreText = ScoreText
Process._Scores[State].Score = Score Process._Scores[State].Score = Score
self:T( Process._Scores ) --self:T3( Process._Scores )
return Process return Process
end end
@@ -576,7 +576,7 @@ do -- FSM
self[__Event] = self[__Event] or self:_delayed_transition(Event) self[__Event] = self[__Event] or self:_delayed_transition(Event)
-- Debug message. -- Debug message.
self:T2( "Added methods: " .. Event .. ", " .. __Event ) --self:T3( "Added methods: " .. Event .. ", " .. __Event )
Events[Event] = self.Events[Event] or { map = {} } Events[Event] = self.Events[Event] or { map = {} }
self:_add_to_map( Events[Event].map, EventStructure ) self:_add_to_map( Events[Event].map, EventStructure )
@@ -791,7 +791,7 @@ do -- FSM
return function( self, DelaySeconds, ... ) return function( self, DelaySeconds, ... )
-- Debug. -- Debug.
self:T2( "Delayed Event: " .. EventName ) self:T3( "Delayed Event: " .. EventName )
local CallID = 0 local CallID = 0
if DelaySeconds ~= nil then if DelaySeconds ~= nil then
@@ -809,23 +809,23 @@ do -- FSM
self._EventSchedules[EventName] = CallID self._EventSchedules[EventName] = CallID
-- Debug output. -- Debug output.
self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID))) self:T2(string.format("NEGATIVE Event %s delayed by %.3f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
else else
self:T2(string.format("NEGATIVE Event %s delayed by %.1f sec CANCELLED as we already have such an event in the queue.", EventName, DelaySeconds)) self:T2(string.format("NEGATIVE Event %s delayed by %.3f sec CANCELLED as we already have such an event in the queue.", EventName, DelaySeconds))
-- reschedule -- reschedule
end end
else else
CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true ) CallID = self.CallScheduler:Schedule( self, self._handler, { EventName, ... }, DelaySeconds or 1, nil, nil, nil, 4, true )
self:T2(string.format("Event %s delayed by %.1f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID))) self:T2(string.format("Event %s delayed by %.3f sec SCHEDULED with CallID=%s", EventName, DelaySeconds, tostring(CallID)))
end end
else else
error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." ) error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." )
end end
-- Debug. -- Debug.
self:T2( { CallID = CallID } ) --self:T3( { CallID = CallID } )
end end
end end
@@ -846,7 +846,7 @@ do -- FSM
function FSM:_gosub( ParentFrom, ParentEvent ) function FSM:_gosub( ParentFrom, ParentEvent )
local fsmtable = {} local fsmtable = {}
if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then if self.subs[ParentFrom] and self.subs[ParentFrom][ParentEvent] then
self:T( { ParentFrom, ParentEvent, self.subs[ParentFrom], self.subs[ParentFrom][ParentEvent] } ) --self:T3( { ParentFrom, ParentEvent, self.subs[ParentFrom], self.subs[ParentFrom][ParentEvent] } )
return self.subs[ParentFrom][ParentEvent] return self.subs[ParentFrom][ParentEvent]
else else
return {} return {}
@@ -893,7 +893,7 @@ do -- FSM
end end
end end
self:T3( { Map, Event } ) --self:T3( { Map, Event } )
end end
--- Get current state. --- Get current state.
@@ -1150,7 +1150,7 @@ do -- FSM_PROCESS
-- @param #FSM_PROCESS self -- @param #FSM_PROCESS self
-- @return #FSM_PROCESS -- @return #FSM_PROCESS
function FSM_PROCESS:Copy( Controllable, Task ) function FSM_PROCESS:Copy( Controllable, Task )
self:T( { self:GetClassNameAndID() } ) --self:T3( { self:GetClassNameAndID() } )
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
@@ -1176,13 +1176,13 @@ do -- FSM_PROCESS
-- Copy End States -- Copy End States
for EndStateID, EndState in pairs( self:GetEndStates() ) do for EndStateID, EndState in pairs( self:GetEndStates() ) do
self:T( EndState ) --self:T3( EndState )
NewFsm:AddEndState( EndState ) NewFsm:AddEndState( EndState )
end end
-- Copy the score tables -- Copy the score tables
for ScoreID, Score in pairs( self:GetScores() ) do for ScoreID, Score in pairs( self:GetScores() ) do
self:T( Score ) --self:T3( Score )
NewFsm:AddScore( ScoreID, Score.ScoreText, Score.Score ) NewFsm:AddScore( ScoreID, Score.ScoreText, Score.Score )
end end
@@ -1431,7 +1431,7 @@ do -- FSM_SET
-- @param #FSM_SET self -- @param #FSM_SET self
-- @return Core.Set#SET_BASE -- @return Core.Set#SET_BASE
function FSM_SET:Get() function FSM_SET:Get()
return self.Controllable return self.Set
end end
function FSM_SET:_call_handler( step, trigger, params, EventName ) function FSM_SET:_call_handler( step, trigger, params, EventName )
+85 -30
View File
@@ -26,19 +26,28 @@
-- --
-- ### Authors: -- ### Authors:
-- --
-- * FlightControl : Design & Programming -- * FlightControl (Design & Programming)
-- --
-- ### Contributions: -- ### Contributions:
--
-- * funkyfranky
-- * Applevangelist
--
-- ===
-- --
-- @module Core.Point -- @module Core.Point
-- @image Core_Coordinate.JPG -- @image Core_Coordinate.JPG
do -- COORDINATE do -- COORDINATE
--- @type COORDINATE --- @type COORDINATE
-- @field #string ClassName Name of the class
-- @field #number x Component of the 3D vector.
-- @field #number y Component of the 3D vector.
-- @field #number z Component of the 3D vector.
-- @field #number Heading Heading in degrees. Needs to be set first.
-- @field #number Velocity Velocity in meters per second. Needs to be set first.
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
@@ -201,44 +210,69 @@ do -- COORDINATE
ClassName = "COORDINATE", ClassName = "COORDINATE",
} }
--- @field COORDINATE.WaypointAltType --- Waypoint altitude types.
-- @type COORDINATE.WaypointAltType
-- @field #string BARO Barometric altitude.
-- @field #string RADIO Radio altitude.
COORDINATE.WaypointAltType = { COORDINATE.WaypointAltType = {
BARO = "BARO", BARO = "BARO",
RADIO = "RADIO", RADIO = "RADIO",
} }
--- @field COORDINATE.WaypointAction --- Waypoint actions.
-- @type COORDINATE.WaypointAction
-- @field #string TurningPoint Turning point.
-- @field #string FlyoverPoint Fly over point.
-- @field #string FromParkingArea From parking area.
-- @field #string FromParkingAreaHot From parking area hot.
-- @field #string FromGroundAreaHot From ground area hot.
-- @field #string FromGroundArea From ground area.
-- @field #string FromRunway From runway.
-- @field #string Landing Landing.
-- @field #string LandingReFuAr Landing and refuel and rearm.
COORDINATE.WaypointAction = { COORDINATE.WaypointAction = {
TurningPoint = "Turning Point", TurningPoint = "Turning Point",
FlyoverPoint = "Fly Over Point", FlyoverPoint = "Fly Over Point",
FromParkingArea = "From Parking Area", FromParkingArea = "From Parking Area",
FromParkingAreaHot = "From Parking Area Hot", FromParkingAreaHot = "From Parking Area Hot",
FromGroundAreaHot = "From Ground Area Hot",
FromGroundArea = "From Ground Area",
FromRunway = "From Runway", FromRunway = "From Runway",
Landing = "Landing", Landing = "Landing",
LandingReFuAr = "LandingReFuAr", LandingReFuAr = "LandingReFuAr",
} }
--- @field COORDINATE.WaypointType --- Waypoint types.
-- @type COORDINATE.WaypointType
-- @field #string TakeOffParking Take of parking.
-- @field #string TakeOffParkingHot Take of parking hot.
-- @field #string TakeOff Take off parking hot.
-- @field #string TakeOffGroundHot Take of from ground hot.
-- @field #string TurningPoint Turning point.
-- @field #string Land Landing point.
-- @field #string LandingReFuAr Landing and refuel and rearm.
COORDINATE.WaypointType = { COORDINATE.WaypointType = {
TakeOffParking = "TakeOffParking", TakeOffParking = "TakeOffParking",
TakeOffParkingHot = "TakeOffParkingHot", TakeOffParkingHot = "TakeOffParkingHot",
TakeOff = "TakeOffParkingHot", TakeOff = "TakeOffParkingHot",
TakeOffGroundHot = "TakeOffGroundHot",
TakeOffGround = "TakeOffGround",
TurningPoint = "Turning Point", TurningPoint = "Turning Point",
Land = "Land", Land = "Land",
LandingReFuAr = "LandingReFuAr", LandingReFuAr = "LandingReFuAr",
} }
--- COORDINATE constructor. --- COORDINATE constructor.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North. -- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North.
-- @param DCS#Distance y The y coordinate of the Vec3 point, pointing to the Right. -- @param DCS#Distance y The y coordinate of the Vec3 point, pointing to up.
-- @param DCS#Distance z The z coordinate of the Vec3 point, pointing to the Right. -- @param DCS#Distance z The z coordinate of the Vec3 point, pointing to the right.
-- @return #COORDINATE -- @return #COORDINATE self
function COORDINATE:New( x, y, z ) function COORDINATE:New( x, y, z )
--env.info("FF COORDINATE New") local self=BASE:Inherit(self, BASE:New()) -- #COORDINATE
local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE
self.x = x self.x = x
self.y = y self.y = y
self.z = z self.z = z
@@ -249,7 +283,7 @@ do -- COORDINATE
--- COORDINATE constructor. --- COORDINATE constructor.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #COORDINATE Coordinate. -- @param #COORDINATE Coordinate.
-- @return #COORDINATE -- @return #COORDINATE self
function COORDINATE:NewFromCoordinate( Coordinate ) function COORDINATE:NewFromCoordinate( Coordinate )
local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE
@@ -263,8 +297,8 @@ do -- COORDINATE
--- Create a new COORDINATE object from Vec2 coordinates. --- Create a new COORDINATE object from Vec2 coordinates.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param DCS#Vec2 Vec2 The Vec2 point. -- @param DCS#Vec2 Vec2 The Vec2 point.
-- @param DCS#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. -- @param DCS#Distance LandHeightAdd (Optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height.
-- @return #COORDINATE -- @return #COORDINATE self
function COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) function COORDINATE:NewFromVec2( Vec2, LandHeightAdd )
local LandHeight = land.getHeight( Vec2 ) local LandHeight = land.getHeight( Vec2 )
@@ -274,8 +308,6 @@ do -- COORDINATE
local self = self:New( Vec2.x, LandHeight, Vec2.y ) -- #COORDINATE local self = self:New( Vec2.x, LandHeight, Vec2.y ) -- #COORDINATE
self:F2( self )
return self return self
end end
@@ -283,7 +315,7 @@ do -- COORDINATE
--- Create a new COORDINATE object from Vec3 coordinates. --- Create a new COORDINATE object from Vec3 coordinates.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param DCS#Vec3 Vec3 The Vec3 point. -- @param DCS#Vec3 Vec3 The Vec3 point.
-- @return #COORDINATE -- @return #COORDINATE self
function COORDINATE:NewFromVec3( Vec3 ) function COORDINATE:NewFromVec3( Vec3 )
local self = self:New( Vec3.x, Vec3.y, Vec3.z ) -- #COORDINATE local self = self:New( Vec3.x, Vec3.y, Vec3.z ) -- #COORDINATE
@@ -292,6 +324,22 @@ do -- COORDINATE
return self return self
end end
--- Create a new COORDINATE object from a waypoint. This uses the components
--
-- * `waypoint.x`
-- * `waypoint.alt`
-- * `waypoint.y`
--
-- @param #COORDINATE self
-- @param DCS#Waypoint Waypoint The waypoint.
-- @return #COORDINATE self
function COORDINATE:NewFromWaypoint(Waypoint)
local self=self:New(Waypoint.x, Waypoint.alt, Waypoint.y) -- #COORDINATE
return self
end
--- Return the coordinates itself. Sounds stupid but can be useful for compatibility. --- Return the coordinates itself. Sounds stupid but can be useful for compatibility.
-- @param #COORDINATE self -- @param #COORDINATE self
@@ -647,7 +695,8 @@ do -- COORDINATE
local y=X*math.sin(phi)+Y*math.cos(phi) local y=X*math.sin(phi)+Y*math.cos(phi)
-- Coordinate assignment looks bit strange but is correct. -- Coordinate assignment looks bit strange but is correct.
return COORDINATE:NewFromVec3({x=y, y=self.y, z=x}) local coord=COORDINATE:NewFromVec3({x=y, y=self.y, z=x})
return coord
end end
--- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. --- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE.
@@ -690,7 +739,8 @@ do -- COORDINATE
function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius ) function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius )
self:F2( { OuterRadius, InnerRadius } ) self:F2( { OuterRadius, InnerRadius } )
return COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) ) local coord=COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) )
return coord
end end
@@ -1611,8 +1661,8 @@ do -- COORDINATE
if x and y then if x and y then
local vec2={ x = x, y = y } local vec2={ x = x, y = y }
coord=COORDINATE:NewFromVec2(vec2) coord=COORDINATE:NewFromVec2(vec2)
end end
return coord return coord
end end
@@ -1718,7 +1768,7 @@ do -- COORDINATE
return self:GetSurfaceType()==land.SurfaceType.LAND return self:GetSurfaceType()==land.SurfaceType.LAND
end end
--- Checks if the surface type is road. --- Checks if the surface type is land.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @return #boolean If true, the surface type at the coordinate is land. -- @return #boolean If true, the surface type at the coordinate is land.
function COORDINATE:IsSurfaceTypeLand() function COORDINATE:IsSurfaceTypeLand()
@@ -1758,10 +1808,9 @@ do -- COORDINATE
--- Creates an explosion at the point of a certain intensity. --- Creates an explosion at the point of a certain intensity.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #number ExplosionIntensity Intensity of the explosion in kg TNT. Default 100 kg. -- @param #number ExplosionIntensity Intensity of the explosion in kg TNT. Default 100 kg.
-- @param #number Delay Delay before explosion in seconds. -- @param #number Delay (Optional) Delay before explosion is triggered in seconds.
-- @return #COORDINATE self -- @return #COORDINATE self
function COORDINATE:Explosion( ExplosionIntensity, Delay ) function COORDINATE:Explosion( ExplosionIntensity, Delay )
self:F2( { ExplosionIntensity } )
ExplosionIntensity=ExplosionIntensity or 100 ExplosionIntensity=ExplosionIntensity or 100
if Delay and Delay>0 then if Delay and Delay>0 then
self:ScheduleOnce(Delay, self.Explosion, self, ExplosionIntensity) self:ScheduleOnce(Delay, self.Explosion, self, ExplosionIntensity)
@@ -1773,11 +1822,17 @@ do -- COORDINATE
--- Creates an illumination bomb at the point. --- Creates an illumination bomb at the point.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #number power Power of illumination bomb in Candela. -- @param #number Power Power of illumination bomb in Candela. Default 1000 cd.
-- @param #number Delay (Optional) Delay before bomb is ignited in seconds.
-- @return #COORDINATE self -- @return #COORDINATE self
function COORDINATE:IlluminationBomb(power) function COORDINATE:IlluminationBomb(Power, Delay)
self:F2() Power=Power or 1000
trigger.action.illuminationBomb( self:GetVec3(), power ) if Delay and Delay>0 then
self:ScheduleOnce(Delay, self.IlluminationBomb, self, Power)
else
trigger.action.illuminationBomb(self:GetVec3(), Power)
end
return self
end end
@@ -2075,7 +2130,7 @@ do -- COORDINATE
--- Circle to all. --- Circle to all.
-- Creates a circle on the map with a given radius, color, fill color, and outline. -- Creates a circle on the map with a given radius, color, fill color, and outline.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #numberr Radius Radius in meters. Default 1000 m. -- @param #number Radius Radius in meters. Default 1000 m.
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
-- @param #number Alpha Transparency [0,1]. Default 1. -- @param #number Alpha Transparency [0,1]. Default 1.
@@ -317,7 +317,7 @@ end
--- Stop dispatcher. --- Stop dispatcher.
-- @param #SCHEDULEDISPATCHER self -- @param #SCHEDULEDISPATCHER self
-- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object. -- @param Core.Scheduler#SCHEDULER Scheduler Scheduler object.
-- @param #table CallID Call ID. -- @param #string CallID (Optional) Scheduler Call ID. If nil, all pending schedules are stopped recursively.
function SCHEDULEDISPATCHER:Stop( Scheduler, CallID ) function SCHEDULEDISPATCHER:Stop( Scheduler, CallID )
self:F2( { Stop = CallID, Scheduler = Scheduler } ) self:F2( { Stop = CallID, Scheduler = Scheduler } )
+2 -2
View File
@@ -238,7 +238,7 @@ end
-- @param #number Stop Time interval in seconds after which the scheduler will be stoppe. -- @param #number Stop Time interval in seconds after which the scheduler will be stoppe.
-- @param #number TraceLevel Trace level [0,3]. Default 3. -- @param #number TraceLevel Trace level [0,3]. Default 3.
-- @param Core.Fsm#FSM Fsm Finite state model. -- @param Core.Fsm#FSM Fsm Finite state model.
-- @return #table The ScheduleID of the planned schedule. -- @return #string The Schedule ID of the planned schedule.
function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm ) function SCHEDULER:Schedule( MasterObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop, TraceLevel, Fsm )
self:F2( { Start, Repeat, RandomizeFactor, Stop } ) self:F2( { Start, Repeat, RandomizeFactor, Stop } )
self:T3( { SchedulerArguments } ) self:T3( { SchedulerArguments } )
@@ -273,7 +273,7 @@ end
--- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided. --- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided.
-- @param #SCHEDULER self -- @param #SCHEDULER self
-- @param #string ScheduleID (Optional) The ScheduleID of the planned (repeating) schedule. -- @param #string ScheduleID (Optional) The Schedule ID of the planned (repeating) schedule.
function SCHEDULER:Start( ScheduleID ) function SCHEDULER:Start( ScheduleID )
self:F3( { ScheduleID } ) self:F3( { ScheduleID } )
self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID))) self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID)))
+664 -4
View File
@@ -233,7 +233,9 @@ do -- SET_BASE
-- @param Core.Base#BASE Object The object itself. -- @param Core.Base#BASE Object The object itself.
-- @return Core.Base#BASE The added BASE Object. -- @return Core.Base#BASE The added BASE Object.
function SET_BASE:Add( ObjectName, Object ) function SET_BASE:Add( ObjectName, Object )
self:F2( { ObjectName = ObjectName, Object = Object } )
-- Debug info.
self:T( { ObjectName = ObjectName, Object = Object } )
-- Ensure that the existing element is removed from the Set before a new one is inserted to the Set -- Ensure that the existing element is removed from the Set before a new one is inserted to the Set
if self.Set[ObjectName] then if self.Set[ObjectName] then
@@ -263,6 +265,32 @@ do -- SET_BASE
end end
--- Sort the set by name.
-- @param #SET_BASE self
-- @return Core.Base#BASE The added BASE Object.
function SET_BASE:SortByName()
local function sort(a, b)
return a<b
end
table.sort(self.Index)
end
--- Add a SET to this set.
-- @param #SET_BASE self
-- @param Core.Set#SET_BASE SetToAdd Set to add.
-- @return #SET_BASE self
function SET_BASE:AddSet(SetToAdd)
for _,ObjectB in pairs(SetToAdd.Set) do
self:AddObject(ObjectB)
end
return self
end
--- Get the *union* of two sets. --- Get the *union* of two sets.
-- @param #SET_BASE self -- @param #SET_BASE self
@@ -1397,6 +1425,22 @@ do -- SET_GROUP
return self return self
end end
--- Activate late activated groups.
-- @param #SET_GROUP self
-- @param #number Delay Delay in seconds.
-- @return #SET_GROUP self
function SET_GROUP:Activate(Delay)
local Set = self:GetSet()
for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP
local group=GroupData --Wrapper.Group#GROUP
if group and group:IsAlive()==false then
group:Activate(Delay)
end
end
return self
end
--- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function.
-- @param #SET_GROUP self -- @param #SET_GROUP self
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
@@ -5907,12 +5951,13 @@ do -- SET_ZONE_GOAL
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
function SET_ZONE_GOAL:OnEventNewZoneGoal( EventData ) function SET_ZONE_GOAL:OnEventNewZoneGoal( EventData )
self:I( { "New Zone Capture Coalition", EventData } ) -- Debug info.
self:I( { "Zone Capture Coalition", EventData.ZoneGoal } ) self:T( { "New Zone Capture Coalition", EventData } )
self:T( { "Zone Capture Coalition", EventData.ZoneGoal } )
if EventData.ZoneGoal then if EventData.ZoneGoal then
if EventData.ZoneGoal and self:IsIncludeObject( EventData.ZoneGoal ) then if EventData.ZoneGoal and self:IsIncludeObject( EventData.ZoneGoal ) then
self:I( { "Adding Zone Capture Coalition", EventData.ZoneGoal.ZoneName, EventData.ZoneGoal } ) self:T( { "Adding Zone Capture Coalition", EventData.ZoneGoal.ZoneName, EventData.ZoneGoal } )
self:Add( EventData.ZoneGoal.ZoneName , EventData.ZoneGoal ) self:Add( EventData.ZoneGoal.ZoneName , EventData.ZoneGoal )
end end
end end
@@ -5963,3 +6008,618 @@ do -- SET_ZONE_GOAL
end end
end end
do -- SET_OPSGROUP
--- @type SET_OPSGROUP
-- @extends Core.Set#SET_BASE
--- Mission designers can use the @{Core.Set#SET_OPSGROUP} class to build sets of OPS groups belonging to certain:
--
-- * Coalitions
-- * Categories
-- * Countries
-- * Contain a certain string pattern
--
-- ## SET_OPSGROUP constructor
--
-- Create a new SET_OPSGROUP object with the @{#SET_OPSGROUP.New} method:
--
-- * @{#SET_OPSGROUP.New}: Creates a new SET_OPSGROUP object.
--
-- ## Add or Remove GROUP(s) from SET_OPSGROUP
--
-- GROUPS can be added and removed using the @{Core.Set#SET_OPSGROUP.AddGroupsByName} and @{Core.Set#SET_OPSGROUP.RemoveGroupsByName} respectively.
-- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_OPSGROUP.
--
-- ## SET_OPSGROUP filter criteria
--
-- You can set filter criteria to define the set of groups within the SET_OPSGROUP.
-- Filter criteria are defined by:
--
-- * @{#SET_OPSGROUP.FilterCoalitions}: Builds the SET_OPSGROUP with the groups belonging to the coalition(s).
-- * @{#SET_OPSGROUP.FilterCategories}: Builds the SET_OPSGROUP with the groups belonging to the category(ies).
-- * @{#SET_OPSGROUP.FilterCountries}: Builds the SET_OPSGROUP with the groups belonging to the country(ies).
-- * @{#SET_OPSGROUP.FilterPrefixes}: Builds the SET_OPSGROUP with the groups *containing* the given string in the group name. **Attention!** Bad naming convention, as this not really filtering *prefixes*.
-- * @{#SET_OPSGROUP.FilterActive}: Builds the SET_OPSGROUP with the groups that are only active. Groups that are inactive (late activation) won't be included in the set!
--
-- For the Category Filter, extra methods have been added:
--
-- * @{#SET_OPSGROUP.FilterCategoryAirplane}: Builds the SET_OPSGROUP from airplanes.
-- * @{#SET_OPSGROUP.FilterCategoryHelicopter}: Builds the SET_OPSGROUP from helicopters.
-- * @{#SET_OPSGROUP.FilterCategoryGround}: Builds the SET_OPSGROUP from ground vehicles or infantry.
-- * @{#SET_OPSGROUP.FilterCategoryShip}: Builds the SET_OPSGROUP from ships.
--
--
-- Once the filter criteria have been set for the SET_OPSGROUP, you can start filtering using:
--
-- * @{#SET_OPSGROUP.FilterStart}: Starts the filtering of the groups within the SET_OPSGROUP and add or remove GROUP objects **dynamically**.
-- * @{#SET_OPSGROUP.FilterOnce}: Filters of the groups **once**.
--
--
-- ## SET_OPSGROUP iterators
--
-- Once the filters have been defined and the SET_OPSGROUP has been built, you can iterate the SET_OPSGROUP with the available iterator methods.
-- The iterator methods will walk the SET_OPSGROUP set, and call for each element within the set a function that you provide.
-- The following iterator methods are currently available within the SET_OPSGROUP:
--
-- * @{#SET_OPSGROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_OPSGROUP.
--
-- ## SET_OPSGROUP trigger events on the GROUP objects.
--
-- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the GROUP objects in the SET_OPSGROUP.
--
-- ### When a GROUP object crashes or is dead, the SET_OPSGROUP will trigger a **Dead** event.
--
-- You can handle the event using the OnBefore and OnAfter event handlers.
-- The event handlers need to have the paramters From, Event, To, GroupObject.
-- The GroupObject is the GROUP object that is dead and within the SET_OPSGROUP, and is passed as a parameter to the event handler.
-- See the following example:
--
-- -- Create the SetCarrier SET_OPSGROUP collection.
--
-- local SetHelicopter = SET_OPSGROUP:New():FilterPrefixes( "Helicopter" ):FilterStart()
--
-- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset.
--
-- function SetHelicopter:OnAfterDead( From, Event, To, GroupObject )
-- self:F( { GroupObject = GroupObject:GetName() } )
-- end
--
--
-- ===
--
-- @field #SET_OPSGROUP SET_OPSGROUP
--
SET_OPSGROUP = {
ClassName = "SET_OPSGROUP",
Filter = {
Coalitions = nil,
Categories = nil,
Countries = nil,
GroupPrefixes = nil,
},
FilterMeta = {
Coalitions = {
red = coalition.side.RED,
blue = coalition.side.BLUE,
neutral = coalition.side.NEUTRAL,
},
Categories = {
plane = Group.Category.AIRPLANE,
helicopter = Group.Category.HELICOPTER,
ground = Group.Category.GROUND,
ship = Group.Category.SHIP,
},
}, -- FilterMeta
}
--- Creates a new SET_OPSGROUP object, building a set of groups belonging to a coalitions, categories, countries, types or with defined prefix names.
-- @param #SET_OPSGROUP self
-- @return #SET_OPSGROUP
function SET_OPSGROUP:New()
-- Inherit SET_BASE.
local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.GROUPS)) -- #SET_OPSGROUP
-- Include non activated
self:FilterActive( false )
return self
end
--- Gets the Set.
-- @param #SET_OPSGROUP self
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:GetAliveSet()
local AliveSet = SET_OPSGROUP:New()
-- Clean the Set before returning with only the alive Groups.
for GroupName, GroupObject in pairs(self.Set) do
local GroupObject=GroupObject --Wrapper.Group#GROUP
if GroupObject and GroupObject:IsAlive() then
AliveSet:Add(GroupName, GroupObject)
end
end
return AliveSet.Set or {}
end
--- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using a given ObjectName as the index.
-- @param #SET_BASE self
-- @param #string ObjectName The name of the object.
-- @param Core.Base#BASE Object The object itself.
-- @return Core.Base#BASE The added BASE Object.
function SET_OPSGROUP:Add(ObjectName, Object)
self:T( { ObjectName = ObjectName, Object = Object } )
-- Ensure that the existing element is removed from the Set before a new one is inserted to the Set
if self.Set[ObjectName] then
self:Remove(ObjectName, true)
end
local object=nil --Ops.OpsGroup#OPSGROUP
if Object:IsInstanceOf("GROUP") then
---
-- GROUP Object
---
-- Fist, look up in the DATABASE if an OPSGROUP already exists.
object=_DATABASE:FindOpsGroup(ObjectName)
if not object then
if Object:IsShip() then
object=NAVYGROUP:New(Object)
elseif Object:IsGround() then
object=ARMYGROUP:New(Object)
elseif Object:IsAir() then
object=FLIGHTGROUP:New(Object)
else
env.error("ERROR: Unknown category of group object!")
end
end
elseif Object:IsInstanceOf("OPSGROUP") then
-- We already have an OPSGROUP.
object=Object
else
env.error("ERROR: Object must be a GROUP or OPSGROUP!")
end
-- Add object to set.
self.Set[ObjectName]=object
-- Add Object name to Index.
table.insert(self.Index, ObjectName)
-- Trigger Added event.
self:Added(ObjectName, object)
end
--- Add a GROUP or OPSGROUP object to the set.
-- **NOTE** that an OPSGROUP is automatically created from the GROUP if it does not exist already.
-- @param Core.Set#SET_OPSGROUP self
-- @param Wrapper.Group#GROUP group The GROUP which should be added to the set. Can also be given as an #OPSGROUP object.
-- @return Core.Set#SET_OPSGROUP self
function SET_OPSGROUP:AddGroup(group)
local groupname=group:GetName()
self:Add(groupname, group )
return self
end
--- Add GROUP(s) or OPSGROUP(s) to the set.
-- @param Core.Set#SET_OPSGROUP self
-- @param #string AddGroupNames A single name or an array of GROUP names.
-- @return Core.Set#SET_OPSGROUP self
function SET_OPSGROUP:AddGroupsByName( AddGroupNames )
local AddGroupNamesArray = ( type( AddGroupNames ) == "table" ) and AddGroupNames or { AddGroupNames }
for AddGroupID, AddGroupName in pairs( AddGroupNamesArray ) do
self:Add(AddGroupName, GROUP:FindByName(AddGroupName))
end
return self
end
--- Remove GROUP(s) or OPSGROUP(s) from the set.
-- @param Core.Set#SET_OPSGROUP self
-- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names.
-- @return Core.Set#SET_OPSGROUP self
function SET_OPSGROUP:RemoveGroupsByName( RemoveGroupNames )
local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames }
for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do
self:Remove( RemoveGroupName )
end
return self
end
--- Finds an OPSGROUP based on the group name.
-- @param #SET_OPSGROUP self
-- @param #string GroupName Name of the group.
-- @return Ops.OpsGroup#OPSGROUP The found OPSGROUP (FLIGHTGROUP, ARMYGROUP or NAVYGROUP) or `#nil` if the group is not in the set.
function SET_OPSGROUP:FindGroup(GroupName)
local GroupFound = self.Set[GroupName]
return GroupFound
end
--- Finds a FLIGHTGROUP based on the group name.
-- @param #SET_OPSGROUP self
-- @param #string GroupName Name of the group.
-- @return Ops.FlightGroup#FLIGHTGROUP The found FLIGHTGROUP or `#nil` if the group is not in the set.
function SET_OPSGROUP:FindFlightGroup(GroupName)
local GroupFound = self:FindGroup(GroupName)
return GroupFound
end
--- Finds a ARMYGROUP based on the group name.
-- @param #SET_OPSGROUP self
-- @param #string GroupName Name of the group.
-- @return Ops.ArmyGroup#ARMYGROUP The found ARMYGROUP or `#nil` if the group is not in the set.
function SET_OPSGROUP:FindArmyGroup(GroupName)
local GroupFound = self:FindGroup(GroupName)
return GroupFound
end
--- Finds a NAVYGROUP based on the group name.
-- @param #SET_OPSGROUP self
-- @param #string GroupName Name of the group.
-- @return Ops.NavyGroup#NAVYGROUP The found NAVYGROUP or `#nil` if the group is not in the set.
function SET_OPSGROUP:FindNavyGroup(GroupName)
local GroupFound = self:FindGroup(GroupName)
return GroupFound
end
--- Builds a set of groups of coalitions.
-- Possible current coalitions are red, blue and neutral.
-- @param #SET_OPSGROUP self
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral" or combinations as a table, for example `{"red", "neutral"}`.
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterCoalitions(Coalitions)
-- Create an empty set.
if not self.Filter.Coalitions then
self.Filter.Coalitions={}
end
-- Ensure we got a table.
if type(Coalitions)~="table" then
Coalitions = {Coalitions}
end
-- Set filter.
for CoalitionID, Coalition in pairs( Coalitions ) do
self.Filter.Coalitions[Coalition] = Coalition
end
return self
end
--- Builds a set of groups out of categories.
--
-- Possible current categories are:
--
-- * "plane" for fixed wing groups
-- * "helicopter" for rotary wing groups
-- * "ground" for ground groups
-- * "ship" for naval groups
--
-- @param #SET_OPSGROUP self
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship" or combinations as a table, for example `{"plane", "helicopter"}`.
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterCategories( Categories )
if not self.Filter.Categories then
self.Filter.Categories={}
end
if type(Categories)~="table" then
Categories={Categories}
end
for CategoryID, Category in pairs( Categories ) do
self.Filter.Categories[Category] = Category
end
return self
end
--- Builds a set of groups out of ground category.
-- @param #SET_OPSGROUP self
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterCategoryGround()
self:FilterCategories("ground")
return self
end
--- Builds a set of groups out of airplane category.
-- @param #SET_OPSGROUP self
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterCategoryAirplane()
self:FilterCategories("plane")
return self
end
--- Builds a set of groups out of aicraft category (planes and helicopters).
-- @param #SET_OPSGROUP self
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterCategoryAircraft()
self:FilterCategories({"plane", "helicopter"})
return self
end
--- Builds a set of groups out of helicopter category.
-- @param #SET_OPSGROUP self
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterCategoryHelicopter()
self:FilterCategories("helicopter")
return self
end
--- Builds a set of groups out of ship category.
-- @param #SET_OPSGROUP self
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterCategoryShip()
self:FilterCategories("ship")
return self
end
--- Builds a set of groups of defined countries.
-- @param #SET_OPSGROUP self
-- @param #string Countries Can take those country strings known within DCS world.
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterCountries(Countries)
-- Create empty table if necessary.
if not self.Filter.Countries then
self.Filter.Countries = {}
end
-- Ensure input is a table.
if type(Countries)~="table" then
Countries={Countries}
end
-- Set filter.
for CountryID, Country in pairs( Countries ) do
self.Filter.Countries[Country] = Country
end
return self
end
--- Builds a set of groups that contain the given string in their group name.
-- **Attention!** Bad naming convention as this **does not** filter only **prefixes** but all groups that **contain** the string.
-- @param #SET_OPSGROUP self
-- @param #string Prefixes The string pattern(s) that needs to be contained in the group name. Can also be passed as a `#table` of strings.
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterPrefixes(Prefixes)
-- Create emtpy table if necessary.
if not self.Filter.GroupPrefixes then
self.Filter.GroupPrefixes={}
end
-- Ensure we have a table.
if type(Prefixes)~="table" then
Prefixes={Prefixes}
end
-- Set group prefixes.
for PrefixID, Prefix in pairs(Prefixes) do
self.Filter.GroupPrefixes[Prefix]=Prefix
end
return self
end
--- Builds a set of groups that are only active.
-- Only the groups that are active will be included within the set.
-- @param #SET_OPSGROUP self
-- @param #boolean Active (optional) Include only active groups to the set.
-- Include inactive groups if you provide false.
-- @return #SET_OPSGROUP self
-- @usage
--
-- -- Include only active groups to the set.
-- GroupSet = SET_OPSGROUP:New():FilterActive():FilterStart()
--
-- -- Include only active groups to the set of the blue coalition, and filter one time.
-- GroupSet = SET_OPSGROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce()
--
-- -- Include only active groups to the set of the blue coalition, and filter one time.
-- -- Later, reset to include back inactive groups to the set.
-- GroupSet = SET_OPSGROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce()
-- ... logic ...
-- GroupSet = SET_OPSGROUP:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce()
--
function SET_OPSGROUP:FilterActive( Active )
Active = Active or not ( Active == false )
self.Filter.Active = Active
return self
end
--- Starts the filtering.
-- @param #SET_OPSGROUP self
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:FilterStart()
if _DATABASE then
self:_FilterStart()
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
end
return self
end
--- Activate late activated groups in the set.
-- @param #SET_OPSGROUP self
-- @param #number Delay Delay in seconds.
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:Activate(Delay)
local Set = self:GetSet()
for GroupID, GroupData in pairs(Set) do
local group=GroupData --Ops.OpsGroup#OPSGROUP
if group and group:IsAlive()==false then
group:Activate(Delay)
end
end
return self
end
--- Handles the OnDead or OnCrash event for alive groups set.
-- Note: The GROUP object in the SET_OPSGROUP collection will only be removed if the last unit is destroyed of the GROUP.
-- @param #SET_OPSGROUP self
-- @param Core.Event#EVENTDATA Event
function SET_OPSGROUP:_EventOnDeadOrCrash( Event )
self:F( { Event } )
if Event.IniDCSUnit then
local ObjectName, Object = self:FindInDatabase( Event )
if ObjectName then
if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed.
self:Remove( ObjectName )
end
end
end
end
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
-- @param #SET_OPSGROUP self
-- @param Core.Event#EVENTDATA Event
-- @return #string The name of the GROUP
-- @return #table The GROUP
function SET_OPSGROUP:AddInDatabase( Event )
if Event.IniObjectCategory==1 then
if not self.Database[Event.IniDCSGroupName] then
self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName )
end
end
return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName]
end
--- Handles the Database to check on any event that Object exists in the Database.
-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa!
-- @param #SET_OPSGROUP self
-- @param Core.Event#EVENTDATA Event Event data table.
-- @return #string The name of the GROUP
-- @return #table The GROUP
function SET_OPSGROUP:FindInDatabase(Event)
return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName]
end
--- Iterate the set and call an iterator function for each OPSGROUP object.
-- @param #SET_OPSGROUP self
-- @param #function IteratorFunction The function that will be called for all OPSGROUPs in the set. **NOTE** that the function must have the OPSGROUP as first parameter!
-- @param ... (Optional) arguments passed to the `IteratorFunction`.
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:ForEachGroup( IteratorFunction, ... )
self:ForEach(IteratorFunction, arg, self:GetSet())
return self
end
--- Check include object.
-- @param #SET_OPSGROUP self
-- @param Wrapper.Group#GROUP MGroup The group that is checked for inclusion.
-- @return #SET_OPSGROUP self
function SET_OPSGROUP:IsIncludeObject(MGroup)
-- Assume it is and check later if not.
local MGroupInclude=true
-- Filter active.
if self.Filter.Active~=nil then
local MGroupActive = false
if self.Filter.Active==false or (self.Filter.Active==true and MGroup:IsActive()==true) then
MGroupActive = true
end
MGroupInclude = MGroupInclude and MGroupActive
end
-- Filter coalitions.
if self.Filter.Coalitions then
local MGroupCoalition = false
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName]==MGroup:GetCoalition() then
MGroupCoalition = true
end
end
MGroupInclude = MGroupInclude and MGroupCoalition
end
-- Filter categories.
if self.Filter.Categories then
local MGroupCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName]==MGroup:GetCategory() then
MGroupCategory = true
end
end
MGroupInclude = MGroupInclude and MGroupCategory
end
-- Filter countries.
if self.Filter.Countries then
local MGroupCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
if country.id[CountryName] == MGroup:GetCountry() then
MGroupCountry = true
end
end
MGroupInclude = MGroupInclude and MGroupCountry
end
-- Filter "prefixes".
if self.Filter.GroupPrefixes then
local MGroupPrefix = false
for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do
if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then --Not sure why "-" is replaced by "%-" ?!
MGroupPrefix = true
end
end
MGroupInclude = MGroupInclude and MGroupPrefix
end
return MGroupInclude
end
end
+7 -14
View File
@@ -2423,8 +2423,7 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @param DCS#Vec3 Vec3 The Vec3 coordinates where to spawn the group. -- @param DCS#Vec3 Vec3 The Vec3 coordinates where to spawn the group.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } ) self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } )
@@ -2493,8 +2492,7 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @param Core.Point#Coordinate Coordinate The Coordinate coordinates where to spawn the group. -- @param Core.Point#Coordinate Coordinate The Coordinate coordinates where to spawn the group.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex ) function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex )
self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) self:F( { self.SpawnTemplatePrefix, SpawnIndex } )
@@ -2510,8 +2508,7 @@ end
-- @param #SPAWN self -- @param #SPAWN self
-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group. -- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnPointVec3 = ZONE:New( ZoneName ):GetPointVec3( 2000 ) -- Get the center of the ZONE object at 2000 meters from the ground. -- local SpawnPointVec3 = ZONE:New( ZoneName ):GetPointVec3( 2000 ) -- Get the center of the ZONE object at 2000 meters from the ground.
@@ -2535,8 +2532,7 @@ end
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnVec2 = ZONE:New( ZoneName ):GetVec2() -- local SpawnVec2 = ZONE:New( ZoneName ):GetVec2()
@@ -2569,8 +2565,7 @@ end
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2() -- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2()
@@ -2626,8 +2621,7 @@ end
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil Nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnStatic = STATIC:FindByName( StaticName ) -- local SpawnStatic = STATIC:FindByName( StaticName )
@@ -2658,8 +2652,7 @@ end
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
-- @return Wrapper.Group#GROUP that was spawned. -- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
-- @return #nil when nothing was spawned.
-- @usage -- @usage
-- --
-- local SpawnZone = ZONE:New( ZoneName ) -- local SpawnZone = ZONE:New( ZoneName )
+14 -15
View File
@@ -34,8 +34,6 @@
-- --
-- === -- ===
-- --
-- ![Banner Image](..\Presentations\Timer\TIMER_Main.jpg)
--
-- # The TIMER Concept -- # The TIMER Concept
-- --
-- The TIMER class is the little sister of the @{Core.Scheduler#SCHEDULER} class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases. -- The TIMER class is the little sister of the @{Core.Scheduler#SCHEDULER} class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases.
@@ -107,19 +105,17 @@ TIMER = {
--- Timer ID. --- Timer ID.
_TIMERID=0 _TIMERID=0
--- Timer data base.
--_TIMERDB={}
--- TIMER class version. --- TIMER class version.
-- @field #string version -- @field #string version
TIMER.version="0.1.1" TIMER.version="0.1.2"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot. -- TODO: Randomization.
-- TODO: Write docs. -- TODO: Pause/unpause.
-- DONE: Write docs.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor -- Constructor
@@ -156,9 +152,6 @@ function TIMER:New(Function, ...)
-- Log id. -- Log id.
self.lid=string.format("TIMER UID=%d | ", self.uid) self.lid=string.format("TIMER UID=%d | ", self.uid)
-- Add to DB.
--_TIMERDB[self.uid]=self
return self return self
end end
@@ -219,10 +212,7 @@ function TIMER:Stop(Delay)
-- Not running any more. -- Not running any more.
self.isrunning=false self.isrunning=false
-- Remove DB entry.
--_TIMERDB[self.uid]=nil
end end
end end
@@ -239,6 +229,15 @@ function TIMER:SetMaxFunctionCalls(Nmax)
return self return self
end end
--- Set time interval. Can also be set when the timer is already running and is applied after the next function call.
-- @param #TIMER self
-- @param #number dT Time interval in seconds.
-- @return #TIMER self
function TIMER:SetTimeInterval(dT)
self.dT=dT
return self
end
--- Check if the timer has been started and was not stopped. --- Check if the timer has been started and was not stopped.
-- @param #TIMER self -- @param #TIMER self
-- @return #boolean If `true`, the timer is running. -- @return #boolean If `true`, the timer is running.
+102 -54
View File
@@ -288,6 +288,23 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1
return self.Coordinate return self.Coordinate
end end
--- Get 2D distance to a coordinate.
-- @param #ZONE_BASE self
-- @param Core.Point#COORDINATE Coordinate Reference coordinate. Can also be a DCS#Vec2 or DCS#Vec3 object.
-- @return #number Distance to the reference coordinate in meters.
function ZONE_BASE:Get2DDistance(Coordinate)
local a=self:GetVec2()
local b={}
if Coordinate.z then
b.x=Coordinate.x
b.y=Coordinate.z
else
b.x=Coordinate.x
b.y=Coordinate.y
end
local dist=UTILS.VecDist2D(a,b)
return dist
end
--- Define a random @{DCS#Vec2} within the zone. --- Define a random @{DCS#Vec2} within the zone.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
@@ -1161,24 +1178,54 @@ end
--- Returns a random Vec2 location within the zone. --- Returns a random Vec2 location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
-- @return DCS#Vec2 The random location within the zone. -- @return DCS#Vec2 The random location within the zone.
function ZONE_RADIUS:GetRandomVec2( inner, outer ) function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
self:F( self.ZoneName, inner, outer )
local Point = {} local Vec2 = self:GetVec2()
local Vec2 = self:GetVec2() local _inner = inner or 0
local _inner = inner or 0 local _outer = outer or self:GetRadius()
local _outer = outer or self:GetRadius()
local angle = math.random() * math.pi * 2; if surfacetypes and type(surfacetypes)~="table" then
Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer); surfacetypes={surfacetypes}
Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer); end
self:T( { Point } ) local function _getpoint()
local point = {}
local angle = math.random() * math.pi * 2
point.x = Vec2.x + math.cos(angle) * math.random(_inner, _outer)
point.y = Vec2.y + math.sin(angle) * math.random(_inner, _outer)
return point
end
return Point local function _checkSurface(point)
local stype=land.getSurfaceType(point)
for _,sf in pairs(surfacetypes) do
if sf==stype then
return true
end
end
return false
end
local point=_getpoint()
if surfacetypes then
local N=1 ; local Nmax=1000 ; local gotit=false
while gotit==false and N<=Nmax do
gotit=_checkSurface(point)
if gotit then
env.info(string.format("Got random coordinate with surface type %d after N=%d/%d iterations", land.getSurfaceType(point), N, Nmax))
else
point=_getpoint()
N=N+1
end
end
end
return point
end end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
@@ -1230,15 +1277,15 @@ end
--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone. --- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (Optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. -- @param #number outer (Optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
-- @return Core.Point#COORDINATE -- @param #table surfacetypes (Optional) Table of surface types. Can also be a single surface type. We will try max 1000 times to find the right type!
function ZONE_RADIUS:GetRandomCoordinate( inner, outer ) -- @return Core.Point#COORDINATE The random coordinate.
self:F( self.ZoneName, inner, outer ) function ZONE_RADIUS:GetRandomCoordinate(inner, outer, surfacetypes)
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2(inner, outer) ) local vec2=self:GetRandomVec2(inner, outer, surfacetypes)
self:T3( { Coordinate = Coordinate } ) local Coordinate = COORDINATE:NewFromVec2(vec2)
return Coordinate return Coordinate
end end
@@ -1301,7 +1348,7 @@ function ZONE:New( ZoneName )
-- Error! -- Error!
if not Zone then if not Zone then
error( "Zone " .. ZoneName .. " does not exist." ) env.error( "ERROR: Zone " .. ZoneName .. " does not exist!" )
return nil return nil
end end
@@ -1941,26 +1988,28 @@ end
-- @param #ZONE_POLYGON_BASE self -- @param #ZONE_POLYGON_BASE self
-- @return DCS#Vec2 The Vec2 coordinate. -- @return DCS#Vec2 The Vec2 coordinate.
function ZONE_POLYGON_BASE:GetRandomVec2() function ZONE_POLYGON_BASE:GetRandomVec2()
self:F2()
--- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way... -- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
local Vec2Found = false
local Vec2 -- Get the bounding square.
local BS = self:GetBoundingSquare() local BS = self:GetBoundingSquare()
self:T2( BS ) local Nmax=1000 ; local n=0
while n<Nmax do
while Vec2Found == false do -- Random point in the bounding square.
Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } local Vec2={x=math.random(BS.x1, BS.x2), y=math.random(BS.y1, BS.y2)}
self:T2( Vec2 )
if self:IsVec2InZone( Vec2 ) then -- Check if this is in the polygon.
Vec2Found = true if self:IsVec2InZone(Vec2) then
return Vec2
end end
n=n+1
end end
self:T2( Vec2 ) self:E("Could not find a random point in the polygon zone!")
return nil
return Vec2
end end
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. --- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.
@@ -2152,6 +2201,9 @@ end
do -- ZONE_AIRBASE do -- ZONE_AIRBASE
--- @type ZONE_AIRBASE --- @type ZONE_AIRBASE
-- @field #boolean isShip If `true`, airbase is a ship.
-- @field #boolean isHelipad If `true`, airbase is a helipad.
-- @field #boolean isAirdrome If `true`, airbase is an airdrome.
-- @extends #ZONE_RADIUS -- @extends #ZONE_RADIUS
@@ -2180,6 +2232,20 @@ do -- ZONE_AIRBASE
self._.ZoneAirbase = Airbase self._.ZoneAirbase = Airbase
self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2() self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2()
if Airbase:IsShip() then
self.isShip=true
self.isHelipad=false
self.isAirdrome=false
elseif Airbase:IsHelipad() then
self.isShip=false
self.isHelipad=true
self.isAirdrome=false
elseif Airbase:IsAirdrome() then
self.isShip=false
self.isHelipad=false
self.isAirdrome=true
end
-- Zone objects are added to the _DATABASE and SET_ZONE objects. -- Zone objects are added to the _DATABASE and SET_ZONE objects.
_EVENTDISPATCHER:CreateEventNewZone( self ) _EVENTDISPATCHER:CreateEventNewZone( self )
@@ -2194,9 +2260,9 @@ do -- ZONE_AIRBASE
return self._.ZoneAirbase return self._.ZoneAirbase
end end
--- Returns the current location of the @{Wrapper.Group}. --- Returns the current location of the AIRBASE.
-- @param #ZONE_AIRBASE self -- @param #ZONE_AIRBASE self
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. -- @return DCS#Vec2 The location of the zone based on the AIRBASE location.
function ZONE_AIRBASE:GetVec2() function ZONE_AIRBASE:GetVec2()
self:F( self.ZoneName ) self:F( self.ZoneName )
@@ -2214,24 +2280,6 @@ do -- ZONE_AIRBASE
return ZoneVec2 return ZoneVec2
end end
--- Returns a random location within the zone of the @{Wrapper.Group}.
-- @param #ZONE_AIRBASE self
-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location.
function ZONE_AIRBASE:GetRandomVec2()
self:F( self.ZoneName )
local Point = {}
local Vec2 = self._.ZoneAirbase:GetVec2()
local angle = math.random() * math.pi*2;
Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius();
Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius();
self:T( { Point } )
return Point
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
-- @param #ZONE_AIRBASE self -- @param #ZONE_AIRBASE self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
+25
View File
@@ -471,6 +471,22 @@ do -- Types
--@field #boolean lateActivated --@field #boolean lateActivated
--@field #boolean uncontrolled --@field #boolean uncontrolled
--- DCS template data structure.
-- @type Template
-- @field #boolean uncontrolled Aircraft is uncontrolled.
-- @field #boolean lateActivation Group is late activated.
-- @field #number x 2D Position on x-axis in meters.
-- @field #number y 2D Position on y-axis in meters.
-- @field #table units Unit list.
--
--- Unit data structure.
--@type Template.Unit
--@field #string name Name of the unit.
--@field #number x
--@field #number y
--@field #number alt
end -- end --
@@ -498,6 +514,10 @@ do -- Object
--- @function [parent=#Object] isExist --- @function [parent=#Object] isExist
-- @param #Object self -- @param #Object self
-- @return #boolean -- @return #boolean
--- @function [parent=#Object] isActive
-- @param #Object self
-- @return #boolean
--- @function [parent=#Object] destroy --- @function [parent=#Object] destroy
-- @param #Object self -- @param #Object self
@@ -1130,6 +1150,11 @@ do -- Unit
-- @function [parent=#Unit] getAmmo -- @function [parent=#Unit] getAmmo
-- @param #Unit self -- @param #Unit self
-- @return #Unit.Ammo -- @return #Unit.Ammo
--- Returns the number of infantry that can be embark onto the aircraft. Only returns a value if run on airplanes or helicopters. Returns nil if run on ground or ship units.
-- @function [parent=#Unit] getDescentCapacity
-- @param #Unit self
-- @return #number Number of soldiers that embark.
--- Returns the unit sensors. --- Returns the unit sensors.
-- @function [parent=#Unit] getSensors -- @function [parent=#Unit] getSensors
+1 -2
View File
@@ -349,7 +349,6 @@ RANGE.Defaults={
boxlength=3000, boxlength=3000,
boxwidth=300, boxwidth=300,
goodpass=20, goodpass=20,
goodhitrange=25,
foulline=610, foulline=610,
} }
@@ -2570,7 +2569,7 @@ function RANGE:_DisplayBombTargets(_unitname)
end end
end end
self:_DisplayMessageToGroup(_unit,_text, 60, true, true) self:_DisplayMessageToGroup(_unit,_text, 120, true, true)
end end
end end
+186 -128
View File
@@ -80,12 +80,14 @@
-- @field #string autosavepath Path where the asset file is saved on auto save. -- @field #string autosavepath Path where the asset file is saved on auto save.
-- @field #string autosavefile File name of the auto asset save file. Default is auto generated from warehouse id and name. -- @field #string autosavefile File name of the auto asset save file. Default is auto generated from warehouse id and name.
-- @field #boolean safeparking If true, parking spots for aircraft are considered as occupied if e.g. a client aircraft is parked there. Default false. -- @field #boolean safeparking If true, parking spots for aircraft are considered as occupied if e.g. a client aircraft is parked there. Default false.
-- @field #boolean isunit If true, warehouse is represented by a unit instead of a static. -- @field #boolean isUnit If `true`, warehouse is represented by a unit instead of a static.
-- @field #boolean isShip If `true`, warehouse is represented by a ship unit.
-- @field #number lowfuelthresh Low fuel threshold. Triggers the event AssetLowFuel if for any unit fuel goes below this number. -- @field #number lowfuelthresh Low fuel threshold. Triggers the event AssetLowFuel if for any unit fuel goes below this number.
-- @field #boolean respawnafterdestroyed If true, warehouse is respawned after it was destroyed. Assets are kept. -- @field #boolean respawnafterdestroyed If true, warehouse is respawned after it was destroyed. Assets are kept.
-- @field #number respawndelay Delay before respawn in seconds. -- @field #number respawndelay Delay before respawn in seconds.
-- @field #number runwaydestroyed Time stamp timer.getAbsTime() when the runway was destroyed. -- @field #number runwaydestroyed Time stamp timer.getAbsTime() when the runway was destroyed.
-- @field #number runwayrepairtime Time in seconds until runway will be repaired after it was destroyed. Default is 3600 sec (one hour). -- @field #number runwayrepairtime Time in seconds until runway will be repaired after it was destroyed. Default is 3600 sec (one hour).
-- @field Ops.FlightControl#FLIGHTCONTROL flightcontrol Flight control of this warehouse.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Have your assets at the right place at the right time - or not! --- Have your assets at the right place at the right time - or not!
@@ -1590,7 +1592,8 @@ WAREHOUSE = {
autosavepath = nil, autosavepath = nil,
autosavefile = nil, autosavefile = nil,
saveparking = false, saveparking = false,
isunit = false, isUnit = false,
isShip = false,
lowfuelthresh = 0.15, lowfuelthresh = 0.15,
respawnafterdestroyed=false, respawnafterdestroyed=false,
respawndelay = nil, respawndelay = nil,
@@ -1599,6 +1602,8 @@ WAREHOUSE = {
--- Item of the warehouse stock table. --- Item of the warehouse stock table.
-- @type WAREHOUSE.Assetitem -- @type WAREHOUSE.Assetitem
-- @field #number uid Unique id of the asset. -- @field #number uid Unique id of the asset.
-- @field #number wid ID of the warehouse this asset belongs to.
-- @field #number rid Request ID of this asset (if any).
-- @field #string templatename Name of the template group. -- @field #string templatename Name of the template group.
-- @field #table template The spawn template of the group. -- @field #table template The spawn template of the group.
-- @field DCS#Group.Category category Category of the group. -- @field DCS#Group.Category category Category of the group.
@@ -1620,9 +1625,17 @@ WAREHOUSE = {
-- @field #boolean spawned If true, asset was spawned into the cruel world. If false, it is still in stock. -- @field #boolean spawned If true, asset was spawned into the cruel world. If false, it is still in stock.
-- @field #string spawngroupname Name of the spawned group. -- @field #string spawngroupname Name of the spawned group.
-- @field #boolean iscargo If true, asset is cargo. If false asset is transport. Nil if in stock. -- @field #boolean iscargo If true, asset is cargo. If false asset is transport. Nil if in stock.
-- @field #number rid The request ID of this asset.
-- @field #boolean arrived If true, asset arrived at its destination. -- @field #boolean arrived If true, asset arrived at its destination.
--
-- @field #number damage Damage of asset group in percent. -- @field #number damage Damage of asset group in percent.
-- @field Ops.AirWing#AIRWING.Payload payload The payload of the asset.
-- @field Ops.OpsGroup#OPSGROUP flightgroup The flightgroup object.
-- @field Ops.Cohort#COHORT cohort The cohort this asset belongs to.
-- @field Ops.Legion#LEGION legion The legion this asset belonts to.
-- @field #string squadname Name of the squadron this asset belongs to.
-- @field #number Treturned Time stamp when asset returned to its legion (airwing, brigade).
-- @field #boolean requested If `true`, asset was requested and cannot be selected by another request.
-- @field #boolean isReserved If `true`, asset was reserved and cannot be selected by another request.
--- Item of the warehouse queue table. --- Item of the warehouse queue table.
-- @type WAREHOUSE.Queueitem -- @type WAREHOUSE.Queueitem
@@ -1645,6 +1658,7 @@ WAREHOUSE = {
-- @field #table transportassets Table of transport carrier assets. Each element of the table is a @{#WAREHOUSE.Assetitem}. -- @field #table transportassets Table of transport carrier assets. Each element of the table is a @{#WAREHOUSE.Assetitem}.
-- @field #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}. -- @field #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}.
-- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}. -- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}.
-- @field #boolean lateActivation Assets are spawned in late activated state.
--- Item of the warehouse pending queue table. --- Item of the warehouse pending queue table.
-- @type WAREHOUSE.Pendingitem -- @type WAREHOUSE.Pendingitem
@@ -1842,40 +1856,50 @@ WAREHOUSE.version="1.0.2"
--- The WAREHOUSE constructor. Creates a new WAREHOUSE object from a static object. Parameters like the coalition and country are taken from the static object structure. --- The WAREHOUSE constructor. Creates a new WAREHOUSE object from a static object. Parameters like the coalition and country are taken from the static object structure.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @param Wrapper.Static#STATIC warehouse The physical structure representing the warehouse. -- @param Wrapper.Static#STATIC warehouse The physical structure representing the warehouse. Can also be a @{Wrapper.Unit#UNIT}.
-- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static -- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static/unit representing the warehouse.
-- @return #WAREHOUSE self -- @return #WAREHOUSE self
function WAREHOUSE:New(warehouse, alias) function WAREHOUSE:New(warehouse, alias)
-- Inherit everthing from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #WAREHOUSE
-- Check if just a string was given and convert to static. -- Check if just a string was given and convert to static.
if type(warehouse)=="string" then if type(warehouse)=="string" then
local warehousename=warehouse local warehousename=warehouse
warehouse=UNIT:FindByName(warehousename) warehouse=UNIT:FindByName(warehousename)
if warehouse==nil then if warehouse==nil then
warehouse=STATIC:FindByName(warehousename, true) warehouse=STATIC:FindByName(warehousename, true)
self.isunit=false
else
self.isunit=true
end end
end end
-- Nil check. -- Nil check.
if warehouse==nil then if warehouse==nil then
BASE:E("ERROR: Warehouse does not exist!") env.error("ERROR: Warehouse does not exist!")
return nil return nil
end end
-- Check if we have a STATIC or UNIT object.
if warehouse:IsInstanceOf("STATIC") then
self.isUnit=false
elseif warehouse:IsInstanceOf("UNIT") then
self.isUnit=true
if warehouse:IsShip() then
self.isShip=true
end
else
env.error("ERROR: Warehouse is neither STATIC nor UNIT object!")
return nil
end
-- Set alias. -- Set alias.
self.alias=alias or warehouse:GetName() self.alias=alias or warehouse:GetName()
-- Print version.
env.info(string.format("Adding warehouse v%s for structure %s with alias %s", WAREHOUSE.version, warehouse:GetName(), self.alias))
-- Inherit everthing from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #WAREHOUSE
-- Set some string id for output to DCS.log file. -- Set some string id for output to DCS.log file.
self.lid=string.format("WAREHOUSE %s | ", self.alias) self.lid=string.format("WAREHOUSE %s | ", self.alias)
-- Print version.
self:I(self.lid..string.format("Adding warehouse v%s for structure %s [isUnit=%s, isShip=%s]", WAREHOUSE.version, warehouse:GetName(), tostring(self:IsUnit()), tostring(self:IsShip())))
-- Set some variables. -- Set some variables.
self.warehouse=warehouse self.warehouse=warehouse
@@ -1899,14 +1923,19 @@ function WAREHOUSE:New(warehouse, alias)
end end
-- Define warehouse and default spawn zone. -- Define warehouse and default spawn zone.
self.zone=ZONE_RADIUS:New(string.format("Warehouse zone %s", self.warehouse:GetName()), warehouse:GetVec2(), 500) if self.isShip then
self.spawnzone=ZONE_RADIUS:New(string.format("Warehouse %s spawn zone", self.warehouse:GetName()), warehouse:GetVec2(), 250) self.zone=ZONE_AIRBASE:New(self.warehouse:GetName(), 1000)
self.spawnzone=ZONE_AIRBASE:New(self.warehouse:GetName(), 1000)
else
self.zone=ZONE_RADIUS:New(string.format("Warehouse zone %s", self.warehouse:GetName()), warehouse:GetVec2(), 500)
self.spawnzone=ZONE_RADIUS:New(string.format("Warehouse %s spawn zone", self.warehouse:GetName()), warehouse:GetVec2(), 250)
end
-- Defaults -- Defaults
self:SetMarker(true) self:SetMarker(true)
self:SetReportOff() self:SetReportOff()
self:SetRunwayRepairtime() self:SetRunwayRepairtime()
--self:SetVerbosityLevel(0)
-- Add warehouse to database. -- Add warehouse to database.
_WAREHOUSEDB.Warehouses[self.uid]=self _WAREHOUSEDB.Warehouses[self.uid]=self
@@ -2590,6 +2619,12 @@ function WAREHOUSE:SetSpawnZone(zone, maxdist)
return self return self
end end
--- Get the spawn zone.
-- @param #WAREHOUSE self
-- @return Core.Zone#ZONE The spawn zone.
function WAREHOUSE:GetSpawnZone()
return self.spawnzone
end
--- Set a warehouse zone. If this zone is captured, the warehouse and all its assets fall into the hands of the enemy. --- Set a warehouse zone. If this zone is captured, the warehouse and all its assets fall into the hands of the enemy.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
@@ -2631,9 +2666,8 @@ end
--- Check parking ID. --- Check parking ID.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot. -- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot.
-- @param Wrapper.Airbase#AIRBASE airbase The airbase.
-- @return #boolean If true, parking is valid. -- @return #boolean If true, parking is valid.
function WAREHOUSE:_CheckParkingValid(spot, airbase) function WAREHOUSE:_CheckParkingValid(spot)
if self.parkingIDs==nil then if self.parkingIDs==nil then
return true return true
@@ -2648,6 +2682,25 @@ function WAREHOUSE:_CheckParkingValid(spot, airbase)
return false return false
end end
--- Check parking ID for an asset.
-- @param #WAREHOUSE self
-- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot.
-- @return #boolean If true, parking is valid.
function WAREHOUSE:_CheckParkingAsset(spot, asset)
if asset.parkingIDs==nil then
return true
end
for _,id in pairs(asset.parkingIDs or {}) do
if spot.TerminalID==id then
return true
end
end
return false
end
--- Enable auto save of warehouse assets at mission end event. --- Enable auto save of warehouse assets at mission end event.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
@@ -3088,14 +3141,16 @@ end
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @return DCS#Vec3 The 3D vector of the warehouse. -- @return DCS#Vec3 The 3D vector of the warehouse.
function WAREHOUSE:GetVec3() function WAREHOUSE:GetVec3()
return self.warehouse:GetVec3() local vec3=self.warehouse:GetVec3()
return vec3
end end
--- Get 2D vector of warehouse static. --- Get 2D vector of warehouse static.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @return DCS#Vec2 The 2D vector of the warehouse. -- @return DCS#Vec2 The 2D vector of the warehouse.
function WAREHOUSE:GetVec2() function WAREHOUSE:GetVec2()
return self.warehouse:GetVec2() local vec2=self.warehouse:GetVec2()
return vec2
end end
@@ -3164,18 +3219,6 @@ function WAREHOUSE:GetAssignment(request)
return tostring(request.assignment) return tostring(request.assignment)
end end
--[[
--- Get warehouse unique ID from static warehouse object. This is the ID under which you find the @{#WAREHOUSE} object in the global data base.
-- @param #WAREHOUSE self
-- @param #string staticname Name of the warehouse static object.
-- @return #number Warehouse unique ID.
function WAREHOUSE:GetWarehouseID(staticname)
local warehouse=STATIC:FindByName(staticname, true)
local uid=tonumber(warehouse:GetID())
return uid
end
]]
--- Find a warehouse in the global warehouse data base. --- Find a warehouse in the global warehouse data base.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @param #number uid The unique ID of the warehouse. -- @param #number uid The unique ID of the warehouse.
@@ -3291,7 +3334,7 @@ end
--- Check if runway is operational. --- Check if runway is operational.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @return #boolean If true, runway is operational. -- @return #boolean If `true`, runway is operational.
function WAREHOUSE:IsRunwayOperational() function WAREHOUSE:IsRunwayOperational()
if self.airbase then if self.airbase then
if self.runwaydestroyed then if self.runwaydestroyed then
@@ -3327,6 +3370,27 @@ function WAREHOUSE:GetRunwayRepairtime()
return 0 return 0
end end
--- Check if warehouse physical representation is a unit (not a static) object.
-- @param #WAREHOUSE self
-- @return #boolean If `true`, warehouse object is a unit.
function WAREHOUSE:IsUnit()
return self.isUnit
end
--- Check if warehouse physical representation is a static (not a unit) object.
-- @param #WAREHOUSE self
-- @return #boolean If `true`, warehouse object is a static.
function WAREHOUSE:IsStatic()
return not self.isUnit
end
--- Check if warehouse physical representation is a ship.
-- @param #WAREHOUSE self
-- @return #boolean If `true`, warehouse object is a ship.
function WAREHOUSE:IsShip()
return self.isShip
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM states -- FSM states
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -3537,7 +3601,7 @@ function WAREHOUSE:onafterStatus(From, Event, To)
self:_PrintQueue(self.pending, "Queue pending") self:_PrintQueue(self.pending, "Queue pending")
-- Check fuel for all assets. -- Check fuel for all assets.
self:_CheckFuel() --self:_CheckFuel()
-- Update warhouse marker on F10 map. -- Update warhouse marker on F10 map.
self:_UpdateWarehouseMarkText() self:_UpdateWarehouseMarkText()
@@ -3921,6 +3985,8 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu
-- Asset is not spawned. -- Asset is not spawned.
asset.spawned=false asset.spawned=false
asset.requested=false
asset.isReserved=false
asset.iscargo=nil asset.iscargo=nil
asset.arrived=nil asset.arrived=nil
@@ -3971,9 +4037,21 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu
-- Destroy group if it is alive. -- Destroy group if it is alive.
if group:IsAlive()==true then if group:IsAlive()==true then
self:_DebugMessage(string.format("Removing group %s", group:GetName()), 5) self:_DebugMessage(string.format("Removing group %s", group:GetName()), 5)
-- Setting parameter to false, i.e. creating NO dead or remove unit event, seems to not confuse the dispatcher logic.
-- TODO: It would be nice, however, to have the remove event. local opsgroup=_DATABASE:GetOpsGroup(group:GetName())
group:Destroy() --(false) if opsgroup then
opsgroup:Despawn(0, true)
opsgroup:__Stop(-0.01)
else
-- Setting parameter to false, i.e. creating NO dead or remove unit event, seems to not confuse the dispatcher logic.
-- TODO: It would be nice, however, to have the remove event.
group:Destroy() --(false)
end
else
local opsgroup=_DATABASE:GetOpsGroup(group:GetName())
if opsgroup then
opsgroup:Stop()
end
end end
else else
@@ -4099,6 +4177,8 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
asset.skill=skill asset.skill=skill
asset.assignment=assignment asset.assignment=assignment
asset.spawned=false asset.spawned=false
asset.requested=false
asset.isReserved=false
asset.life0=group:GetLife0() asset.life0=group:GetLife0()
asset.damage=0 asset.damage=0
asset.spawngroupname=string.format("%s_AID-%d", templategroupname, asset.uid) asset.spawngroupname=string.format("%s_AID-%d", templategroupname, asset.uid)
@@ -4350,7 +4430,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request)
-- Delete request from queue because it will never be possible. -- Delete request from queue because it will never be possible.
-- Unless(!) at least one is a moving warehouse, which could, e.g., be an aircraft carrier. -- Unless(!) at least one is a moving warehouse, which could, e.g., be an aircraft carrier.
if not (self.isunit or Request.warehouse.isunit) then if not (self.isUnit or Request.warehouse.isUnit) then
self:_DeleteQueueItem(Request, self.queue) self:_DeleteQueueItem(Request, self.queue)
end end
@@ -4467,6 +4547,11 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request)
return return
end end
-- Trigger event.
if spawngroup then
self:__AssetSpawned(0.01, spawngroup, _assetitem, Request)
end
end end
-- Init problem table. -- Init problem table.
@@ -4883,6 +4968,13 @@ function WAREHOUSE:onbeforeArrived(From, Event, To, group)
local asset=self:FindAssetInDB(group) local asset=self:FindAssetInDB(group)
if asset then if asset then
if asset.flightgroup and not asset.arrived then
--env.info("FF asset has a flightgroup. arrival will be handled there!")
asset.arrived=true
return false
end
if asset.arrived==true then if asset.arrived==true then
-- Asset already arrived (e.g. if multiple units trigger the event via landing). -- Asset already arrived (e.g. if multiple units trigger the event via landing).
return false return false
@@ -4890,6 +4982,7 @@ function WAREHOUSE:onbeforeArrived(From, Event, To, group)
asset.arrived=true --ensure this is not called again from the same asset group. asset.arrived=true --ensure this is not called again from the same asset group.
return true return true
end end
end end
end end
@@ -5288,24 +5381,6 @@ function WAREHOUSE:onafterRunwayRepaired(From, Event, To)
end end
--- On before "AssetSpawned" event. Checks whether the asset was already set to "spawned" for groups with multiple units.
-- @param #WAREHOUSE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Wrapper.Group#GROUP group The group spawned.
-- @param #WAREHOUSE.Assetitem asset The asset that is dead.
-- @param #WAREHOUSE.Pendingitem request The request of the dead asset.
function WAREHOUSE:onbeforeAssetSpawned(From, Event, To, group, asset, request)
if asset.spawned then
--return false
else
--return true
end
return true
end
--- On after "AssetSpawned" event triggered when an asset group is spawned into the cruel world. --- On after "AssetSpawned" event triggered when an asset group is spawned into the cruel world.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @param #string From From state. -- @param #string From From state.
@@ -5320,6 +5395,24 @@ function WAREHOUSE:onafterAssetSpawned(From, Event, To, group, asset, request)
-- Sete asset state to spawned. -- Sete asset state to spawned.
asset.spawned=true asset.spawned=true
-- Set spawn group name.
asset.spawngroupname=group:GetName()
-- Remove asset from stock.
self:_DeleteStockItem(asset)
-- Add group.
if asset.iscargo==true then
request.cargogroupset=request.cargogroupset or SET_GROUP:New()
request.cargogroupset:AddGroup(group)
else
request.transportgroupset=request.transportgroupset or SET_GROUP:New()
request.transportgroupset:AddGroup(group)
end
-- Set warehouse state.
group:SetState(group, "WAREHOUSE", self)
-- Check if all assets groups are spawned and trigger events. -- Check if all assets groups are spawned and trigger events.
local n=0 local n=0
@@ -5670,15 +5763,15 @@ function WAREHOUSE:_SpawnAssetRequest(Request)
if asset.category==Group.Category.GROUND then if asset.category==Group.Category.GROUND then
-- Spawn ground troops. -- Spawn ground troops.
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone) _group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation)
elseif asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then elseif asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then
-- Spawn air units. -- Spawn air units.
if Parking[asset.uid] then if Parking[asset.uid] then
_group=self:_SpawnAssetAircraft(_alias, asset, Request, Parking[asset.uid], UnControlled) _group=self:_SpawnAssetAircraft(_alias, asset, Request, Parking[asset.uid], UnControlled, Request.lateActivation)
else else
_group=self:_SpawnAssetAircraft(_alias, asset, Request, nil, UnControlled) _group=self:_SpawnAssetAircraft(_alias, asset, Request, nil, UnControlled, Request.lateActivation)
end end
elseif asset.category==Group.Category.TRAIN then elseif asset.category==Group.Category.TRAIN then
@@ -5688,7 +5781,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request)
--TODO: Rail should only get one asset because they would spawn on top! --TODO: Rail should only get one asset because they would spawn on top!
-- Spawn naval assets. -- Spawn naval assets.
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone) _group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation)
end end
--self:E(self.lid.."ERROR: Spawning of TRAIN assets not possible yet!") --self:E(self.lid.."ERROR: Spawning of TRAIN assets not possible yet!")
@@ -5696,11 +5789,16 @@ function WAREHOUSE:_SpawnAssetRequest(Request)
elseif asset.category==Group.Category.SHIP then elseif asset.category==Group.Category.SHIP then
-- Spawn naval assets. -- Spawn naval assets.
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.portzone) _group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.portzone, Request.lateActivation)
else else
self:E(self.lid.."ERROR: Unknown asset category!") self:E(self.lid.."ERROR: Unknown asset category!")
end end
-- Trigger event.
if _group then
self:__AssetSpawned(0.01, _group, asset, Request)
end
end end
@@ -5713,9 +5811,9 @@ end
-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned.
-- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias.
-- @param Core.Zone#ZONE spawnzone Zone where the assets should be spawned. -- @param Core.Zone#ZONE spawnzone Zone where the assets should be spawned.
-- @param #boolean aioff If true, AI of ground units are set to off. -- @param #boolean lateactivated If true, groups are spawned late activated.
-- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned.
function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aioff) function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, lateactivated)
if asset and (asset.category==Group.Category.GROUND or asset.category==Group.Category.SHIP or asset.category==Group.Category.TRAIN) then if asset and (asset.category==Group.Category.GROUND or asset.category==Group.Category.SHIP or asset.category==Group.Category.TRAIN) then
@@ -5758,6 +5856,9 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aiof
end end
end end
-- Late activation.
template.lateActivation=lateactivated
template.route.points[1].x = coord.x template.route.points[1].x = coord.x
template.route.points[1].y = coord.z template.route.points[1].y = coord.z
@@ -5769,14 +5870,6 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aiof
-- Spawn group. -- Spawn group.
local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP
-- Activate group. Should only be necessary for late activated groups.
--group:Activate()
-- Switch AI off if desired. This works only for ground and naval groups.
if aioff then
group:SetAIOff()
end
return group return group
end end
@@ -5790,8 +5883,9 @@ end
-- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias.
-- @param #table parking Parking data for this asset. -- @param #table parking Parking data for this asset.
-- @param #boolean uncontrolled Spawn aircraft in uncontrolled state. -- @param #boolean uncontrolled Spawn aircraft in uncontrolled state.
-- @param #boolean lateactivated If true, groups are spawned late activated.
-- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned.
function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrolled) function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrolled, lateactivated)
if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then
@@ -6043,18 +6137,10 @@ function WAREHOUSE:_RouteGround(group, request)
end end
for n,wp in ipairs(Waypoints) do for n,wp in ipairs(Waypoints) do
env.info(n)
local tf=self:_SimpleTaskFunctionWP("warehouse:_PassingWaypoint",group, n, #Waypoints) local tf=self:_SimpleTaskFunctionWP("warehouse:_PassingWaypoint",group, n, #Waypoints)
group:SetTaskWaypoint(wp, tf) group:SetTaskWaypoint(wp, tf)
end end
-- Task function triggering the arrived event at the last waypoint.
--local TaskFunction = self:_SimpleTaskFunction("warehouse:_Arrived", group)
-- Put task function on last waypoint.
--local Waypoint = Waypoints[#Waypoints]
--group:SetTaskWaypoint(Waypoint, TaskFunction)
-- Route group to destination. -- Route group to destination.
group:Route(Waypoints, 1) group:Route(Waypoints, 1)
@@ -6132,9 +6218,11 @@ function WAREHOUSE:_RouteAir(aircraft)
self:T2(self.lid..string.format("RouteAir aircraft group %s alive=%s", aircraft:GetName(), tostring(aircraft:IsAlive()))) self:T2(self.lid..string.format("RouteAir aircraft group %s alive=%s", aircraft:GetName(), tostring(aircraft:IsAlive())))
-- Give start command to activate uncontrolled aircraft within the next 60 seconds. -- Give start command to activate uncontrolled aircraft within the next 60 seconds.
local starttime=math.random(60) if not self.flightcontrol then
local starttime=math.random(60)
aircraft:StartUncontrolled(starttime) aircraft:StartUncontrolled(starttime)
end
-- Debug info. -- Debug info.
self:T2(self.lid..string.format("RouteAir aircraft group %s alive=%s (after start command)", aircraft:GetName(), tostring(aircraft:IsAlive()))) self:T2(self.lid..string.format("RouteAir aircraft group %s alive=%s (after start command)", aircraft:GetName(), tostring(aircraft:IsAlive())))
@@ -6281,41 +6369,12 @@ function WAREHOUSE:_OnEventBirth(EventData)
local request=self:GetRequestByID(rid) local request=self:GetRequestByID(rid)
if asset and request then if asset and request then
-- Debug message. -- Debug message.
self:T(self.lid..string.format("Warehouse %s captured event birth of request ID=%d, asset ID=%d, unit %s spawned=%s", self.alias, request.uid, asset.uid, EventData.IniUnitName, tostring(asset.spawned))) self:T(self.lid..string.format("Warehouse %s captured event birth of request ID=%d, asset ID=%d, unit %s spawned=%s", self.alias, request.uid, asset.uid, EventData.IniUnitName, tostring(asset.spawned)))
-- Set born to true. -- Set born to true.
request.born=true request.born=true
-- Birth is triggered for each unit. We need to make sure not to call this too often!
if not asset.spawned then
-- Remove asset from stock.
self:_DeleteStockItem(asset)
-- Set spawned switch.
asset.spawned=true
asset.spawngroupname=group:GetName()
-- Add group.
if asset.iscargo==true then
request.cargogroupset=request.cargogroupset or SET_GROUP:New()
request.cargogroupset:AddGroup(group)
else
request.transportgroupset=request.transportgroupset or SET_GROUP:New()
request.transportgroupset:AddGroup(group)
end
-- Set warehouse state.
group:SetState(group, "WAREHOUSE", self)
-- Asset spawned FSM function.
--self:__AssetSpawned(1, group, asset, request)
--env.info(string.format("FF asset spawned %s, %s", asset.spawngroupname, EventData.IniUnitName))
self:AssetSpawned(group, asset, request)
end
else else
self:E(self.lid..string.format("ERROR: Either asset AID=%s or request RID=%s are nil in event birth of unit %s", tostring(aid), tostring(rid), tostring(EventData.IniUnitName))) self:E(self.lid..string.format("ERROR: Either asset AID=%s or request RID=%s are nil in event birth of unit %s", tostring(aid), tostring(rid), tostring(EventData.IniUnitName)))
@@ -7016,10 +7075,9 @@ function WAREHOUSE:_CheckRequestValid(request)
-- Check that both spawn zones are not in water. -- Check that both spawn zones are not in water.
local inwater=self.spawnzone:GetCoordinate():IsSurfaceTypeWater() or request.warehouse.spawnzone:GetCoordinate():IsSurfaceTypeWater() local inwater=self.spawnzone:GetCoordinate():IsSurfaceTypeWater() or request.warehouse.spawnzone:GetCoordinate():IsSurfaceTypeWater()
if inwater then if inwater and not request.lateActivation then
self:E("ERROR: Incorrect request. Ground asset requested but at least one spawn zone is in water!") self:E("ERROR: Incorrect request. Ground asset requested but at least one spawn zone is in water!")
--valid=false return false
valid=false
end end
-- No ground assets directly to or from ships. -- No ground assets directly to or from ships.
@@ -7641,7 +7699,7 @@ function WAREHOUSE:_SimpleTaskFunction(Function, group)
local DCSScript = {} local DCSScript = {}
DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(\"%s\") ', groupname) -- The group that executes the task function. Very handy with the "...". DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(\"%s\") ', groupname) -- The group that executes the task function. Very handy with the "...".
if self.isunit then if self.isUnit then
DCSScript[#DCSScript+1] = string.format("local mywarehouse = UNIT:FindByName(\"%s\") ", warehouse) -- The unit that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format("local mywarehouse = UNIT:FindByName(\"%s\") ", warehouse) -- The unit that holds the warehouse self object.
else else
DCSScript[#DCSScript+1] = string.format("local mywarehouse = STATIC:FindByName(\"%s\") ", warehouse) -- The static that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format("local mywarehouse = STATIC:FindByName(\"%s\") ", warehouse) -- The static that holds the warehouse self object.
@@ -7672,7 +7730,7 @@ function WAREHOUSE:_SimpleTaskFunctionWP(Function, group, n, N)
local DCSScript = {} local DCSScript = {}
DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(\"%s\") ', groupname) -- The group that executes the task function. Very handy with the "...". DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(\"%s\") ', groupname) -- The group that executes the task function. Very handy with the "...".
if self.isunit then if self.isUnit then
DCSScript[#DCSScript+1] = string.format("local mywarehouse = UNIT:FindByName(\"%s\") ", warehouse) -- The unit that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format("local mywarehouse = UNIT:FindByName(\"%s\") ", warehouse) -- The unit that holds the warehouse self object.
else else
DCSScript[#DCSScript+1] = string.format("local mywarehouse = STATIC:FindByName(\"%s\") ", warehouse) -- The static that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format("local mywarehouse = STATIC:FindByName(\"%s\") ", warehouse) -- The static that holds the warehouse self object.
@@ -7841,7 +7899,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets)
local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot
-- Check correct terminal type for asset. We don't want helos in shelters etc. -- Check correct terminal type for asset. We don't want helos in shelters etc.
if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) and self:_CheckParkingValid(parkingspot, airbase) and airbase:_CheckParkingLists(parkingspot.TerminalID) then if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) and self:_CheckParkingValid(parkingspot) and self:_CheckParkingAsset(parkingspot, asset) and airbase:_CheckParkingLists(parkingspot.TerminalID) then
-- Coordinate of the parking spot. -- Coordinate of the parking spot.
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
@@ -8045,10 +8103,10 @@ function WAREHOUSE:_GetIDsFromGroup(group)
end end
-- Debug info -- Debug info
self:T(self.lid..string.format("Group Name = %s", tostring(name))) self:T3(self.lid..string.format("Group Name = %s", tostring(name)))
self:T(self.lid..string.format("Warehouse ID = %s", tostring(wid))) self:T3(self.lid..string.format("Warehouse ID = %s", tostring(wid)))
self:T(self.lid..string.format("Asset ID = %s", tostring(aid))) self:T3(self.lid..string.format("Asset ID = %s", tostring(aid)))
self:T(self.lid..string.format("Request ID = %s", tostring(rid))) self:T3(self.lid..string.format("Request ID = %s", tostring(rid)))
return wid,aid,rid return wid,aid,rid
else else
@@ -8740,7 +8798,7 @@ end
-- @param #number duration Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed. -- @param #number duration Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed.
function WAREHOUSE:_DebugMessage(text, duration) function WAREHOUSE:_DebugMessage(text, duration)
duration=duration or 20 duration=duration or 20
if duration>0 then if self.Debug and duration>0 then
MESSAGE:New(text, duration):ToAllIf(self.Debug) MESSAGE:New(text, duration):ToAllIf(self.Debug)
end end
self:T(self.lid..text) self:T(self.lid..text)
@@ -9047,11 +9105,11 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination)
-- Hot start. -- Hot start.
if asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TakeOffParkingHot then if asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TakeOffParkingHot then
env.info("FF hot") --env.info("FF hot")
_type=COORDINATE.WaypointType.TakeOffParkingHot _type=COORDINATE.WaypointType.TakeOffParkingHot
_action=COORDINATE.WaypointAction.FromParkingAreaHot _action=COORDINATE.WaypointAction.FromParkingAreaHot
else else
env.info("FF cold") --env.info("FF cold")
end end
+8
View File
@@ -82,11 +82,19 @@ __Moose.Include( 'Scripts/Moose/Ops/OpsGroup.lua' )
__Moose.Include( 'Scripts/Moose/Ops/FlightGroup.lua' ) __Moose.Include( 'Scripts/Moose/Ops/FlightGroup.lua' )
__Moose.Include( 'Scripts/Moose/Ops/NavyGroup.lua' ) __Moose.Include( 'Scripts/Moose/Ops/NavyGroup.lua' )
__Moose.Include( 'Scripts/Moose/Ops/ArmyGroup.lua' ) __Moose.Include( 'Scripts/Moose/Ops/ArmyGroup.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Cohort.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Squadron.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Squadron.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Platoon.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Legion.lua' )
__Moose.Include( 'Scripts/Moose/Ops/AirWing.lua' ) __Moose.Include( 'Scripts/Moose/Ops/AirWing.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Brigade.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Intelligence.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Intelligence.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Commander.lua' )
__Moose.Include( 'Scripts/Moose/Ops/OpsTransport.lua' )
__Moose.Include( 'Scripts/Moose/Ops/CSAR.lua' ) __Moose.Include( 'Scripts/Moose/Ops/CSAR.lua' )
__Moose.Include( 'Scripts/Moose/Ops/CTLD.lua' ) __Moose.Include( 'Scripts/Moose/Ops/CTLD.lua' )
__Moose.Include( 'Scripts/Moose/Ops/OpsZone.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Chief.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Balancer.lua' )
__Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' ) __Moose.Include( 'Scripts/Moose/AI/AI_Air.lua' )
File diff suppressed because it is too large Load Diff
+31 -26
View File
@@ -3628,36 +3628,39 @@ function AIRBOSS:_CheckAIStatus()
-- Unit -- Unit
local unit=element.unit local unit=element.unit
-- Get lineup and distance to carrier. if unit and unit:IsAlive() then
local lineup=self:_Lineup(unit, true)
local unitcoord=unit:GetCoord() -- Get lineup and distance to carrier.
local lineup=self:_Lineup(unit, true)
local dist=unitcoord:Get2DDistance(self:GetCoord()) local unitcoord=unit:GetCoord()
-- Distance in NM. local dist=unitcoord:Get2DDistance(self:GetCoord())
local distance=UTILS.MetersToNM(dist)
-- Altitude in ft. -- Distance in NM.
local alt=UTILS.MetersToFeet(unitcoord.y) local distance=UTILS.MetersToNM(dist)
-- Check if parameters are right and flight is in the groove. -- Altitude in ft.
if lineup<2 and distance<=0.75 and alt<500 and not element.ballcall then local alt=UTILS.MetersToFeet(unitcoord.y)
-- Paddles: Call the ball! -- Check if parameters are right and flight is in the groove.
self:RadioTransmission(self.LSORadio, self.LSOCall.CALLTHEBALL, nil, nil, nil, true) if lineup<2 and distance<=0.75 and alt<500 and not element.ballcall then
-- Pilot: "405, Hornet Ball, 3.2" -- Paddles: Call the ball!
self:_LSOCallAircraftBall(element.onboard,self:_GetACNickname(unit:GetTypeName()), self:_GetFuelState(unit)/1000) self:RadioTransmission(self.LSORadio, self.LSOCall.CALLTHEBALL, nil, nil, nil, true)
-- Paddles: Roger ball after 0.5 seconds. -- Pilot: "405, Hornet Ball, 3.2"
self:RadioTransmission(self.LSORadio, self.LSOCall.ROGERBALL, nil, nil, 0.5, true) self:_LSOCallAircraftBall(element.onboard,self:_GetACNickname(unit:GetTypeName()), self:_GetFuelState(unit)/1000)
-- Flight element called the ball. -- Paddles: Roger ball after 0.5 seconds.
element.ballcall=true self:RadioTransmission(self.LSORadio, self.LSOCall.ROGERBALL, nil, nil, 0.5, true)
-- This is for the whole flight. Maybe we need it. -- Flight element called the ball.
flight.ballcall=true element.ballcall=true
-- This is for the whole flight. Maybe we need it.
flight.ballcall=true
end
end end
end end
@@ -6263,6 +6266,11 @@ function AIRBOSS:_ScanCarrierZone()
-- Get flight group if possible. -- Get flight group if possible.
local knownflight=self:_GetFlightFromGroupInQueue(group, self.flights) local knownflight=self:_GetFlightFromGroupInQueue(group, self.flights)
-- Unknown new AI flight. Create a new flight group.
if not knownflight and not self:_IsHuman(group) then
knownflight=self:_CreateFlightGroup(group)
end
-- Get aircraft type name. -- Get aircraft type name.
local actype=group:GetTypeName() local actype=group:GetTypeName()
@@ -6276,10 +6284,10 @@ function AIRBOSS:_ScanCarrierZone()
local putintomarshal=false local putintomarshal=false
-- Get flight group. -- Get flight group.
local flight=_DATABASE:GetFlightGroup(groupname) local flight=_DATABASE:GetOpsGroup(groupname)
if flight and flight:IsInbound() and flight.destbase:GetName()==self.carrier:GetName() then if flight and flight:IsInbound() and flight.destbase:GetName()==self.carrier:GetName() then
if flight.ishelo then if flight.isHelo then
else else
putintomarshal=true putintomarshal=true
end end
@@ -6320,10 +6328,7 @@ function AIRBOSS:_ScanCarrierZone()
else else
-- Unknown new AI flight. Create a new flight group.
if not self:_IsHuman(group) then
self:_CreateFlightGroup(group)
end
end end
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+475
View File
@@ -0,0 +1,475 @@
--- **Ops** - Brigade Warehouse.
--
-- **Main Features:**
--
-- * Manage platoons
-- * Carry out ARTY and PATROLZONE missions (AUFTRAG)
-- * Define rearming zones
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- @module Ops.Brigade
-- @image OPS_Brigade.png
--- BRIGADE class.
-- @type BRIGADE
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #table rearmingZones Rearming zones. Each element is of type `#BRIGADE.SupplyZone`.
-- @field #table refuellingZones Refuelling zones. Each element is of type `#BRIGADE.SupplyZone`.
-- @field Core.Set#SET_ZONE retreatZones Retreat zone set.
-- @extends Ops.Legion#LEGION
--- Be surprised!
--
-- ===
--
-- # The BRIGADE Concept
--
-- An BRIGADE consists of one or multiple PLATOONs. These platoons "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed.
--
--
-- @field #BRIGADE
BRIGADE = {
ClassName = "BRIGADE",
verbose = 0,
rearmingZones = {},
refuellingZones = {},
}
--- Supply Zone.
-- @type BRIGADE.SupplyZone
-- @field Core.Zone#ZONE zone The zone.
-- @field Ops.Auftrag#AUFTRAG mission Mission assigned to supply ammo or fuel.
-- @field #boolean markerOn If `true`, marker is on.
-- @field Wrapper.Marker#MARKER marker F10 marker.
--- BRIGADE class version.
-- @field #string version
BRIGADE.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Spawn when hosting warehouse is a ship or oil rig or gas platform.
-- TODO: Rearming zones.
-- TODO: Retreat zones.
-- DONE: Add weapon range.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new BRIGADE class object.
-- @param #BRIGADE self
-- @param #string WarehouseName Name of the warehouse STATIC or UNIT object representing the warehouse.
-- @param #string BrigadeName Name of the brigade.
-- @return #BRIGADE self
function BRIGADE:New(WarehouseName, BrigadeName)
-- Inherit everything from LEGION class.
local self=BASE:Inherit(self, LEGION:New(WarehouseName, BrigadeName)) -- #BRIGADE
-- Nil check.
if not self then
BASE:E(string.format("ERROR: Could not find warehouse %s!", WarehouseName))
return nil
end
-- Set some string id for output to DCS.log file.
self.lid=string.format("BRIGADE %s | ", self.alias)
-- Defaults
self:SetRetreatZones()
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "ArmyOnMission", "*") -- An ARMYGROUP was send on a Mission (AUFTRAG).
------------------------
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Start". Starts the BRIGADE. Initializes parameters and starts event handlers.
-- @function [parent=#BRIGADE] Start
-- @param #BRIGADE self
--- Triggers the FSM event "Start" after a delay. Starts the BRIGADE. Initializes parameters and starts event handlers.
-- @function [parent=#BRIGADE] __Start
-- @param #BRIGADE self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop". Stops the BRIGADE and all its event handlers.
-- @param #BRIGADE self
--- Triggers the FSM event "Stop" after a delay. Stops the BRIGADE and all its event handlers.
-- @function [parent=#BRIGADE] __Stop
-- @param #BRIGADE self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "ArmyOnMission".
-- @function [parent=#BRIGADE] ArmyOnMission
-- @param #BRIGADE self
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
--- Triggers the FSM event "ArmyOnMission" after a delay.
-- @function [parent=#BRIGADE] __ArmyOnMission
-- @param #BRIGADE self
-- @param #number delay Delay in seconds.
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
--- On after "ArmyOnMission" event.
-- @function [parent=#BRIGADE] OnAfterArmyOnMission
-- @param #BRIGADE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add a platoon to the brigade.
-- @param #BRIGADE self
-- @param Ops.Platoon#PLATOON Platoon The platoon object.
-- @return #BRIGADE self
function BRIGADE:AddPlatoon(Platoon)
-- Add platoon to brigade.
table.insert(self.cohorts, Platoon)
-- Add assets to platoon.
self:AddAssetToPlatoon(Platoon, Platoon.Ngroups)
-- Set brigade of platoon.
Platoon:SetBrigade(self)
-- Start platoon.
if Platoon:IsStopped() then
Platoon:Start()
end
return self
end
--- Add asset group(s) to platoon.
-- @param #BRIGADE self
-- @param Ops.Platoon#PLATOON Platoon The platoon object.
-- @param #number Nassets Number of asset groups to add.
-- @return #BRIGADE self
function BRIGADE:AddAssetToPlatoon(Platoon, Nassets)
if Platoon then
-- Get the template group of the platoon.
local Group=GROUP:FindByName(Platoon.templatename)
if Group then
-- Debug text.
local text=string.format("Adding asset %s to platoon %s", Group:GetName(), Platoon.name)
self:T(self.lid..text)
-- Add assets to airwing warehouse.
self:AddAsset(Group, Nassets, nil, nil, nil, nil, Platoon.skill, Platoon.livery, Platoon.name)
else
self:E(self.lid.."ERROR: Group does not exist!")
end
else
self:E(self.lid.."ERROR: Platoon does not exit!")
end
return self
end
--- Define a set of retreat zones.
-- @param #BRIGADE self
-- @param Core.Set#SET_ZONE RetreatZoneSet Set of retreat zones.
-- @return #BRIGADE self
function BRIGADE:SetRetreatZones(RetreatZoneSet)
self.retreatZones=RetreatZoneSet or SET_ZONE:New()
return self
end
--- Add a retreat zone.
-- @param #BRIGADE self
-- @param Core.Zone#ZONE RetreatZone Retreat zone.
-- @return #BRIGADE self
function BRIGADE:AddRetreatZone(RetreatZone)
self.retreatZones:AddZone(RetreatZone)
return self
end
--- Get retreat zones.
-- @param #BRIGADE self
-- @return Core.Set#SET_ZONE Set of retreat zones.
function BRIGADE:GetRetreatZones()
return self.retreatZones
end
--- Add a rearming zone.
-- @param #BRIGADE self
-- @param Core.Zone#ZONE RearmingZone Rearming zone.
-- @return #BRIGADE.SupplyZone The rearming zone data.
function BRIGADE:AddRearmingZone(RearmingZone)
local rearmingzone={} --#BRIGADE.SupplyZone
rearmingzone.zone=RearmingZone
rearmingzone.mission=nil
rearmingzone.marker=MARKER:New(rearmingzone.zone:GetCoordinate(), "Rearming Zone"):ToCoalition(self:GetCoalition())
table.insert(self.rearmingZones, rearmingzone)
return rearmingzone
end
--- Add a refuelling zone.
-- @param #BRIGADE self
-- @param Core.Zone#ZONE RefuellingZone Refuelling zone.
-- @return #BRIGADE.SupplyZone The refuelling zone data.
function BRIGADE:AddRefuellingZone(RefuellingZone)
local supplyzone={} --#BRIGADE.SupplyZone
supplyzone.zone=RefuellingZone
supplyzone.mission=nil
supplyzone.marker=MARKER:New(supplyzone.zone:GetCoordinate(), "Refuelling Zone"):ToCoalition(self:GetCoalition())
table.insert(self.rearmingZones, supplyzone)
return supplyzone
end
--- Get platoon by name.
-- @param #BRIGADE self
-- @param #string PlatoonName Name of the platoon.
-- @return Ops.Platoon#PLATOON The Platoon object.
function BRIGADE:GetPlatoon(PlatoonName)
local platoon=self:_GetCohort(PlatoonName)
return platoon
end
--- Get platoon of an asset.
-- @param #BRIGADE self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The platoon asset.
-- @return Ops.Platoon#PLATOON The platoon object.
function BRIGADE:GetPlatoonOfAsset(Asset)
local platoon=self:GetPlatoon(Asset.squadname)
return platoon
end
--- Remove asset from platoon.
-- @param #BRIGADE self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The platoon asset.
function BRIGADE:RemoveAssetFromPlatoon(Asset)
local platoon=self:GetPlatoonOfAsset(Asset)
if platoon then
platoon:DelAsset(Asset)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Start BRIGADE FSM.
-- @param #BRIGADE self
function BRIGADE:onafterStart(From, Event, To)
-- Start parent Warehouse.
self:GetParent(self, BRIGADE).onafterStart(self, From, Event, To)
-- Info.
self:I(self.lid..string.format("Starting BRIGADE v%s", BRIGADE.version))
end
--- Update status.
-- @param #BRIGADE self
function BRIGADE:onafterStatus(From, Event, To)
-- Status of parent Warehouse.
self:GetParent(self).onafterStatus(self, From, Event, To)
-- FSM state.
local fsmstate=self:GetState()
----------------
-- Transport ---
----------------
self:CheckTransportQueue()
--------------
-- Mission ---
--------------
-- Check if any missions should be cancelled.
self:CheckMissionQueue()
---------------------
-- Rearming Zones ---
---------------------
for _,_rearmingzone in pairs(self.rearmingZones) do
local rearmingzone=_rearmingzone --#BRIGADE.SupplyZone
if (not rearmingzone.mission) or rearmingzone.mission:IsOver() then
rearmingzone.mission=AUFTRAG:NewAMMOSUPPLY(rearmingzone.zone)
self:AddMission(rearmingzone.mission)
end
end
-----------------------
-- Refuelling Zones ---
-----------------------
-- Check refuelling zones.
for _,_supplyzone in pairs(self.refuellingZones) do
local supplyzone=_supplyzone --#BRIGADE.SupplyZone
-- Check if mission is nil or over.
if (not supplyzone.mission) or supplyzone.mission:IsOver() then
supplyzone.mission=AUFTRAG:NewFUELSUPPLY(supplyzone.zone)
self:AddMission(supplyzone.mission)
end
end
-----------
-- Info ---
-----------
-- General info:
if self.verbose>=1 then
-- Count missions not over yet.
local Nmissions=self:CountMissionsInQueue()
-- Asset count.
local Npq, Np, Nq=self:CountAssetsOnMission()
-- Asset string.
local assets=string.format("%d [OnMission: Total=%d, Active=%d, Queued=%d]", self:CountAssets(), Npq, Np, Nq)
-- Output.
local text=string.format("%s: Missions=%d, Platoons=%d, Assets=%s", fsmstate, Nmissions, #self.cohorts, assets)
self:I(self.lid..text)
end
------------------
-- Mission Info --
------------------
if self.verbose>=2 then
local text=string.format("Missions Total=%d:", #self.missionqueue)
for i,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
local prio=string.format("%d/%s", mission.prio, tostring(mission.importance)) ; if mission.urgent then prio=prio.." (!)" end
local assets=string.format("%d/%d", mission:CountOpsGroups(), mission.Nassets or 0)
local target=string.format("%d/%d Damage=%.1f", mission:CountMissionTargets(), mission:GetTargetInitialNumber(), mission:GetTargetDamage())
text=text..string.format("\n[%d] %s %s: Status=%s, Prio=%s, Assets=%s, Targets=%s", i, mission.name, mission.type, mission.status, prio, assets, target)
end
self:I(self.lid..text)
end
--------------------
-- Transport Info --
--------------------
if self.verbose>=2 then
local text=string.format("Transports Total=%d:", #self.transportqueue)
for i,_transport in pairs(self.transportqueue) do
local transport=_transport --Ops.OpsTransport#OPSTRANSPORT
local prio=string.format("%d/%s", transport.prio, tostring(transport.importance)) ; if transport.urgent then prio=prio.." (!)" end
local carriers=string.format("Ncargo=%d/%d, Ncarriers=%d", transport.Ncargo, transport.Ndelivered, transport.Ncarrier)
text=text..string.format("\n[%d] UID=%d: Status=%s, Prio=%s, Cargo: %s", i, transport.uid, transport:GetState(), prio, carriers)
end
self:I(self.lid..text)
end
-------------------
-- Platoon Info --
-------------------
if self.verbose>=3 then
local text="Platoons:"
for i,_platoon in pairs(self.cohorts) do
local platoon=_platoon --Ops.Platoon#PLATOON
local callsign=platoon.callsignName and UTILS.GetCallsignName(platoon.callsignName) or "N/A"
local modex=platoon.modex and platoon.modex or -1
local skill=platoon.skill and tostring(platoon.skill) or "N/A"
-- Platoon text.
text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s", platoon.name, platoon:GetState(), platoon.aircrafttype, platoon:CountAssets(true), #platoon.assets, callsign, modex, skill)
end
self:I(self.lid..text)
end
-------------------
-- Rearming Info --
-------------------
if self.verbose>=4 then
local text="Rearming Zones:"
for i,_rearmingzone in pairs(self.rearmingZones) do
local rearmingzone=_rearmingzone --#BRIGADE.SupplyZone
-- Info text.
text=text..string.format("\n* %s: Mission status=%s, suppliers=%d", rearmingzone.zone:GetName(), rearmingzone.mission:GetState(), rearmingzone.mission:CountOpsGroups())
end
self:I(self.lid..text)
end
-------------------
-- Refuelling Info --
-------------------
if self.verbose>=4 then
local text="Refuelling Zones:"
for i,_refuellingzone in pairs(self.refuellingZones) do
local refuellingzone=_refuellingzone --#BRIGADE.SupplyZone
-- Info text.
text=text..string.format("\n* %s: Mission status=%s, suppliers=%d", refuellingzone.zone:GetName(), refuellingzone.mission:GetState(), refuellingzone.mission:CountOpsGroups())
end
self:I(self.lid..text)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "ArmyOnMission".
-- @param #BRIGADE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup Ops army group on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The requested mission.
function BRIGADE:onafterArmyOnMission(From, Event, To, ArmyGroup, Mission)
-- Debug info.
self:T(self.lid..string.format("Group %s on %s mission %s", ArmyGroup:GetName(), Mission:GetType(), Mission:GetName()))
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+202
View File
@@ -0,0 +1,202 @@
--- **Ops** - Brigade Platoon.
--
-- **Main Features:**
--
-- * Set parameters like livery, skill valid for all platoon members.
-- * Define modex and callsigns.
-- * Define mission types, this platoon can perform (see Ops.Auftrag#AUFTRAG).
-- * Pause/unpause platoon operations.
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Ops.Platoon
-- @image OPS_Platoon.png
--- PLATOON class.
-- @type PLATOON
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity level.
-- @field Ops.OpsGroup#OPSGROUP.WeaponData weaponData Weapon data table with key=BitType.
-- @extends Ops.Cohort#COHORT
--- *Some cool cohort quote* -- Known Author
--
-- ===
--
-- # The PLATOON Concept
--
-- A PLATOON is essential part of an BRIGADE.
--
--
--
-- @field #PLATOON
PLATOON = {
ClassName = "PLATOON",
verbose = 0,
weaponData = {},
}
--- PLATOON class version.
-- @field #string version
PLATOON.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add weapon data.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new PLATOON object and start the FSM.
-- @param #PLATOON self
-- @param #string TemplateGroupName Name of the template group.
-- @param #number Ngroups Number of asset groups of this platoon. Default 3.
-- @param #string PlatoonName Name of the platoon, e.g. "VFA-37".
-- @return #PLATOON self
function PLATOON:New(TemplateGroupName, Ngroups, PlatoonName)
-- Inherit everything from COHORT class.
local self=BASE:Inherit(self, COHORT:New(TemplateGroupName, Ngroups, PlatoonName)) -- #PLATOON
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Platoon specific user functions.
--- Set brigade of this platoon.
-- @param #PLATOON self
-- @param Ops.Brigade#BRIGADE Brigade The brigade.
-- @return #PLATOON self
function PLATOON:SetBrigade(Brigade)
self.legion=Brigade
return self
end
--- Get brigade of this platoon.
-- @param #PLATOON self
-- @return Ops.Brigade#BRIGADE The brigade.
function PLATOON:GetBrigade()
return self.legion
end
--- Add a weapon range for ARTY auftrag.
-- @param #PLATOON self
-- @param #number RangeMin Minimum range in nautical miles. Default 0 NM.
-- @param #number RangeMax Maximum range in nautical miles. Default 10 NM.
-- @param #number BitType Bit mask of weapon type for which the given min/max ranges apply. Default is `ENUMS.WeaponFlag.Auto`, i.e. for all weapon types.
-- @return #PLATOON self
function PLATOON:AddWeaponRange(RangeMin, RangeMax, BitType)
RangeMin=UTILS.NMToMeters(RangeMin or 0)
RangeMax=UTILS.NMToMeters(RangeMax or 10)
local weapon={} --Ops.OpsGroup#OPSGROUP.WeaponData
weapon.BitType=BitType or ENUMS.WeaponFlag.Auto
weapon.RangeMax=RangeMax
weapon.RangeMin=RangeMin
self.weaponData=self.weaponData or {}
self.weaponData[tostring(weapon.BitType)]=weapon
-- Debug info.
self:T(self.lid..string.format("Adding weapon data: Bit=%s, Rmin=%d m, Rmax=%d m", tostring(weapon.BitType), weapon.RangeMin, weapon.RangeMax))
if self.verbose>=2 then
local text="Weapon data:"
for _,_weapondata in pairs(self.weaponData) do
local weapondata=_weapondata
text=text..string.format("\n- Bit=%s, Rmin=%d m, Rmax=%d m", tostring(weapondata.BitType), weapondata.RangeMin, weapondata.RangeMax)
end
self:I(self.lid..text)
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after Start event. Starts the FLIGHTGROUP FSM and event handlers.
-- @param #PLATOON self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function PLATOON:onafterStart(From, Event, To)
-- Short info.
local text=string.format("Starting %s v%s %s", self.ClassName, self.version, self.name)
self:I(self.lid..text)
-- Start the status monitoring.
self:__Status(-1)
end
--- On after "Status" event.
-- @param #PLATOON self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function PLATOON:onafterStatus(From, Event, To)
if self.verbose>=1 then
-- FSM state.
local fsmstate=self:GetState()
local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName) or "N/A"
local modex=self.modex and self.modex or -1
local skill=self.skill and tostring(self.skill) or "N/A"
local NassetsTot=#self.assets
local NassetsInS=self:CountAssets(true)
local NassetsQP=0 ; local NassetsP=0 ; local NassetsQ=0
if self.legion then
NassetsQP, NassetsP, NassetsQ=self.legion:CountAssetsOnMission(nil, self)
end
-- Short info.
local text=string.format("%s [Type=%s, Call=%s, Modex=%d, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]",
fsmstate, self.aircrafttype, callsign, modex, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ)
self:I(self.lid..text)
-- Weapon data info.
if self.verbose>=3 and self.weaponData then
local text="Weapon Data:"
for bit,_weapondata in pairs(self.weaponData) do
local weapondata=_weapondata --Ops.OpsGroup#OPSGROUP.WeaponData
text=text..string.format("\n- Bit=%s: Rmin=%.1f km, Rmax=%.1f km", bit, weapondata.RangeMin/1000, weapondata.RangeMax/1000)
end
self:I(self.lid..text)
end
-- Check if group has detected any units.
self:_CheckAssetStatus()
end
if not self:IsStopped() then
self:__Status(-60)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Misc functions.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+29 -822
View File
@@ -36,7 +36,6 @@
-- @field #number modexcounter Counter to incease modex number for assets. -- @field #number modexcounter Counter to incease modex number for assets.
-- @field #string callsignName Callsign name. -- @field #string callsignName Callsign name.
-- @field #number callsigncounter Counter to increase callsign names for new assets. -- @field #number callsigncounter Counter to increase callsign names for new assets.
-- @field Ops.AirWing#AIRWING airwing The AIRWING object the squadron belongs to.
-- @field #number Ngroups Number of asset flight groups this squadron has. -- @field #number Ngroups Number of asset flight groups this squadron has.
-- @field #number engageRange Mission range in meters. -- @field #number engageRange Mission range in meters.
-- @field #string attribute Generalized attribute of the squadron template group. -- @field #string attribute Generalized attribute of the squadron template group.
@@ -46,7 +45,8 @@
-- @field #number radioFreq Radio frequency in MHz the squad uses. -- @field #number radioFreq Radio frequency in MHz the squad uses.
-- @field #number radioModu Radio modulation the squad uses. -- @field #number radioModu Radio modulation the squad uses.
-- @field #number takeoffType Take of type. -- @field #number takeoffType Take of type.
-- @extends Core.Fsm#FSM -- @field #table parkingIDs Parking IDs for this squadron.
-- @extends Ops.Cohort#COHORT
--- *It is unbelievable what a squadron of twelve aircraft did to tip the balance.* -- Adolf Galland --- *It is unbelievable what a squadron of twelve aircraft did to tip the balance.* -- Adolf Galland
-- --
@@ -64,37 +64,23 @@
SQUADRON = { SQUADRON = {
ClassName = "SQUADRON", ClassName = "SQUADRON",
verbose = 0, verbose = 0,
lid = nil,
name = nil,
templatename = nil,
aircrafttype = nil,
assets = {},
missiontypes = {},
repairtime = 0,
maintenancetime= 0,
livery = nil,
skill = nil,
modex = nil, modex = nil,
modexcounter = 0, modexcounter = 0,
callsignName = nil, callsignName = nil,
callsigncounter= 11, callsigncounter= 11,
airwing = nil,
Ngroups = nil,
engageRange = nil,
tankerSystem = nil, tankerSystem = nil,
refuelSystem = nil, refuelSystem = nil,
tacanChannel = {},
} }
--- SQUADRON class version. --- SQUADRON class version.
-- @field #string version -- @field #string version
SQUADRON.version="0.5.2" SQUADRON.version="0.8.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Parking spots for squadrons? -- DONE: Parking spots for squadrons?
-- DONE: Engage radius. -- DONE: Engage radius.
-- DONE: Modex. -- DONE: Modex.
-- DONE: Call signs. -- DONE: Call signs.
@@ -112,97 +98,20 @@ SQUADRON.version="0.5.2"
function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName) function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName)
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #SQUADRON local self=BASE:Inherit(self, COHORT:New(TemplateGroupName, Ngroups, SquadronName)) -- #SQUADRON
-- Name of the template group.
self.templatename=TemplateGroupName
-- Squadron name.
self.name=tostring(SquadronName or TemplateGroupName)
-- Set some string id for output to DCS.log file.
self.lid=string.format("SQUADRON %s | ", self.name)
-- Template group.
self.templategroup=GROUP:FindByName(self.templatename)
-- Check if template group exists.
if not self.templategroup then
self:E(self.lid..string.format("ERROR: Template group %s does not exist!", tostring(self.templatename)))
return nil
end
-- Defaults.
self.Ngroups=Ngroups or 3
self:SetMissionRange()
self:SetSkill(AI.Skill.GOOD)
--self:SetVerbosity(0)
-- Everyone can ORBIT. -- Everyone can ORBIT.
self:AddMissionCapability(AUFTRAG.Type.ORBIT) self:AddMissionCapability(AUFTRAG.Type.ORBIT)
-- Generalized attribute.
self.attribute=self.templategroup:GetAttribute()
-- Aircraft type.
self.aircrafttype=self.templategroup:GetTypeName()
-- Refueling system. -- Refueling system.
self.refuelSystem=select(2, self.templategroup:GetUnit(1):IsRefuelable()) self.refuelSystem=select(2, self.templategroup:GetUnit(1):IsRefuelable())
self.tankerSystem=select(2, self.templategroup:GetUnit(1):IsTanker()) self.tankerSystem=select(2, self.templategroup:GetUnit(1):IsTanker())
-- Start State.
self:SetStartState("Stopped")
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("Stopped", "Start", "OnDuty") -- Start FSM.
self:AddTransition("*", "Status", "*") -- Status update.
self:AddTransition("OnDuty", "Pause", "Paused") -- Pause squadron.
self:AddTransition("Paused", "Unpause", "OnDuty") -- Unpause squadron.
self:AddTransition("*", "Stop", "Stopped") -- Stop squadron.
------------------------ ------------------------
--- Pseudo Functions --- --- Pseudo Functions ---
------------------------ ------------------------
--- Triggers the FSM event "Start". Starts the SQUADRON. Initializes parameters and starts event handlers. -- See COHORT class
-- @function [parent=#SQUADRON] Start
-- @param #SQUADRON self
--- Triggers the FSM event "Start" after a delay. Starts the SQUADRON. Initializes parameters and starts event handlers.
-- @function [parent=#SQUADRON] __Start
-- @param #SQUADRON self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop". Stops the SQUADRON and all its event handlers.
-- @param #SQUADRON self
--- Triggers the FSM event "Stop" after a delay. Stops the SQUADRON and all its event handlers.
-- @function [parent=#SQUADRON] __Stop
-- @param #SQUADRON self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Status".
-- @function [parent=#SQUADRON] Status
-- @param #SQUADRON self
--- Triggers the FSM event "Status" after a delay.
-- @function [parent=#SQUADRON] __Status
-- @param #SQUADRON self
-- @param #number delay Delay in seconds.
-- Debug trace.
if false then
BASE:TraceOnOff(true)
BASE:TraceClass(self.ClassName)
BASE:TraceLevel(1)
end
return self return self
end end
@@ -211,68 +120,6 @@ end
-- User functions -- User functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set livery painted on all squadron aircraft.
-- Note that the livery name in general is different from the name shown in the mission editor.
--
-- Valid names are the names of the **livery directories**. Check out the folder in your DCS installation for:
--
-- * Full modules: `DCS World OpenBeta\CoreMods\aircraft\<Aircraft Type>\Liveries\<Aircraft Type>\<Livery Name>`
-- * AI units: `DCS World OpenBeta\Bazar\Liveries\<Aircraft Type>\<Livery Name>`
--
-- The folder name `<Livery Name>` is the string you want.
--
-- Or personal liveries you have installed somewhere in your saved games folder.
--
-- @param #SQUADRON self
-- @param #string LiveryName Name of the livery.
-- @return #SQUADRON self
function SQUADRON:SetLivery(LiveryName)
self.livery=LiveryName
return self
end
--- Set skill level of all squadron team members.
-- @param #SQUADRON self
-- @param #string Skill Skill of all flights.
-- @usage mysquadron:SetSkill(AI.Skill.EXCELLENT)
-- @return #SQUADRON self
function SQUADRON:SetSkill(Skill)
self.skill=Skill
return self
end
--- Set verbosity level.
-- @param #SQUADRON self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #SQUADRON self
function SQUADRON:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Set turnover and repair time. If an asset returns from a mission to the airwing, it will need some time until the asset is available for further missions.
-- @param #SQUADRON self
-- @param #number MaintenanceTime Time in minutes it takes until a flight is combat ready again. Default is 0 min.
-- @param #number RepairTime Time in minutes it takes to repair a flight for each life point taken. Default is 0 min.
-- @return #SQUADRON self
function SQUADRON:SetTurnoverTime(MaintenanceTime, RepairTime)
self.maintenancetime=MaintenanceTime and MaintenanceTime*60 or 0
self.repairtime=RepairTime and RepairTime*60 or 0
return self
end
--- Set radio frequency and modulation the squad uses.
-- @param #SQUADRON self
-- @param #number Frequency Radio frequency in MHz. Default 251 MHz.
-- @param #number Modulation Radio modulation. Default 0=AM.
-- @usage mysquadron:SetSkill(AI.Skill.EXCELLENT)
-- @return #SQUADRON self
function SQUADRON:SetRadio(Frequency, Modulation)
self.radioFreq=Frequency or 251
self.radioModu=Modulation or radio.modulation.AM
return self
end
--- Set number of units in groups. --- Set number of units in groups.
-- @param #SQUADRON self -- @param #SQUADRON self
-- @param #number nunits Number of units. Must be >=1 and <=4. Default 2. -- @param #number nunits Number of units. Must be >=1 and <=4. Default 2.
@@ -284,6 +131,18 @@ function SQUADRON:SetGrouping(nunits)
return self return self
end end
--- Set valid parking spot IDs. Assets of this squad are only allowed to be spawned at these parking spots. **Note** that the IDs are different from the ones displayed in the mission editor!
-- @param #SQUADRON self
-- @param #table ParkingIDs Table of parking ID numbers or a single `#number`.
-- @return #SQUADRON self
function SQUADRON:SetParkingIDs(ParkingIDs)
if type(ParkingIDs)~="table" then
ParkingIDs={ParkingIDs}
end
self.parkingIDs=ParkingIDs
return self
end
--- Set takeoff type. All assets of this squadron will be spawned with cold (default) or hot engines. --- Set takeoff type. All assets of this squadron will be spawned with cold (default) or hot engines.
-- Spawning on runways is not supported. -- Spawning on runways is not supported.
@@ -302,7 +161,7 @@ function SQUADRON:SetTakeoffType(TakeoffType)
return self return self
end end
--- Set takeoff type cold (default). --- Set takeoff type cold (default). All assets of this squadron will be spawned with engines off (cold).
-- @param #SQUADRON self -- @param #SQUADRON self
-- @return #SQUADRON self -- @return #SQUADRON self
function SQUADRON:SetTakeoffCold() function SQUADRON:SetTakeoffCold()
@@ -310,7 +169,7 @@ function SQUADRON:SetTakeoffCold()
return self return self
end end
--- Set takeoff type hot. --- Set takeoff type hot. All assets of this squadron will be spawned with engines on (hot).
-- @param #SQUADRON self -- @param #SQUADRON self
-- @return #SQUADRON self -- @return #SQUADRON self
function SQUADRON:SetTakeoffHot() function SQUADRON:SetTakeoffHot()
@@ -318,115 +177,6 @@ function SQUADRON:SetTakeoffHot()
return self return self
end end
--- Set mission types this squadron is able to perform.
-- @param #SQUADRON self
-- @param #table MissionTypes Table of mission types. Can also be passed as a #string if only one type.
-- @param #number Performance Performance describing how good this mission can be performed. Higher is better. Default 50. Max 100.
-- @return #SQUADRON self
function SQUADRON:AddMissionCapability(MissionTypes, Performance)
-- Ensure Missiontypes is a table.
if MissionTypes and type(MissionTypes)~="table" then
MissionTypes={MissionTypes}
end
-- Set table.
self.missiontypes=self.missiontypes or {}
for _,missiontype in pairs(MissionTypes) do
-- Check not to add the same twice.
if self:CheckMissionCapability(missiontype, self.missiontypes) then
self:E(self.lid.."WARNING: Mission capability already present! No need to add it twice.")
-- TODO: update performance.
else
local capability={} --Ops.Auftrag#AUFTRAG.Capability
capability.MissionType=missiontype
capability.Performance=Performance or 50
table.insert(self.missiontypes, capability)
end
end
-- Debug info.
self:T2(self.missiontypes)
return self
end
--- Get mission types this squadron is able to perform.
-- @param #SQUADRON self
-- @return #table Table of mission types. Could be empty {}.
function SQUADRON:GetMissionTypes()
local missiontypes={}
for _,Capability in pairs(self.missiontypes) do
local capability=Capability --Ops.Auftrag#AUFTRAG.Capability
table.insert(missiontypes, capability.MissionType)
end
return missiontypes
end
--- Get mission capabilities of this squadron.
-- @param #SQUADRON self
-- @return #table Table of mission capabilities.
function SQUADRON:GetMissionCapabilities()
return self.missiontypes
end
--- Get mission performance for a given type of misson.
-- @param #SQUADRON self
-- @param #string MissionType Type of mission.
-- @return #number Performance or -1.
function SQUADRON:GetMissionPeformance(MissionType)
for _,Capability in pairs(self.missiontypes) do
local capability=Capability --Ops.Auftrag#AUFTRAG.Capability
if capability.MissionType==MissionType then
return capability.Performance
end
end
return -1
end
--- Set max mission range. Only missions in a circle of this radius around the squadron airbase are executed.
-- @param #SQUADRON self
-- @param #number Range Range in NM. Default 100 NM.
-- @return #SQUADRON self
function SQUADRON:SetMissionRange(Range)
self.engageRange=UTILS.NMToMeters(Range or 100)
return self
end
--- Set call sign.
-- @param #SQUADRON self
-- @param #number Callsign Callsign from CALLSIGN.Aircraft, e.g. "Chevy" for CALLSIGN.Aircraft.CHEVY.
-- @param #number Index Callsign index, Chevy-**1**.
-- @return #SQUADRON self
function SQUADRON:SetCallsign(Callsign, Index)
self.callsignName=Callsign
self.callsignIndex=Index
return self
end
--- Set modex.
-- @param #SQUADRON self
-- @param #number Modex A number like 100.
-- @param #string Prefix A prefix string, which is put before the `Modex` number.
-- @param #string Suffix A suffix string, which is put after the `Modex` number.
-- @return #SQUADRON self
function SQUADRON:SetModex(Modex, Prefix, Suffix)
self.modex=Modex
self.modexPrefix=Prefix
self.modexSuffix=Suffix
return self
end
--- Set low fuel threshold. --- Set low fuel threshold.
-- @param #SQUADRON self -- @param #SQUADRON self
-- @param #number LowFuel Low fuel threshold in percent. Default 25. -- @param #number LowFuel Low fuel threshold in percent. Default 25.
@@ -454,201 +204,17 @@ end
-- @param Ops.AirWing#AIRWING Airwing The airwing. -- @param Ops.AirWing#AIRWING Airwing The airwing.
-- @return #SQUADRON self -- @return #SQUADRON self
function SQUADRON:SetAirwing(Airwing) function SQUADRON:SetAirwing(Airwing)
self.airwing=Airwing self.legion=Airwing
return self return self
end end
--- Add airwing asset to squadron. --- Get airwing.
-- @param #SQUADRON self -- @param #SQUADRON self
-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset. -- @return Ops.AirWing#AIRWING The airwing.
-- @return #SQUADRON self function SQUADRON:GetAirwing(Airwing)
function SQUADRON:AddAsset(Asset) return self.legion
self:T(self.lid..string.format("Adding asset %s of type %s", Asset.spawngroupname, Asset.unittype))
Asset.squadname=self.name
table.insert(self.assets, Asset)
return self
end end
--- Remove airwing asset from squadron.
-- @param #SQUADRON self
-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset.
-- @return #SQUADRON self
function SQUADRON:DelAsset(Asset)
for i,_asset in pairs(self.assets) do
local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset
if Asset.uid==asset.uid then
self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname))
table.remove(self.assets, i)
break
end
end
return self
end
--- Remove airwing asset group from squadron.
-- @param #SQUADRON self
-- @param #string GroupName Name of the asset group.
-- @return #SQUADRON self
function SQUADRON:DelGroup(GroupName)
for i,_asset in pairs(self.assets) do
local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset
if GroupName==asset.spawngroupname then
self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname))
table.remove(self.assets, i)
break
end
end
return self
end
--- Get name of the squadron
-- @param #SQUADRON self
-- @return #string Name of the squadron.
function SQUADRON:GetName()
return self.name
end
--- Get radio frequency and modulation.
-- @param #SQUADRON self
-- @return #number Radio frequency in MHz.
-- @return #number Radio Modulation (0=AM, 1=FM).
function SQUADRON:GetRadio()
return self.radioFreq, self.radioModu
end
--- Create a callsign for the asset.
-- @param #SQUADRON self
-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset.
-- @return #SQUADRON self
function SQUADRON:GetCallsign(Asset)
if self.callsignName then
Asset.callsign={}
for i=1,Asset.nunits do
local callsign={}
callsign[1]=self.callsignName
callsign[2]=math.floor(self.callsigncounter / 10)
callsign[3]=self.callsigncounter % 10
if callsign[3]==0 then
callsign[3]=1
self.callsigncounter=self.callsigncounter+2
else
self.callsigncounter=self.callsigncounter+1
end
Asset.callsign[i]=callsign
self:T3({callsign=callsign})
--TODO: there is also a table entry .name, which is a string.
end
end
end
--- Create a modex for the asset.
-- @param #SQUADRON self
-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset.
-- @return #SQUADRON self
function SQUADRON:GetModex(Asset)
if self.modex then
Asset.modex={}
for i=1,Asset.nunits do
Asset.modex[i]=string.format("%03d", self.modex+self.modexcounter)
self.modexcounter=self.modexcounter+1
self:T3({modex=Asset.modex[i]})
end
end
end
--- Add TACAN channels to the squadron. Note that channels can only range from 1 to 126.
-- @param #SQUADRON self
-- @param #number ChannelMin Channel.
-- @param #number ChannelMax Channel.
-- @return #SQUADRON self
-- @usage mysquad:AddTacanChannel(64,69) -- adds channels 64, 65, 66, 67, 68, 69
function SQUADRON:AddTacanChannel(ChannelMin, ChannelMax)
ChannelMax=ChannelMax or ChannelMin
if ChannelMin>126 then
self:E(self.lid.."ERROR: TACAN Channel must be <= 126! Will not add to available channels")
return self
end
if ChannelMax>126 then
self:E(self.lid.."WARNING: TACAN Channel must be <= 126! Adjusting ChannelMax to 126")
ChannelMax=126
end
for i=ChannelMin,ChannelMax do
self.tacanChannel[i]=true
end
return self
end
--- Get an unused TACAN channel.
-- @param #SQUADRON self
-- @return #number TACAN channel or *nil* if no channel is free.
function SQUADRON:FetchTacan()
for channel,free in pairs(self.tacanChannel) do
if free then
self:T(self.lid..string.format("Checking out Tacan channel %d", channel))
self.tacanChannel[channel]=false
return channel
end
end
return nil
end
--- "Return" a used TACAN channel.
-- @param #SQUADRON self
-- @param #number channel The channel that is available again.
function SQUADRON:ReturnTacan(channel)
self:T(self.lid..string.format("Returning Tacan channel %d", channel))
self.tacanChannel[channel]=true
end
--- Check if squadron is "OnDuty".
-- @param #SQUADRON self
-- @return #boolean If true, squdron is in state "OnDuty".
function SQUADRON:IsOnDuty()
return self:Is("OnDuty")
end
--- Check if squadron is "Stopped".
-- @param #SQUADRON self
-- @return #boolean If true, squdron is in state "Stopped".
function SQUADRON:IsStopped()
return self:Is("Stopped")
end
--- Check if squadron is "Paused".
-- @param #SQUADRON self
-- @return #boolean If true, squdron is in state "Paused".
function SQUADRON:IsPaused()
return self:Is("Paused")
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status -- Start & Status
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -685,10 +251,10 @@ function SQUADRON:onafterStatus(From, Event, To)
local skill=self.skill and tostring(self.skill) or "N/A" local skill=self.skill and tostring(self.skill) or "N/A"
local NassetsTot=#self.assets local NassetsTot=#self.assets
local NassetsInS=self:CountAssetsInStock() local NassetsInS=self:CountAssets(true)
local NassetsQP=0 ; local NassetsP=0 ; local NassetsQ=0 local NassetsQP=0 ; local NassetsP=0 ; local NassetsQ=0
if self.airwing then if self.legion then
NassetsQP, NassetsP, NassetsQ=self.airwing:CountAssetsOnMission(nil, self) NassetsQP, NassetsP, NassetsQ=self.legion:CountAssetsOnMission(nil, self)
end end
-- Short info. -- Short info.
@@ -706,369 +272,10 @@ function SQUADRON:onafterStatus(From, Event, To)
end end
end end
--- Check asset status.
-- @param #SQUADRON self
function SQUADRON:_CheckAssetStatus()
if self.verbose>=2 and #self.assets>0 then
local text=""
for j,_asset in pairs(self.assets) do
local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset
-- Text.
text=text..string.format("\n[%d] %s (%s*%d): ", j, asset.spawngroupname, asset.unittype, asset.nunits)
if asset.spawned then
---
-- Spawned
---
-- Mission info.
local mission=self.airwing and self.airwing:GetAssetCurrentMission(asset) or false
if mission then
local distance=asset.flightgroup and UTILS.MetersToNM(mission:GetTargetDistance(asset.flightgroup.group:GetCoordinate())) or 0
text=text..string.format("Mission %s - %s: Status=%s, Dist=%.1f NM", mission.name, mission.type, mission.status, distance)
else
text=text.."Mission None"
end
-- Flight status.
text=text..", Flight: "
if asset.flightgroup and asset.flightgroup:IsAlive() then
local status=asset.flightgroup:GetState()
local fuelmin=asset.flightgroup:GetFuelMin()
local fuellow=asset.flightgroup:IsFuelLow()
local fuelcri=asset.flightgroup:IsFuelCritical()
text=text..string.format("%s Fuel=%d", status, fuelmin)
if fuelcri then
text=text.." (Critical!)"
elseif fuellow then
text=text.." (Low)"
end
local lifept, lifept0=asset.flightgroup:GetLifePoints()
text=text..string.format(", Life=%d/%d", lifept, lifept0)
local ammo=asset.flightgroup:GetAmmoTot()
text=text..string.format(", Ammo=%d [G=%d, R=%d, B=%d, M=%d]", ammo.Total,ammo.Guns, ammo.Rockets, ammo.Bombs, ammo.Missiles)
else
text=text.."N/A"
end
-- Payload info.
local payload=asset.payload and table.concat(self.airwing:GetPayloadMissionTypes(asset.payload), ", ") or "None"
text=text..", Payload={"..payload.."}"
else
---
-- In Stock
---
text=text..string.format("In Stock")
if self:IsRepaired(asset) then
text=text..", Combat Ready"
else
text=text..string.format(", Repaired in %d sec", self:GetRepairTime(asset))
if asset.damage then
text=text..string.format(" (Damage=%.1f)", asset.damage)
end
end
if asset.Treturned then
local T=timer.getAbsTime()-asset.Treturned
text=text..string.format(", Returned for %d sec", T)
end
end
end
self:I(self.lid..text)
end
end
--- On after "Stop" event.
-- @param #SQUADRON self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SQUADRON:onafterStop(From, Event, To)
self:I(self.lid.."STOPPING Squadron!")
-- Remove all assets.
for i=#self.assets,1,-1 do
local asset=self.assets[i]
self:DelAsset(asset)
end
self.CallScheduler:Clear()
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions -- Misc Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Check if there is a squadron that can execute a given mission.
-- We check the mission type, the refuelling system, engagement range
-- @param #SQUADRON self
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
-- @return #boolean If true, Squadron can do that type of mission.
function SQUADRON:CanMission(Mission)
local cando=true
-- On duty?=
if not self:IsOnDuty() then
self:T(self.lid..string.format("Squad in not OnDuty but in state %s. Cannot do mission %s with target %s", self:GetState(), Mission.name, Mission:GetTargetName()))
return false
end
-- Check mission type. WARNING: This assumes that all assets of the squad can do the same mission types!
if not self:CheckMissionType(Mission.type, self:GetMissionTypes()) then
self:T(self.lid..string.format("INFO: Squad cannot do mission type %s (%s, %s)", Mission.type, Mission.name, Mission:GetTargetName()))
return false
end
-- Check that tanker mission
if Mission.type==AUFTRAG.Type.TANKER then
if Mission.refuelSystem and Mission.refuelSystem==self.tankerSystem then
-- Correct refueling system.
else
self:T(self.lid..string.format("INFO: Wrong refueling system requested=%s != %s=available", tostring(Mission.refuelSystem), tostring(self.tankerSystem)))
return false
end
end
-- Distance to target.
local TargetDistance=Mission:GetTargetDistance(self.airwing:GetCoordinate())
-- Max engage range.
local engagerange=Mission.engageRange and math.max(self.engageRange, Mission.engageRange) or self.engageRange
-- Set range is valid. Mission engage distance can overrule the squad engage range.
if TargetDistance>engagerange then
self:I(self.lid..string.format("INFO: Squad is not in range. Target dist=%d > %d NM max mission Range", UTILS.MetersToNM(TargetDistance), UTILS.MetersToNM(engagerange)))
return false
end
return true
end
--- Count assets in airwing (warehous) stock.
-- @param #SQUADRON self
-- @return #number Assets not spawned.
function SQUADRON:CountAssetsInStock()
local N=0
for _,_asset in pairs(self.assets) do
local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset
if asset.spawned then
else
N=N+1
end
end
return N
end
--- Get assets for a mission.
-- @param #SQUADRON self
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
-- @param #number Nplayloads Number of payloads available.
-- @return #table Assets that can do the required mission.
function SQUADRON:RecruitAssets(Mission, Npayloads)
-- Number of payloads available.
Npayloads=Npayloads or self.airwing:CountPayloadsInStock(Mission.type, self.aircrafttype, Mission.payloads)
local assets={}
-- Loop over assets.
for _,_asset in pairs(self.assets) do
local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset
-- Check if asset is currently on a mission (STARTED or QUEUED).
if self.airwing:IsAssetOnMission(asset) then
---
-- Asset is already on a mission.
---
-- Check if this asset is currently on a GCICAP mission (STARTED or EXECUTING).
if self.airwing:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and Mission.type==AUFTRAG.Type.INTERCEPT then
-- Check if the payload of this asset is compatible with the mission.
-- Note: we do not check the payload as an asset that is on a GCICAP mission should be able to do an INTERCEPT as well!
self:I(self.lid.."Adding asset on GCICAP mission for an INTERCEPT mission")
table.insert(assets, asset)
end
else
---
-- Asset as NO current mission
---
if asset.spawned then
---
-- Asset is already SPAWNED (could be uncontrolled on the airfield or inbound after another mission)
---
local flightgroup=asset.flightgroup
-- Firstly, check if it has the right payload.
if self:CheckMissionCapability(Mission.type, asset.payload.capabilities) and flightgroup and flightgroup:IsAlive() then
-- Assume we are ready and check if any condition tells us we are not.
local combatready=true
if Mission.type==AUFTRAG.Type.INTERCEPT then
combatready=flightgroup:CanAirToAir()
else
local excludeguns=Mission.type==AUFTRAG.Type.BOMBING or Mission.type==AUFTRAG.Type.BOMBRUNWAY or Mission.type==AUFTRAG.Type.BOMBCARPET or Mission.type==AUFTRAG.Type.SEAD or Mission.type==AUFTRAG.Type.ANTISHIP
combatready=flightgroup:CanAirToGround(excludeguns)
end
-- No more attacks if fuel is already low. Safety first!
if flightgroup:IsFuelLow() then
combatready=false
end
-- Check if in a state where we really do not want to fight any more.
if flightgroup:IsHolding() or flightgroup:IsLanding() or flightgroup:IsLanded() or flightgroup:IsArrived() or flightgroup:IsDead() or flightgroup:IsStopped() then
combatready=false
end
-- This asset is "combatready".
if combatready then
self:I(self.lid.."Adding SPAWNED asset to ANOTHER mission as it is COMBATREADY")
table.insert(assets, asset)
end
end
else
---
-- Asset is still in STOCK
---
-- Check that asset is not already requested for another mission.
if Npayloads>0 and self:IsRepaired(asset) and (not asset.requested) then
-- Add this asset to the selection.
table.insert(assets, asset)
-- Reduce number of payloads so we only return the number of assets that could do the job.
Npayloads=Npayloads-1
end
end
end
end -- loop over assets
return assets
end
--- Get the time an asset needs to be repaired.
-- @param #SQUADRON self
-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset.
-- @return #number Time in seconds until asset is repaired.
function SQUADRON:GetRepairTime(Asset)
if Asset.Treturned then
local t=self.maintenancetime
t=t+Asset.damage*self.repairtime
-- Seconds after returned.
local dt=timer.getAbsTime()-Asset.Treturned
local T=t-dt
return T
else
return 0
end
end
--- Checks if a mission type is contained in a table of possible types.
-- @param #SQUADRON self
-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset.
-- @return #boolean If true, the requested mission type is part of the possible mission types.
function SQUADRON:IsRepaired(Asset)
if Asset.Treturned then
local Tnow=timer.getAbsTime()
local Trepaired=Asset.Treturned+self.maintenancetime
if Tnow>=Trepaired then
return true
else
return false
end
else
return true
end
end
--- Checks if a mission type is contained in a table of possible types.
-- @param #SQUADRON self
-- @param #string MissionType The requested mission type.
-- @param #table PossibleTypes A table with possible mission types.
-- @return #boolean If true, the requested mission type is part of the possible mission types.
function SQUADRON:CheckMissionType(MissionType, PossibleTypes)
if type(PossibleTypes)=="string" then
PossibleTypes={PossibleTypes}
end
for _,canmission in pairs(PossibleTypes) do
if canmission==MissionType then
return true
end
end
return false
end
--- Check if a mission type is contained in a list of possible capabilities.
-- @param #SQUADRON self
-- @param #string MissionType The requested mission type.
-- @param #table Capabilities A table with possible capabilities.
-- @return #boolean If true, the requested mission type is part of the possible mission types.
function SQUADRON:CheckMissionCapability(MissionType, Capabilities)
for _,cap in pairs(Capabilities) do
local capability=cap --Ops.Auftrag#AUFTRAG.Capability
if capability.MissionType==MissionType then
return true
end
end
return false
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+176 -30
View File
@@ -17,6 +17,7 @@
-- @type TARGET -- @type TARGET
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity level. -- @field #number verbose Verbosity level.
-- @field #number uid Unique ID of the target.
-- @field #string lid Class id string for output to DCS log file. -- @field #string lid Class id string for output to DCS log file.
-- @field #table targets Table of target objects. -- @field #table targets Table of target objects.
-- @field #number targetcounter Running number to generate target object IDs. -- @field #number targetcounter Running number to generate target object IDs.
@@ -30,14 +31,17 @@
-- @field #number Ndead Number of target elements/units that are dead (destroyed or despawned). -- @field #number Ndead Number of target elements/units that are dead (destroyed or despawned).
-- @field #table elements Table of target elements/units. -- @field #table elements Table of target elements/units.
-- @field #table casualties Table of dead element names. -- @field #table casualties Table of dead element names.
-- @field #number prio Priority.
-- @field #number importance Importance.
-- @field Ops.Auftrag#AUFTRAG mission Mission attached to this target.
-- @field Ops.Intelligence#INTEL.Contact contact Contact attached to this target.
-- @field #boolean isDestroyed If true, target objects were destroyed.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- **It is far more important to be able to hit the target than it is to haggle over who makes a weapon or who pulls a trigger** -- Dwight D. Eisenhower --- **It is far more important to be able to hit the target than it is to haggle over who makes a weapon or who pulls a trigger** -- Dwight D Eisenhower
-- --
-- === -- ===
-- --
-- ![Banner Image](..\Presentations\OPS\Target\_Main.pngs)
--
-- # The TARGET Concept -- # The TARGET Concept
-- --
-- Define a target of your mission and monitor its status. Events are triggered when the target is damaged or destroyed. -- Define a target of your mission and monitor its status. Events are triggered when the target is damaged or destroyed.
@@ -128,13 +132,15 @@ _TARGETID=0
--- TARGET class version. --- TARGET class version.
-- @field #string version -- @field #string version
TARGET.version="0.3.1" TARGET.version="0.5.2"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot. -- TODO: Had cases where target life was 0 but target was not dead. Need to figure out why!
-- TODO: Add pseudo functions.
-- DONE: Initial object can be nil.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor -- Constructor
@@ -151,26 +157,23 @@ function TARGET:New(TargetObject)
-- Increase counter. -- Increase counter.
_TARGETID=_TARGETID+1 _TARGETID=_TARGETID+1
-- Set UID.
self.uid=_TARGETID
if TargetObject then
-- Add object. -- Add object.
self:AddObject(TargetObject) self:AddObject(TargetObject)
-- Get first target.
local Target=self.targets[1] --#TARGET.Object
if not Target then
self:E("ERROR: No valid TARGET!")
return nil
end
-- Target Name. end
self.name=self:GetTargetName(Target)
-- Target category. -- Defaults.
self.category=self:GetTargetCategory(Target) self:SetPriority()
self:SetImportance()
-- Log ID. -- Log ID.
self.lid=string.format("TARGET #%03d [%s] | ", _TARGETID, tostring(self.category)) self.lid=string.format("TARGET #%03d | ", _TARGETID)
-- Start state. -- Start state.
self:SetStartState("Stopped") self:SetStartState("Stopped")
@@ -187,7 +190,7 @@ function TARGET:New(TargetObject)
self:AddTransition("*", "Damaged", "*") -- Target was damaged. self:AddTransition("*", "Damaged", "*") -- Target was damaged.
self:AddTransition("*", "Destroyed", "Dead") -- Target was completely destroyed. self:AddTransition("*", "Destroyed", "Dead") -- Target was completely destroyed.
self:AddTransition("*", "Dead", "Dead") -- Target was completely destroyed. self:AddTransition("*", "Dead", "Dead") -- Target is dead. Could be destroyed or despawned.
------------------------ ------------------------
--- Pseudo Functions --- --- Pseudo Functions ---
@@ -220,7 +223,6 @@ function TARGET:New(TargetObject)
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
-- Start. -- Start.
self:__Start(-1) self:__Start(-1)
@@ -231,12 +233,24 @@ end
-- User functions -- User functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create target data from a given object. --- Create target data from a given object. Valid objects are:
--
-- * GROUP
-- * UNIT
-- * STATIC
-- * AIRBASE
-- * COORDINATE
-- * ZONE
-- * SET_GROUP
-- * SET_UNIT
-- * SET_STATIC
-- * SET_OPSGROUP
--
-- @param #TARGET self -- @param #TARGET self
-- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, AIRBASE or COORDINATE. -- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, AIRBASE or COORDINATE.
function TARGET:AddObject(Object) function TARGET:AddObject(Object)
if Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") then if Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") or Object:IsInstanceOf("SET_STATIC") or Object:IsInstanceOf("SET_OPSGROUP") then
--- ---
-- Sets -- Sets
@@ -247,31 +261,81 @@ function TARGET:AddObject(Object)
for _,object in pairs(set.Set) do for _,object in pairs(set.Set) do
self:AddObject(object) self:AddObject(object)
end end
elseif Object:IsInstanceOf("SET_ZONE") then
local set=Object --Core.Set#SET_ZONE
set:SortByName()
for index,ZoneName in pairs(set.Index) do
local zone=set.Set[ZoneName] --Core.Zone#ZONE
self:_AddObject(zone)
end
else else
--- ---
-- Groups, Units, Statics, Airbases, Coordinates -- Groups, Units, Statics, Airbases, Coordinates
--- ---
self:_AddObject(Object) if Object:IsInstanceOf("OPSGROUP") then
self:_AddObject(Object:GetGroup()) -- We add the MOOSE GROUP object not the OPSGROUP object.
else
self:_AddObject(Object)
end
end end
end end
--- Set priority of the target.
-- @param #TARGET self
-- @param #number Priority Priority of the target. Default 50.
-- @return #TARGET self
function TARGET:SetPriority(Priority)
self.prio=Priority or 50
return self
end
--- Set importance of the target.
-- @param #TARGET self
-- @param #number Importance Importance of the target. Default `nil`.
-- @return #TARGET self
function TARGET:SetImportance(Importance)
self.importance=Importance
return self
end
--- Check if TARGET is alive. --- Check if TARGET is alive.
-- @param #TARGET self -- @param #TARGET self
-- @return #boolean If true, target is alive. -- @return #boolean If true, target is alive.
function TARGET:IsAlive() function TARGET:IsAlive()
return self:Is("Alive")
for _,_target in pairs(self.targets) do
local target=_target --Ops.Target#TARGET.Object
if target.Status==TARGET.ObjectStatus.ALIVE then
return true
end
end
return false
end end
--- Check if TARGET is destroyed.
-- @param #TARGET self
-- @return #boolean If true, target is destroyed.
function TARGET:IsDestroyed()
return self.isDestroyed
end
--- Check if TARGET is dead. --- Check if TARGET is dead.
-- @param #TARGET self -- @param #TARGET self
-- @return #boolean If true, target is dead. -- @return #boolean If true, target is dead.
function TARGET:IsDead() function TARGET:IsDead()
return self:Is("Dead") local is=self:Is("Dead")
return is
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -322,6 +386,11 @@ function TARGET:onafterStatus(From, Event, To)
damaged=true damaged=true
end end
if life==0 then
self:I(self.lid..string.format("FF life is zero but no object dead event fired ==> object dead now for traget object %s!", tostring(target.Name)))
self:ObjectDead(target)
end
end end
-- Target was damaged. -- Target was damaged.
@@ -422,6 +491,8 @@ function TARGET:onafterObjectDead(From, Event, To, Target)
if self.Ndestroyed==self.Ntargets0 then if self.Ndestroyed==self.Ntargets0 then
self.isDestroyed=true
self:Destroyed() self:Destroyed()
else else
@@ -691,6 +762,13 @@ function TARGET:_AddObject(Object)
target.Object=Object target.Object=Object
table.insert(self.targets, target) table.insert(self.targets, target)
if self.name==nil then
self.name=self:GetTargetName(target)
end
if self.category==nil then
self.category=self:GetTargetCategory(target)
end
end end
@@ -805,10 +883,25 @@ function TARGET:GetLife()
return N return N
end end
--- Get target 2D position vector.
-- @param #TARGET self
-- @param #TARGET.Object Target Target object.
-- @return DCS#Vec2 Vector with x,y components.
function TARGET:GetTargetVec2(Target)
local vec3=self:GetTargetVec3(Target)
if vec3 then
return {x=vec3.x, y=vec3.z}
end
return nil
end
--- Get target 3D position vector. --- Get target 3D position vector.
-- @param #TARGET self -- @param #TARGET self
-- @param #TARGET.Object Target Target object. -- @param #TARGET.Object Target Target object.
-- @return DCS#Vec3 Vector with x,y,z components -- @return DCS#Vec3 Vector with x,y,z components.
function TARGET:GetTargetVec3(Target) function TARGET:GetTargetVec3(Target)
if Target.Type==TARGET.ObjectType.GROUP then if Target.Type==TARGET.ObjectType.GROUP then
@@ -955,6 +1048,12 @@ function TARGET:GetTargetName(Target)
local coord=Target.Object --Core.Point#COORDINATE local coord=Target.Object --Core.Point#COORDINATE
return coord:ToStringMGRS() return coord:ToStringMGRS()
elseif Target.Type==TARGET.ObjectType.ZONE then
local Zone=Target.Object --Core.Zone#ZONE
return Zone:GetName()
end end
@@ -965,7 +1064,48 @@ end
-- @param #TARGET self -- @param #TARGET self
-- @return #string Name of the target usually the first object. -- @return #string Name of the target usually the first object.
function TARGET:GetName() function TARGET:GetName()
return self.name local name=self.name or "Unknown"
return name
end
--- Get 2D vector.
-- @param #TARGET self
-- @return DCS#Vec2 2D vector of the target.
function TARGET:GetVec2()
for _,_target in pairs(self.targets) do
local Target=_target --#TARGET.Object
local coordinate=self:GetTargetVec2(Target)
if coordinate then
return coordinate
end
end
self:E(self.lid..string.format("ERROR: Cannot get Vec2 of target %s", self.name))
return nil
end
--- Get 3D vector.
-- @param #TARGET self
-- @return DCS#Vec3 3D vector of the target.
function TARGET:GetVec3()
for _,_target in pairs(self.targets) do
local Target=_target --#TARGET.Object
local coordinate=self:GetTargetVec3(Target)
if coordinate then
return coordinate
end
end
self:E(self.lid..string.format("ERROR: Cannot get Vec3 of target %s", self.name))
return nil
end end
--- Get coordinate. --- Get coordinate.
@@ -988,6 +1128,12 @@ function TARGET:GetCoordinate()
return nil return nil
end end
--- Get category.
-- @param #TARGET self
-- @return #string Target category. See `TARGET.Category.X`, where `X=AIRCRAFT, GROUND`.
function TARGET:GetCategory()
return self.category
end
--- Get target category. --- Get target category.
-- @param #TARGET self -- @param #TARGET self
+1 -1
View File
@@ -671,7 +671,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
else else
-- Add gender. -- Add gender.
if gender and gender~="female" then if gender and gender~="female" then
command=command..string.format(" --gender=%s", tostring(gender)) command=command..string.format(" -g %s", tostring(gender))
end end
-- Add culture. -- Add culture.
if culture and culture~="en-GB" then if culture and culture~="en-GB" then
@@ -750,7 +750,7 @@ routines.groupRandomDistSelf = function(gpData, dist, form, heading, speed)
local pos = routines.getLeadPos(gpData) local pos = routines.getLeadPos(gpData)
local fakeZone = {} local fakeZone = {}
fakeZone.radius = dist or math.random(300, 1000) fakeZone.radius = dist or math.random(300, 1000)
fakeZone.point = {x = pos.x, y, pos.y, z = pos.z} fakeZone.point = {x = pos.x, y = pos.y, z = pos.z}
routines.groupToRandomZone(gpData, fakeZone, form, heading, speed) routines.groupToRandomZone(gpData, fakeZone, form, heading, speed)
return return
@@ -970,6 +970,15 @@ function UTILS.VecDot(a, b)
return a.x*b.x + a.y*b.y + a.z*b.z return a.x*b.x + a.y*b.y + a.z*b.z
end end
--- Calculate the [dot product](https://en.wikipedia.org/wiki/Dot_product) of two 2D vectors. The result is a number.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param DCS#Vec2 b Vector in 2D with x, y components.
-- @return #number Scalar product of the two vectors a*b.
function UTILS.Vec2Dot(a, b)
return a.x*b.x + a.y*b.y
end
--- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 3D vector. --- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 3D vector.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @return #number Norm of the vector. -- @return #number Norm of the vector.
@@ -977,6 +986,13 @@ function UTILS.VecNorm(a)
return math.sqrt(UTILS.VecDot(a, a)) return math.sqrt(UTILS.VecDot(a, a))
end end
--- Calculate the [euclidean norm](https://en.wikipedia.org/wiki/Euclidean_distance) (length) of a 2D vector.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @return #number Norm of the vector.
function UTILS.Vec2Norm(a)
return math.sqrt(UTILS.Vec2Dot(a, a))
end
--- Calculate the distance between two 2D vectors. --- Calculate the distance between two 2D vectors.
-- @param DCS#Vec2 a Vector in 3D with x, y components. -- @param DCS#Vec2 a Vector in 3D with x, y components.
-- @param DCS#Vec2 b Vector in 3D with x, y components. -- @param DCS#Vec2 b Vector in 3D with x, y components.
@@ -1059,6 +1075,17 @@ function UTILS.VecHdg(a)
return h return h
end end
--- Calculate "heading" of a 2D vector in the X-Y plane.
-- @param DCS#Vec2 a Vector in "D with x, y components.
-- @return #number Heading in degrees in [0,360).
function UTILS.Vec2Hdg(a)
local h=math.deg(math.atan2(a.y, a.x))
if h<0 then
h=h+360
end
return h
end
--- Calculate the difference between two "heading", i.e. angles in [0,360) deg. --- Calculate the difference between two "heading", i.e. angles in [0,360) deg.
-- @param #number h1 Heading one. -- @param #number h1 Heading one.
-- @param #number h2 Heading two. -- @param #number h2 Heading two.
@@ -1095,6 +1122,22 @@ function UTILS.VecTranslate(a, distance, angle)
return {x=TX, y=a.y, z=TY} return {x=TX, y=a.y, z=TY}
end end
--- Translate 2D vector in the 2D (x,z) plane.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param #number distance The distance to translate.
-- @param #number angle Rotation angle in degrees.
-- @return DCS#Vec2 Translated vector.
function UTILS.Vec2Translate(a, distance, angle)
local SX = a.x
local SY = a.y
local Radians=math.rad(angle or 0)
local TX=distance*math.cos(Radians)+SX
local TY=distance*math.sin(Radians)+SY
return {x=TX, y=TY}
end
--- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged. --- Rotate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param #number angle Rotation angle in degrees. -- @param #number angle Rotation angle in degrees.
@@ -1115,6 +1158,25 @@ function UTILS.Rotate2D(a, angle)
return A return A
end end
--- Rotate 2D vector in the 2D (x,z) plane.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param #number angle Rotation angle in degrees.
-- @return DCS#Vec2 Vector rotated in the (x,y) plane.
function UTILS.Vec2Rotate2D(a, angle)
local phi=math.rad(angle)
local x=a.x
local y=a.y
local X=x*math.cos(phi)-y*math.sin(phi)
local Y=x*math.sin(phi)+y*math.cos(phi)
local A={x=X, y=Y}
return A
end
--- Converts a TACAN Channel/Mode couple into a frequency in Hz. --- Converts a TACAN Channel/Mode couple into a frequency in Hz.
-- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X". -- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
+36 -9
View File
@@ -142,7 +142,7 @@ AIRBASE.Caucasus = {
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip -- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
-- * AIRBASE.Nevada.Tonopah_Airport -- * AIRBASE.Nevada.Tonopah_Airport
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield -- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
-- --
-- @field Nevada -- @field Nevada
AIRBASE.Nevada = { AIRBASE.Nevada = {
["Creech_AFB"] = "Creech AFB", ["Creech_AFB"] = "Creech AFB",
@@ -197,7 +197,7 @@ AIRBASE.Nevada = {
-- * AIRBASE.Normandy.Funtington -- * AIRBASE.Normandy.Funtington
-- * AIRBASE.Normandy.Tangmere -- * AIRBASE.Normandy.Tangmere
-- * AIRBASE.Normandy.Ford_AF -- * AIRBASE.Normandy.Ford_AF
-- --
-- @field Normandy -- @field Normandy
AIRBASE.Normandy = { AIRBASE.Normandy = {
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont", ["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
@@ -271,7 +271,7 @@ AIRBASE.Normandy = {
-- * AIRBASE.PersianGulf.Sirri_Island -- * AIRBASE.PersianGulf.Sirri_Island
-- * AIRBASE.PersianGulf.Tunb_Island_AFB -- * AIRBASE.PersianGulf.Tunb_Island_AFB
-- * AIRBASE.PersianGulf.Tunb_Kochak -- * AIRBASE.PersianGulf.Tunb_Kochak
-- --
-- @field PersianGulf -- @field PersianGulf
AIRBASE.PersianGulf = { AIRBASE.PersianGulf = {
["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl", ["Abu_Dhabi_International_Airport"] = "Abu Dhabi Intl",
@@ -473,6 +473,13 @@ AIRBASE.MarianaIslands={
-- @field #boolean Free This spot is currently free, i.e. there is no alive aircraft on it at the present moment. -- @field #boolean Free This spot is currently free, i.e. there is no alive aircraft on it at the present moment.
-- @field #number TerminalID0 Unknown what this means. If you know, please tell us! -- @field #number TerminalID0 Unknown what this means. If you know, please tell us!
-- @field #number DistToRwy Distance to runway in meters. Currently bugged and giving the same number as the TerminalID. -- @field #number DistToRwy Distance to runway in meters. Currently bugged and giving the same number as the TerminalID.
-- @field #string AirbaseName Name of the airbase.
-- @field #number MarkerID Numerical ID of marker placed at parking spot.
-- @field Wrapper.Marker#MARKER Marker The marker on the F10 map.
-- @field #string ClientSpot Client unit sitting at this spot or *nil*.
-- @field #string Status Status of spot e.g. AIRBASE.SpotStatus.FREE.
-- @field #string OccupiedBy Name of the aircraft occupying the spot or "unknown". Can be *nil* if spot is not occupied.
-- @field #string ReservedBy Name of the aircraft for which this spot is reserved. Can be *nil* if spot is not reserved.
--- Terminal Types of parking spots. See also https://wiki.hoggitworld.com/view/DCS_func_getParking --- Terminal Types of parking spots. See also https://wiki.hoggitworld.com/view/DCS_func_getParking
-- --
@@ -507,6 +514,17 @@ AIRBASE.TerminalType = {
FighterAircraft=244, FighterAircraft=244,
} }
--- Status of a parking spot.
-- @type AIRBASE.SpotStatus
-- @field #string FREE Spot is free.
-- @field #string OCCUPIED Spot is occupied.
-- @field #string RESERVED Spot is reserved.
AIRBASE.SpotStatus = {
FREE="Free",
OCCUPIED="Occupied",
RESERVED="Reserved",
}
--- Runway data. --- Runway data.
-- @type AIRBASE.Runway -- @type AIRBASE.Runway
-- @field #number heading Heading of the runway in degrees. -- @field #number heading Heading of the runway in degrees.
@@ -537,6 +555,9 @@ function AIRBASE:Register(AirbaseName)
-- Get descriptors. -- Get descriptors.
self.descriptors=self:GetDesc() self.descriptors=self:GetDesc()
-- Debug info.
--self:I({airbase=AirbaseName, descriptors=self.descriptors})
-- Category. -- Category.
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
@@ -553,13 +574,15 @@ function AIRBASE:Register(AirbaseName)
self.isShip=false self.isShip=false
self.category=Airbase.Category.HELIPAD self.category=Airbase.Category.HELIPAD
_DATABASE:AddStatic(AirbaseName) _DATABASE:AddStatic(AirbaseName)
end end
else else
self:E("ERROR: Unknown airbase category!") self:E("ERROR: Unknown airbase category!")
end end
-- Init parking spots.
self:_InitParkingSpots() self:_InitParkingSpots()
-- Get 2D position vector.
local vec2=self:GetVec2() local vec2=self:GetVec2()
-- Init coordinate. -- Init coordinate.
@@ -1024,6 +1047,8 @@ function AIRBASE:GetParkingSpotsTable(termtype)
spot.Free=_isfree(_spot) -- updated spot.Free=_isfree(_spot) -- updated
spot.TOAC=_spot.TO_AC -- updated spot.TOAC=_spot.TO_AC -- updated
spot.AirbaseName=self.AirbaseName
spot.ClientSpot=nil --TODO
table.insert(spots, spot) table.insert(spots, spot)
@@ -1060,6 +1085,8 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
spot.Free=true -- updated spot.Free=true -- updated
spot.TOAC=_spot.TO_AC -- updated spot.TOAC=_spot.TO_AC -- updated
spot.AirbaseName=self.AirbaseName
spot.ClientSpot=nil --TODO
table.insert(freespots, spot) table.insert(freespots, spot)
@@ -1141,7 +1168,7 @@ end
-- @param #table parkingdata (Optional) Parking spots data table. If not given it is automatically derived from the GetParkingSpotsTable() function. -- @param #table parkingdata (Optional) Parking spots data table. If not given it is automatically derived from the GetParkingSpotsTable() function.
-- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID. -- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID.
function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots, parkingdata) function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots, parkingdata)
-- Init default -- Init default
scanradius=scanradius or 50 scanradius=scanradius or 50
if scanunits==nil then if scanunits==nil then
@@ -1197,7 +1224,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
az = 17 -- width az = 17 -- width
end end
-- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size! -- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size!
local _nspots=nspots or group:GetSize() local _nspots=nspots or group:GetSize()
@@ -1325,7 +1352,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
-- Retrun spots we found, even if there were not enough. -- Retrun spots we found, even if there were not enough.
return validspots return validspots
end end
--- Check black and white lists. --- Check black and white lists.
@@ -1344,7 +1371,7 @@ function AIRBASE:_CheckParkingLists(TerminalID)
end end
end end
-- Check if a whitelist was defined. -- Check if a whitelist was defined.
if self.parkingWhitelist and #self.parkingWhitelist>0 then if self.parkingWhitelist and #self.parkingWhitelist>0 then
for _,terminalID in pairs(self.parkingWhitelist or {}) do for _,terminalID in pairs(self.parkingWhitelist or {}) do
@@ -1454,7 +1481,7 @@ function AIRBASE:GetRunwayData(magvar, mark)
name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or name==AIRBASE.PersianGulf.Abu_Dhabi_International_Airport or
name==AIRBASE.PersianGulf.Dubai_Intl or name==AIRBASE.PersianGulf.Dubai_Intl or
name==AIRBASE.PersianGulf.Shiraz_International_Airport or name==AIRBASE.PersianGulf.Shiraz_International_Airport or
name==AIRBASE.PersianGulf.Kish_International_Airport or name==AIRBASE.PersianGulf.Kish_International_Airport or
name==AIRBASE.MarianaIslands.Andersen_AFB then name==AIRBASE.MarianaIslands.Andersen_AFB then
-- 1-->4, 2-->3, 3-->2, 4-->1 -- 1-->4, 2-->3, 3-->2, 4-->1
@@ -1407,7 +1407,7 @@ function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, E
DCSTask = { DCSTask = {
id = 'Escort', id = 'Escort',
params = { params = {
groupId = FollowControllable:GetID(), groupId = FollowControllable and FollowControllable:GetID() or nil,
pos = Vec3, pos = Vec3,
lastWptIndexFlag = LastWaypointIndex and true or false, lastWptIndexFlag = LastWaypointIndex and true or false,
lastWptIndex = LastWaypointIndex, lastWptIndex = LastWaypointIndex,
@@ -1437,11 +1437,11 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Alti
id = 'FireAtPoint', id = 'FireAtPoint',
params = { params = {
point = Vec2, point = Vec2,
x=Vec2.x, x = Vec2.x,
y=Vec2.y, y = Vec2.y,
zoneRadius = Radius, zoneRadius = Radius,
radius = Radius, radius = Radius,
expendQty = 100, -- dummy value expendQty = 1, -- dummy value
expendQtyEnabled = false, expendQtyEnabled = false,
alt_type = ASL and 0 or 1 alt_type = ASL and 0 or 1
} }
@@ -1460,7 +1460,8 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Alti
DCSTask.params.weaponType=WeaponType DCSTask.params.weaponType=WeaponType
end end
--self:I(DCSTask) --env.info("FF fireatpoint")
--BASE:I(DCSTask)
return DCSTask return DCSTask
end end
@@ -2022,22 +2023,20 @@ do -- Patrol methods
end end
--- Return a Misson task to follow a given route defined by Points. --- Return a "Misson" task to follow a given route defined by Points.
-- @param #CONTROLLABLE self -- @param #CONTROLLABLE self
-- @param #table Points A table of route points. -- @param #table Points A table of route points.
-- @return DCS#Task -- @return DCS#Task DCS mission task. Has entries `.id="Mission"`, `params`, were params has entries `airborne` and `route`, which is a table of `points`.
function CONTROLLABLE:TaskRoute( Points ) function CONTROLLABLE:TaskRoute( Points )
self:F2( Points )
local DCSTask = { local DCSTask = {
id = 'Mission', id = 'Mission',
params = { params = {
airborne = self:IsAir(), airborne = self:IsAir(), -- This is important to make aircraft land without respawning them (which was a long standing DCS issue).
route = {points = Points}, route = {points = Points},
}, },
} }
self:T3( { DCSTask } )
return DCSTask return DCSTask
end end
+3 -8
View File
@@ -310,8 +310,7 @@ end
--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. --- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission.
-- @param Wrapper.Positionable#POSITIONABLE self -- @param Wrapper.Positionable#POSITIONABLE self
-- @return DCS#Position The 3D position vectors of the POSITIONABLE. -- @return DCS#Position The 3D position vectors of the POSITIONABLE or #nil if the groups not existing or alive.
-- @return #nil The POSITIONABLE is not existing or alive.
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3() function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
self:F2( self.PositionableName ) self:F2( self.PositionableName )
@@ -339,9 +338,7 @@ end
-- If the first @{Wrapper.Unit} of the group is inactive, it will return false. -- If the first @{Wrapper.Unit} of the group is inactive, it will return false.
-- --
-- @param #GROUP self -- @param #GROUP self
-- @return #boolean true if the group is alive and active. -- @return #boolean `true` if the group is alive *and* active, `false` if the group is alive but inactive or `#nil` if the group does not exist anymore.
-- @return #boolean false if the group is alive but inactive.
-- @return #nil if the group does not exist anymore.
function GROUP:IsAlive() function GROUP:IsAlive()
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -363,8 +360,7 @@ end
--- Returns if the group is activated. --- Returns if the group is activated.
-- @param #GROUP self -- @param #GROUP self
-- @return #boolean true if group is activated. -- @return #boolean `true` if group is activated or `#nil` The group is not existing or alive.
-- @return #nil The group is not existing or alive.
function GROUP:IsActive() function GROUP:IsActive()
self:F2( self.GroupName ) self:F2( self.GroupName )
@@ -412,7 +408,6 @@ function GROUP:Destroy( GenerateEvent, delay )
self:F2( self.GroupName ) self:F2( self.GroupName )
if delay and delay>0 then if delay and delay>0 then
--SCHEDULER:New(nil, GROUP.Destroy, {self, GenerateEvent}, delay)
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent) self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
else else
@@ -56,8 +56,7 @@ end
-- If the Identifiable is not alive, nil is returned. -- If the Identifiable is not alive, nil is returned.
-- If the Identifiable is alive, true is returned. -- If the Identifiable is alive, true is returned.
-- @param #IDENTIFIABLE self -- @param #IDENTIFIABLE self
-- @return #boolean true if Identifiable is alive. -- @return #boolean true if Identifiable is alive or `#nil` if the Identifiable is not existing or is not alive.
-- @return #nil if the Identifiable is not existing or is not alive.
function IDENTIFIABLE:IsAlive() function IDENTIFIABLE:IsAlive()
self:F3( self.IdentifiableName ) self:F3( self.IdentifiableName )
@@ -77,11 +76,8 @@ end
--- Returns DCS Identifiable object name. --- Returns DCS Identifiable object name.
-- The function provides access to non-activated objects too. -- The function provides access to non-activated objects too.
-- @param #IDENTIFIABLE self -- @param #IDENTIFIABLE self
-- @return #string The name of the DCS Identifiable. -- @return #string The name of the DCS Identifiable or `#nil`.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetName() function IDENTIFIABLE:GetName()
self:F2( self.IdentifiableName )
local IdentifiableName = self.IdentifiableName local IdentifiableName = self.IdentifiableName
return IdentifiableName return IdentifiableName
end end
@@ -148,8 +144,7 @@ end
--- Returns coalition of the Identifiable. --- Returns coalition of the Identifiable.
-- @param #IDENTIFIABLE self -- @param #IDENTIFIABLE self
-- @return DCS#coalition.side The side of the coalition. -- @return DCS#coalition.side The side of the coalition or `#nil` The DCS Identifiable is not existing or alive.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetCoalition() function IDENTIFIABLE:GetCoalition()
self:F2( self.IdentifiableName ) self:F2( self.IdentifiableName )
@@ -190,8 +185,7 @@ end
--- Returns country of the Identifiable. --- Returns country of the Identifiable.
-- @param #IDENTIFIABLE self -- @param #IDENTIFIABLE self
-- @return DCS#country.id The country identifier. -- @return DCS#country.id The country identifier or `#nil` The DCS Identifiable is not existing or alive.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetCountry() function IDENTIFIABLE:GetCountry()
self:F2( self.IdentifiableName ) self:F2( self.IdentifiableName )
@@ -222,8 +216,7 @@ end
--- Returns Identifiable descriptor. Descriptor type depends on Identifiable category. --- Returns Identifiable descriptor. Descriptor type depends on Identifiable category.
-- @param #IDENTIFIABLE self -- @param #IDENTIFIABLE self
-- @return DCS#Object.Desc The Identifiable descriptor. -- @return DCS#Object.Desc The Identifiable descriptor or `#nil` The DCS Identifiable is not existing or alive.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:GetDesc() function IDENTIFIABLE:GetDesc()
self:F2( self.IdentifiableName ) self:F2( self.IdentifiableName )
@@ -242,8 +235,7 @@ end
--- Check if the Object has the attribute. --- Check if the Object has the attribute.
-- @param #IDENTIFIABLE self -- @param #IDENTIFIABLE self
-- @param #string AttributeName The attribute name. -- @param #string AttributeName The attribute name.
-- @return #boolean true if the attribute exists. -- @return #boolean true if the attribute exists or `#nil` The DCS Identifiable is not existing or alive.
-- @return #nil The DCS Identifiable is not existing or alive.
function IDENTIFIABLE:HasAttribute( AttributeName ) function IDENTIFIABLE:HasAttribute( AttributeName )
self:F2( self.IdentifiableName ) self:F2( self.IdentifiableName )
@@ -266,8 +258,10 @@ function IDENTIFIABLE:GetCallsign()
return '' return ''
end end
--- Gets the threat level.
-- @param #IDENTIFIABLE self
-- @return #number Threat level.
-- @return #string Type.
function IDENTIFIABLE:GetThreatLevel() function IDENTIFIABLE:GetThreatLevel()
return 0, "Scenery" return 0, "Scenery"
end end
@@ -1368,8 +1368,6 @@ do -- Cargo
return self.__.Cargo return self.__.Cargo
end end
--- Remove cargo. --- Remove cargo.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param Core.Cargo#CARGO Cargo -- @param Core.Cargo#CARGO Cargo
@@ -1414,17 +1412,15 @@ do -- Cargo
return ItemCount return ItemCount
end end
-- --- Get Cargo Bay Free Volume in m3. --- Get the number of infantry soldiers that can be embarked into an aircraft (airplane or helicopter).
-- -- @param #POSITIONABLE self -- Returns `nil` for ground or ship units.
-- -- @return #number CargoBayFreeVolume -- @param #POSITIONABLE self
-- function POSITIONABLE:GetCargoBayFreeVolume() -- @return #number Descent number of soldiers that fit into the unit. Returns `#nil` for ground and ship units.
-- local CargoVolume = 0 function POSITIONABLE:GetTroopCapacity()
-- for CargoName, Cargo in pairs( self.__.Cargo ) do local DCSunit=self:GetDCSObject() --DCS#Unit
-- CargoVolume = CargoVolume + Cargo:GetVolume() local capacity=DCSunit:getDescentCapacity()
-- end return capacity
-- return self.__.CargoBayVolumeLimit - CargoVolume end
-- end
--
--- Get Cargo Bay Free Weight in kg. --- Get Cargo Bay Free Weight in kg.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
@@ -1443,55 +1439,97 @@ do -- Cargo
return self.__.CargoBayWeightLimit - CargoWeight return self.__.CargoBayWeightLimit - CargoWeight
end end
-- --- Get Cargo Bay Volume Limit in m3.
-- -- @param #POSITIONABLE self
-- -- @param #number VolumeLimit
-- function POSITIONABLE:SetCargoBayVolumeLimit( VolumeLimit )
-- self.__.CargoBayVolumeLimit = VolumeLimit
-- end
--- Set Cargo Bay Weight Limit in kg. --- Set Cargo Bay Weight Limit in kg.
-- @param #POSITIONABLE self -- @param #POSITIONABLE self
-- @param #number WeightLimit -- @param #number WeightLimit (Optional) Weight limit in kg. If not given, the value is taken from the descriptors or hard coded.
function POSITIONABLE:SetCargoBayWeightLimit( WeightLimit ) function POSITIONABLE:SetCargoBayWeightLimit( WeightLimit )
if WeightLimit then if WeightLimit then
---
-- User defined value
---
self.__.CargoBayWeightLimit = WeightLimit self.__.CargoBayWeightLimit = WeightLimit
elseif self.__.CargoBayWeightLimit~=nil then elseif self.__.CargoBayWeightLimit~=nil then
-- Value already set ==> Do nothing! -- Value already set ==> Do nothing!
else else
-- If weightlimit is not provided, we will calculate it depending on the type of unit. ---
-- Weightlimit is not provided, we will calculate it depending on the type of unit.
---
-- Descriptors that contain the type name and for aircraft also weights.
local Desc = self:GetDesc()
self:F({Desc=Desc})
-- Unit type name.
local TypeName=Desc.typeName or "Unknown Type"
-- When an airplane or helicopter, we calculate the weightlimit based on the descriptor. -- When an airplane or helicopter, we calculate the weightlimit based on the descriptor.
if self:IsAir() then if self:IsAir() then
local Desc = self:GetDesc()
self:F({Desc=Desc})
-- Max takeoff weight if DCS descriptors have unrealstic values.
local Weights = { local Weights = {
["C-17A"] = 35000, --77519 cannot be used, because it loads way too much apcs and infantry., -- C-17A
["C-130"] = 22000 --The real value cannot be used, because it loads way too much apcs and infantry., -- Wiki says: max=265,352, empty=128,140, payload=77,516 (134 troops, 1 M1 Abrams tank, 2 M2 Bradley or 3 Stryker)
-- DCS says: max=265,350, empty=125,645, fuel=132,405 ==> Cargo Bay=7300 kg with a full fuel load (lot of fuel!) and 73300 with half a fuel load.
--["C-17A"] = 35000, --77519 cannot be used, because it loads way too much apcs and infantry.
-- C-130:
-- DCS says: max=79,380, empty=36,400, fuel=10,415 kg ==> Cargo Bay=32,565 kg with fuel load.
-- Wiki says: max=70,307, empty=34,382, payload=19,000 kg (92 passengers, 2-3 Humvees or 2 M113s), max takeoff weight 70,037 kg.
-- Here we say two M113s should be transported. Each one weights 11,253 kg according to DCS. So the cargo weight should be 23,000 kg with a full load of fuel.
-- This results in a max takeoff weight of 69,815 kg (23,000+10,415+36,400), which is very close to the Wiki value of 70,037 kg.
["C-130"] = 70000,
} }
self.__.CargoBayWeightLimit = Weights[Desc.typeName] or ( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) ) -- Max (takeoff) weight (empty+fuel+cargo weight).
local massMax= Desc.massMax or 0
-- Adjust value if set above.
local maxTakeoff=Weights[TypeName]
if maxTakeoff then
massMax=maxTakeoff
end
-- Empty weight.
local massEmpty=Desc.massEmpty or 0
-- Fuel. The descriptor provides the max fuel mass in kg. This needs to be multiplied by the relative fuel amount to calculate the actual fuel mass on board.
local massFuelMax=Desc.fuelMassMax or 0
local relFuel=math.max(self:GetFuel() or 1.0, 1.0) -- We take 1.0 as max in case of external fuel tanks.
local massFuel=massFuelMax*relFuel
-- Number of soldiers according to DCS function
--local troopcapacity=self:GetTroopCapacity() or 0
-- Calculate max cargo weight, which is the max (takeoff) weight minus the empty weight minus the actual fuel weight.
local CargoWeight=massMax-(massEmpty+massFuel)
-- Debug info.
self:T(string.format("Setting Cargo bay weight limit [%s]=%d kg (Mass max=%d, empty=%d, fuelMax=%d kg (rel=%.3f), fuel=%d kg", TypeName, CargoWeight, massMax, massEmpty, massFuelMax, relFuel, massFuel))
--self:T(string.format("Descent Troop Capacity=%d ==> %d kg (for 95 kg soldier)", troopcapacity, troopcapacity*95))
-- Set value.
self.__.CargoBayWeightLimit = CargoWeight
elseif self:IsShip() then elseif self:IsShip() then
local Desc = self:GetDesc()
self:F({Desc=Desc})
-- Hard coded cargo weights in kg.
local Weights = { local Weights = {
["Type_071"] = 245000, ["Type_071"] = 245000,
["LHA_Tarawa"] = 500000, ["LHA_Tarawa"] = 500000,
["Ropucha-class"] = 150000, ["Ropucha-class"] = 150000,
["Dry-cargo ship-1"] = 70000, ["Dry-cargo ship-1"] = 70000,
["Dry-cargo ship-2"] = 70000, ["Dry-cargo ship-2"] = 70000,
["Higgins_boat"] = 3700, -- Higgins Boat can load 3700 kg of general cargo or 36 men (source wikipedia). ["Higgins_boat"] = 3700, -- Higgins Boat can load 3700 kg of general cargo or 36 men (source wikipedia).
["USS_Samuel_Chase"] = 25000, -- Let's say 25 tons for now. Wiki says 33 Higgins boats, which would be 264 tons (can't be right!) and/or 578 troops. ["USS_Samuel_Chase"] = 25000, -- Let's say 25 tons for now. Wiki says 33 Higgins boats, which would be 264 tons (can't be right!) and/or 578 troops.
["LST_Mk2"] =2100000, -- Can carry 2100 tons according to wiki source! ["LST_Mk2"] = 2100000, -- Can carry 2100 tons according to wiki source!
["speedboat"] = 500, -- 500 kg ~ 5 persons
["Seawise_Giant"] =261000000, -- Gross tonnage is 261,000 tonns.
} }
self.__.CargoBayWeightLimit = ( Weights[Desc.typeName] or 50000 ) self.__.CargoBayWeightLimit = ( Weights[TypeName] or 50000 )
else else
local Desc = self:GetDesc()
-- Hard coded number of soldiers.
local Weights = { local Weights = {
["AAV7"] = 25, ["AAV7"] = 25,
["Bedford_MWD"] = 8, -- new by kappa ["Bedford_MWD"] = 8, -- new by kappa
@@ -1537,12 +1575,28 @@ do -- Cargo
["VAB_Mephisto"] = 8, -- new by Apple ["VAB_Mephisto"] = 8, -- new by Apple
} }
local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95 -- Assuming that each passenger weighs 95 kg on average.
local CargoBayWeightLimit = ( Weights[TypeName] or 0 ) * 95
self.__.CargoBayWeightLimit = CargoBayWeightLimit self.__.CargoBayWeightLimit = CargoBayWeightLimit
end end
end end
self:F({CargoBayWeightLimit = self.__.CargoBayWeightLimit}) self:F({CargoBayWeightLimit = self.__.CargoBayWeightLimit})
end end
--- Get Cargo Bay Weight Limit in kg.
-- @param #POSITIONABLE self
-- @return #number Max cargo weight in kg.
function POSITIONABLE:GetCargoBayWeightLimit()
if self.__.CargoBayWeightLimit==nil then
self:SetCargoBayWeightLimit()
end
return self.__.CargoBayWeightLimit
end
end --- Cargo end --- Cargo
--- Signal a flare at the position of the POSITIONABLE. --- Signal a flare at the position of the POSITIONABLE.
+58 -1
View File
@@ -524,6 +524,63 @@ function UNIT:IsTanker()
return tanker, system return tanker, system
end end
--- Check if the unit can supply ammo. Currently, we have
--
-- * M 818
-- * Ural-375
-- * ZIL-135
--
-- This list needs to be extended, if DCS adds other units capable of supplying ammo.
--
-- @param #UNIT self
-- @return #boolean If `true`, unit can supply ammo.
function UNIT:IsAmmoSupply()
-- Type name is the only thing we can check. There is no attribute (Sep. 2021) which would tell us.
local typename=self:GetTypeName()
if typename=="M 818" then
-- Blue ammo truck.
return true
elseif typename=="Ural-375" then
-- Red ammo truck.
return true
elseif typename=="ZIL-135" then
-- Red ammo truck. Checked that it can also provide ammo.
return true
end
return false
end
--- Check if the unit can supply fuel. Currently, we have
--
-- * M978 HEMTT Tanker
-- * ATMZ-5
-- * ATMZ-10
-- * ATZ-5
--
-- This list needs to be extended, if DCS adds other units capable of supplying fuel.
--
-- @param #UNIT self
-- @return #boolean If `true`, unit can supply fuel.
function UNIT:IsFuelSupply()
-- Type name is the only thing we can check. There is no attribute (Sep. 2021) which would tell us.
local typename=self:GetTypeName()
if typename=="M978 HEMTT Tanker" then
return true
elseif typename=="ATMZ-5" then
return true
elseif typename=="ATMZ-10" then
return true
elseif typename=="ATZ-5" then
return true
end
return false
end
--- Returns the unit's group if it exist and nil otherwise. --- Returns the unit's group if it exist and nil otherwise.
-- @param Wrapper.Unit#UNIT self -- @param Wrapper.Unit#UNIT self
@@ -999,7 +1056,7 @@ function UNIT:GetThreatLevel()
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
not Attributes["ATGM"] then ThreatLevel = 3 not Attributes["ATGM"] then ThreatLevel = 3
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2 elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
elseif Attributes["Infantry"] then ThreatLevel = 1 elseif Attributes["Infantry"] or Attributes["EWR"] then ThreatLevel = 1
end end
ThreatText = ThreatLevels[ThreatLevel+1] ThreatText = ThreatLevels[ThreatLevel+1]
+6
View File
@@ -79,9 +79,15 @@ Ops/Auftrag.lua
Ops/OpsGroup.lua Ops/OpsGroup.lua
Ops/FlightGroup.lua Ops/FlightGroup.lua
Ops/NavyGroup.lua Ops/NavyGroup.lua
Ops/Cohort.lua
Ops/Squadron.lua Ops/Squadron.lua
Ops/Platoon.lua
Ops/Legion.lua
Ops/AirWing.lua Ops/AirWing.lua
Ops/Brigade.lua
Ops/Intelligence.lua Ops/Intelligence.lua
Ops/Commander.lua
Ops/Chief.lua
Ops/CSAR.lua Ops/CSAR.lua
Ops/CTLD.lua Ops/CTLD.lua