mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
commit
3df494cacd
@ -234,7 +234,8 @@ FORMATION = {
|
||||
-- @param #BASE self
|
||||
-- @return #BASE
|
||||
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
|
||||
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 #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, ... }.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
-- @return #string The Schedule ID of the planned schedule.
|
||||
function BASE:ScheduleOnce( Start, SchedulerFunction, ... )
|
||||
self:F2( { Start } )
|
||||
self:T3( { ... } )
|
||||
|
||||
-- Object name.
|
||||
local ObjectName = "-"
|
||||
ObjectName = self.ClassName .. self.ClassID
|
||||
|
||||
-- Debug info.
|
||||
self:F3( { "ScheduleOnce: ", ObjectName, Start } )
|
||||
|
||||
if not self.Scheduler then
|
||||
self.Scheduler = SCHEDULER:New( self )
|
||||
end
|
||||
|
||||
-- FF this was wrong!
|
||||
--[[
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
@ -896,6 +899,10 @@ do -- Scheduling
|
||||
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
|
||||
|
||||
@ -910,7 +917,7 @@ do -- Scheduling
|
||||
-- @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 #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, ... )
|
||||
self:F2( { Start } )
|
||||
self:T3( { ... } )
|
||||
@ -924,6 +931,7 @@ do -- Scheduling
|
||||
self.Scheduler = SCHEDULER:New( self )
|
||||
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(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
@ -942,13 +950,13 @@ do -- Scheduling
|
||||
|
||||
--- Stops the Schedule.
|
||||
-- @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.
|
||||
function BASE:ScheduleStop( SchedulerFunction )
|
||||
|
||||
-- @param #string SchedulerID (Optional) Scheduler ID to be stopped. If nil, all pending schedules are stopped.
|
||||
function BASE:ScheduleStop( SchedulerID )
|
||||
self:F3( { "ScheduleStop:" } )
|
||||
|
||||
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
|
||||
|
||||
@ -1092,7 +1100,7 @@ end
|
||||
|
||||
--- Set tracing for a class
|
||||
-- @param #BASE self
|
||||
-- @param #string Class
|
||||
-- @param #string Class Class name.
|
||||
function BASE:TraceClass( Class )
|
||||
_TraceClass[Class] = true
|
||||
_TraceClassMethod[Class] = {}
|
||||
@ -1101,8 +1109,8 @@ end
|
||||
|
||||
--- Set tracing for a specific method of class
|
||||
-- @param #BASE self
|
||||
-- @param #string Class
|
||||
-- @param #string Method
|
||||
-- @param #string Class Class name.
|
||||
-- @param #string Method Method.
|
||||
function BASE:TraceClassMethod( Class, Method )
|
||||
if not _TraceClassMethod[Class] then
|
||||
_TraceClassMethod[Class] = {}
|
||||
|
||||
@ -860,6 +860,20 @@ function DATABASE:GetGroupTemplateFromUnitName( UnitName )
|
||||
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.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ClientName Name of the Client.
|
||||
@ -1474,19 +1488,19 @@ function DATABASE:SetPlayerSettings( PlayerName, Settings )
|
||||
self.PLAYERSETTINGS[PlayerName] = Settings
|
||||
end
|
||||
|
||||
--- Add a flight group to the data base.
|
||||
--- Add an OPS group (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) to the data base.
|
||||
-- @param #DATABASE self
|
||||
-- @param Ops.FlightGroup#FLIGHTGROUP flightgroup
|
||||
function DATABASE:AddFlightGroup(flightgroup)
|
||||
self:T({NewFlightGroup=flightgroup.groupname})
|
||||
self.FLIGHTGROUPS[flightgroup.groupname]=flightgroup
|
||||
-- @param Ops.OpsGroup#OPSGROUP opsgroup The OPS group added to the DB.
|
||||
function DATABASE:AddOpsGroup(opsgroup)
|
||||
--env.info("Adding OPSGROUP "..tostring(opsgroup.groupname))
|
||||
self.FLIGHTGROUPS[opsgroup.groupname]=opsgroup
|
||||
end
|
||||
|
||||
--- Get a flight group from the data base.
|
||||
--- Get an OPS group (FLIGHTGROUP, ARMYGROUP, NAVYGROUP) from the data base.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string groupname Group name of the flight group. Can also be passed as GROUP object.
|
||||
-- @return Ops.FlightGroup#FLIGHTGROUP Flight group object.
|
||||
function DATABASE:GetFlightGroup(groupname)
|
||||
-- @param #string groupname Group name of the group. Can also be passed as GROUP object.
|
||||
-- @return Ops.OpsGroup#OPSGROUP OPS group object.
|
||||
function DATABASE:GetOpsGroup(groupname)
|
||||
|
||||
-- Get group and group name.
|
||||
if type(groupname)=="string" then
|
||||
@ -1494,6 +1508,23 @@ function DATABASE:GetFlightGroup(groupname)
|
||||
groupname=groupname:GetName()
|
||||
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]
|
||||
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
|
||||
|
||||
self:_RegisterGroupTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID)
|
||||
self:_RegisterGroupTemplate(Template, CoalitionSide, _DATABASECategory[string.lower(CategoryName)], CountryID)
|
||||
|
||||
else
|
||||
|
||||
|
||||
@ -410,7 +410,7 @@ do -- FSM
|
||||
Transition.To = To
|
||||
|
||||
-- Debug message.
|
||||
self:T2( Transition )
|
||||
--self:T3( Transition )
|
||||
|
||||
self._Transitions[Transition] = 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.
|
||||
-- @return Core.Fsm#FSM_PROCESS The SubFSM.
|
||||
function FSM:AddProcess( From, Event, Process, ReturnEvents )
|
||||
self:T( { From, Event } )
|
||||
--self:T3( { From, Event } )
|
||||
|
||||
local Sub = {}
|
||||
Sub.From = From
|
||||
@ -533,7 +533,7 @@ do -- FSM
|
||||
Process._Scores[State].ScoreText = ScoreText
|
||||
Process._Scores[State].Score = Score
|
||||
|
||||
self:T( Process._Scores )
|
||||
--self:T3( Process._Scores )
|
||||
|
||||
return Process
|
||||
end
|
||||
@ -576,7 +576,7 @@ do -- FSM
|
||||
self[__Event] = self[__Event] or self:_delayed_transition(Event)
|
||||
|
||||
-- Debug message.
|
||||
self:T2( "Added methods: " .. Event .. ", " .. __Event )
|
||||
--self:T3( "Added methods: " .. Event .. ", " .. __Event )
|
||||
|
||||
Events[Event] = self.Events[Event] or { map = {} }
|
||||
self:_add_to_map( Events[Event].map, EventStructure )
|
||||
@ -791,7 +791,7 @@ do -- FSM
|
||||
return function( self, DelaySeconds, ... )
|
||||
|
||||
-- Debug.
|
||||
self:T2( "Delayed Event: " .. EventName )
|
||||
self:T3( "Delayed Event: " .. EventName )
|
||||
|
||||
local CallID = 0
|
||||
if DelaySeconds ~= nil then
|
||||
@ -809,23 +809,23 @@ do -- FSM
|
||||
self._EventSchedules[EventName] = CallID
|
||||
|
||||
-- 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
|
||||
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
|
||||
end
|
||||
else
|
||||
|
||||
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
|
||||
else
|
||||
error( "FSM: An asynchronous event trigger requires a DelaySeconds parameter!!! This can be positive or negative! Sorry, but will not process this." )
|
||||
end
|
||||
|
||||
-- Debug.
|
||||
self:T2( { CallID = CallID } )
|
||||
--self:T3( { CallID = CallID } )
|
||||
end
|
||||
|
||||
end
|
||||
@ -846,7 +846,7 @@ do -- FSM
|
||||
function FSM:_gosub( ParentFrom, ParentEvent )
|
||||
local fsmtable = {}
|
||||
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]
|
||||
else
|
||||
return {}
|
||||
@ -893,7 +893,7 @@ do -- FSM
|
||||
end
|
||||
end
|
||||
|
||||
self:T3( { Map, Event } )
|
||||
--self:T3( { Map, Event } )
|
||||
end
|
||||
|
||||
--- Get current state.
|
||||
@ -1150,7 +1150,7 @@ do -- FSM_PROCESS
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @return #FSM_PROCESS
|
||||
function FSM_PROCESS:Copy( Controllable, Task )
|
||||
self:T( { self:GetClassNameAndID() } )
|
||||
--self:T3( { self:GetClassNameAndID() } )
|
||||
|
||||
|
||||
local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS
|
||||
@ -1176,13 +1176,13 @@ do -- FSM_PROCESS
|
||||
|
||||
-- Copy End States
|
||||
for EndStateID, EndState in pairs( self:GetEndStates() ) do
|
||||
self:T( EndState )
|
||||
--self:T3( EndState )
|
||||
NewFsm:AddEndState( EndState )
|
||||
end
|
||||
|
||||
-- Copy the score tables
|
||||
for ScoreID, Score in pairs( self:GetScores() ) do
|
||||
self:T( Score )
|
||||
--self:T3( Score )
|
||||
NewFsm:AddScore( ScoreID, Score.ScoreText, Score.Score )
|
||||
end
|
||||
|
||||
@ -1431,7 +1431,7 @@ do -- FSM_SET
|
||||
-- @param #FSM_SET self
|
||||
-- @return Core.Set#SET_BASE
|
||||
function FSM_SET:Get()
|
||||
return self.Controllable
|
||||
return self.Set
|
||||
end
|
||||
|
||||
function FSM_SET:_call_handler( step, trigger, params, EventName )
|
||||
|
||||
@ -26,19 +26,28 @@
|
||||
--
|
||||
-- ### Authors:
|
||||
--
|
||||
-- * FlightControl : Design & Programming
|
||||
-- * FlightControl (Design & Programming)
|
||||
--
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * funkyfranky
|
||||
-- * Applevangelist
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Core.Point
|
||||
-- @image Core_Coordinate.JPG
|
||||
|
||||
|
||||
|
||||
|
||||
do -- 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
|
||||
|
||||
|
||||
@ -201,44 +210,69 @@ do -- COORDINATE
|
||||
ClassName = "COORDINATE",
|
||||
}
|
||||
|
||||
--- @field COORDINATE.WaypointAltType
|
||||
--- Waypoint altitude types.
|
||||
-- @type COORDINATE.WaypointAltType
|
||||
-- @field #string BARO Barometric altitude.
|
||||
-- @field #string RADIO Radio altitude.
|
||||
COORDINATE.WaypointAltType = {
|
||||
BARO = "BARO",
|
||||
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 = {
|
||||
TurningPoint = "Turning Point",
|
||||
FlyoverPoint = "Fly Over Point",
|
||||
FromParkingArea = "From Parking Area",
|
||||
FromParkingAreaHot = "From Parking Area Hot",
|
||||
FromGroundAreaHot = "From Ground Area Hot",
|
||||
FromGroundArea = "From Ground Area",
|
||||
FromRunway = "From Runway",
|
||||
Landing = "Landing",
|
||||
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 = {
|
||||
TakeOffParking = "TakeOffParking",
|
||||
TakeOffParkingHot = "TakeOffParkingHot",
|
||||
TakeOff = "TakeOffParkingHot",
|
||||
TakeOffGroundHot = "TakeOffGroundHot",
|
||||
TakeOffGround = "TakeOffGround",
|
||||
TurningPoint = "Turning Point",
|
||||
Land = "Land",
|
||||
LandingReFuAr = "LandingReFuAr",
|
||||
LandingReFuAr = "LandingReFuAr",
|
||||
}
|
||||
|
||||
|
||||
--- COORDINATE constructor.
|
||||
-- @param #COORDINATE self
|
||||
-- @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 z The z coordinate of the Vec3 point, pointing to the Right.
|
||||
-- @return #COORDINATE
|
||||
-- @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.
|
||||
-- @return #COORDINATE self
|
||||
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.y = y
|
||||
self.z = z
|
||||
@ -249,7 +283,7 @@ do -- COORDINATE
|
||||
--- COORDINATE constructor.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE Coordinate.
|
||||
-- @return #COORDINATE
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:NewFromCoordinate( Coordinate )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE
|
||||
@ -263,8 +297,8 @@ do -- COORDINATE
|
||||
--- Create a new COORDINATE object from Vec2 coordinates.
|
||||
-- @param #COORDINATE self
|
||||
-- @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.
|
||||
-- @return #COORDINATE
|
||||
-- @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 self
|
||||
function COORDINATE:NewFromVec2( Vec2, LandHeightAdd )
|
||||
|
||||
local LandHeight = land.getHeight( Vec2 )
|
||||
@ -274,8 +308,6 @@ do -- COORDINATE
|
||||
|
||||
local self = self:New( Vec2.x, LandHeight, Vec2.y ) -- #COORDINATE
|
||||
|
||||
self:F2( self )
|
||||
|
||||
return self
|
||||
|
||||
end
|
||||
@ -283,7 +315,7 @@ do -- COORDINATE
|
||||
--- Create a new COORDINATE object from Vec3 coordinates.
|
||||
-- @param #COORDINATE self
|
||||
-- @param DCS#Vec3 Vec3 The Vec3 point.
|
||||
-- @return #COORDINATE
|
||||
-- @return #COORDINATE self
|
||||
function COORDINATE:NewFromVec3( Vec3 )
|
||||
|
||||
local self = self:New( Vec3.x, Vec3.y, Vec3.z ) -- #COORDINATE
|
||||
@ -292,6 +324,22 @@ do -- COORDINATE
|
||||
|
||||
return self
|
||||
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.
|
||||
-- @param #COORDINATE self
|
||||
@ -647,7 +695,8 @@ do -- COORDINATE
|
||||
local y=X*math.sin(phi)+Y*math.cos(phi)
|
||||
|
||||
-- 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
|
||||
|
||||
--- 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 )
|
||||
self:F2( { OuterRadius, InnerRadius } )
|
||||
|
||||
return COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) )
|
||||
local coord=COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) )
|
||||
return coord
|
||||
end
|
||||
|
||||
|
||||
@ -1611,8 +1661,8 @@ do -- COORDINATE
|
||||
if x and y then
|
||||
local vec2={ x = x, y = y }
|
||||
coord=COORDINATE:NewFromVec2(vec2)
|
||||
end
|
||||
return coord
|
||||
end
|
||||
return coord
|
||||
end
|
||||
|
||||
|
||||
@ -1718,7 +1768,7 @@ do -- COORDINATE
|
||||
return self:GetSurfaceType()==land.SurfaceType.LAND
|
||||
end
|
||||
|
||||
--- Checks if the surface type is road.
|
||||
--- Checks if the surface type is land.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #boolean If true, the surface type at the coordinate is land.
|
||||
function COORDINATE:IsSurfaceTypeLand()
|
||||
@ -1758,10 +1808,9 @@ do -- COORDINATE
|
||||
--- Creates an explosion at the point of a certain intensity.
|
||||
-- @param #COORDINATE self
|
||||
-- @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
|
||||
function COORDINATE:Explosion( ExplosionIntensity, Delay )
|
||||
self:F2( { ExplosionIntensity } )
|
||||
ExplosionIntensity=ExplosionIntensity or 100
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, self.Explosion, self, ExplosionIntensity)
|
||||
@ -1773,11 +1822,17 @@ do -- COORDINATE
|
||||
|
||||
--- Creates an illumination bomb at the point.
|
||||
-- @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
|
||||
function COORDINATE:IlluminationBomb(power)
|
||||
self:F2()
|
||||
trigger.action.illuminationBomb( self:GetVec3(), power )
|
||||
function COORDINATE:IlluminationBomb(Power, Delay)
|
||||
Power=Power or 1000
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, self.IlluminationBomb, self, Power)
|
||||
else
|
||||
trigger.action.illuminationBomb(self:GetVec3(), Power)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
@ -2075,7 +2130,7 @@ do -- COORDINATE
|
||||
--- Circle to all.
|
||||
-- Creates a circle on the map with a given radius, color, fill color, and outline.
|
||||
-- @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 #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default).
|
||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||
|
||||
@ -317,7 +317,7 @@ end
|
||||
--- Stop dispatcher.
|
||||
-- @param #SCHEDULEDISPATCHER self
|
||||
-- @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 )
|
||||
self:F2( { Stop = CallID, Scheduler = Scheduler } )
|
||||
|
||||
|
||||
@ -238,7 +238,7 @@ end
|
||||
-- @param #number Stop Time interval in seconds after which the scheduler will be stoppe.
|
||||
-- @param #number TraceLevel Trace level [0,3]. Default 3.
|
||||
-- @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 )
|
||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||
self:T3( { SchedulerArguments } )
|
||||
@ -273,7 +273,7 @@ end
|
||||
|
||||
--- (Re-)Starts the schedules or a specific schedule if a valid ScheduleID is provided.
|
||||
-- @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 )
|
||||
self:F3( { ScheduleID } )
|
||||
self:T(string.format("Starting scheduler ID=%s", tostring(ScheduleID)))
|
||||
|
||||
@ -233,7 +233,9 @@ do -- SET_BASE
|
||||
-- @param Core.Base#BASE Object The object itself.
|
||||
-- @return Core.Base#BASE The added BASE 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
|
||||
if self.Set[ObjectName] then
|
||||
@ -263,6 +265,32 @@ do -- SET_BASE
|
||||
|
||||
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.
|
||||
-- @param #SET_BASE self
|
||||
@ -1397,6 +1425,22 @@ do -- SET_GROUP
|
||||
return self
|
||||
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.
|
||||
-- @param #SET_GROUP self
|
||||
-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for.
|
||||
@ -5907,12 +5951,13 @@ do -- SET_ZONE_GOAL
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function SET_ZONE_GOAL:OnEventNewZoneGoal( EventData )
|
||||
|
||||
self:I( { "New Zone Capture Coalition", EventData } )
|
||||
self:I( { "Zone Capture Coalition", EventData.ZoneGoal } )
|
||||
-- Debug info.
|
||||
self:T( { "New Zone Capture Coalition", EventData } )
|
||||
self:T( { "Zone Capture Coalition", EventData.ZoneGoal } )
|
||||
|
||||
if 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 )
|
||||
end
|
||||
end
|
||||
@ -5963,3 +6008,618 @@ do -- SET_ZONE_GOAL
|
||||
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
|
||||
|
||||
@ -2423,8 +2423,7 @@ end
|
||||
-- @param #SPAWN self
|
||||
-- @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.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
|
||||
function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, Vec3, SpawnIndex } )
|
||||
|
||||
@ -2493,8 +2492,7 @@ end
|
||||
-- @param #SPAWN self
|
||||
-- @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.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
|
||||
function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, SpawnIndex } )
|
||||
|
||||
@ -2510,8 +2508,7 @@ end
|
||||
-- @param #SPAWN self
|
||||
-- @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.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
|
||||
-- @usage
|
||||
--
|
||||
-- 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 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.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
|
||||
-- @usage
|
||||
--
|
||||
-- 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 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.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
|
||||
-- @usage
|
||||
--
|
||||
-- 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 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.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
|
||||
-- @usage
|
||||
--
|
||||
-- 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 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.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil when nothing was spawned.
|
||||
-- @return Wrapper.Group#GROUP that was spawned or #nil if nothing was spawned.
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnZone = ZONE:New( ZoneName )
|
||||
|
||||
@ -34,8 +34,6 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # 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.
|
||||
@ -107,19 +105,17 @@ TIMER = {
|
||||
--- Timer ID.
|
||||
_TIMERID=0
|
||||
|
||||
--- Timer data base.
|
||||
--_TIMERDB={}
|
||||
|
||||
--- TIMER class version.
|
||||
-- @field #string version
|
||||
TIMER.version="0.1.1"
|
||||
TIMER.version="0.1.2"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot.
|
||||
-- TODO: Write docs.
|
||||
-- TODO: Randomization.
|
||||
-- TODO: Pause/unpause.
|
||||
-- DONE: Write docs.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
@ -156,9 +152,6 @@ function TIMER:New(Function, ...)
|
||||
-- Log id.
|
||||
self.lid=string.format("TIMER UID=%d | ", self.uid)
|
||||
|
||||
-- Add to DB.
|
||||
--_TIMERDB[self.uid]=self
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -219,10 +212,7 @@ function TIMER:Stop(Delay)
|
||||
|
||||
-- Not running any more.
|
||||
self.isrunning=false
|
||||
|
||||
-- Remove DB entry.
|
||||
--_TIMERDB[self.uid]=nil
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@ -239,6 +229,15 @@ function TIMER:SetMaxFunctionCalls(Nmax)
|
||||
return self
|
||||
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.
|
||||
-- @param #TIMER self
|
||||
-- @return #boolean If `true`, the timer is running.
|
||||
|
||||
@ -288,6 +288,23 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1
|
||||
return self.Coordinate
|
||||
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.
|
||||
-- @param #ZONE_BASE self
|
||||
@ -1161,24 +1178,54 @@ end
|
||||
|
||||
--- Returns a random Vec2 location within the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @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 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 #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.
|
||||
function ZONE_RADIUS:GetRandomVec2( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
local _inner = inner or 0
|
||||
local _outer = outer or self:GetRadius()
|
||||
local Vec2 = self:GetVec2()
|
||||
local _inner = inner or 0
|
||||
local _outer = outer or self:GetRadius()
|
||||
|
||||
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);
|
||||
if surfacetypes and type(surfacetypes)~="table" then
|
||||
surfacetypes={surfacetypes}
|
||||
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
|
||||
|
||||
--- 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.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @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.
|
||||
-- @return Core.Point#COORDINATE
|
||||
function ZONE_RADIUS:GetRandomCoordinate( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
-- @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 #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 Core.Point#COORDINATE The random coordinate.
|
||||
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
|
||||
end
|
||||
@ -1301,7 +1348,7 @@ function ZONE:New( ZoneName )
|
||||
|
||||
-- Error!
|
||||
if not Zone then
|
||||
error( "Zone " .. ZoneName .. " does not exist." )
|
||||
env.error( "ERROR: Zone " .. ZoneName .. " does not exist!" )
|
||||
return nil
|
||||
end
|
||||
|
||||
@ -1941,26 +1988,28 @@ end
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return DCS#Vec2 The Vec2 coordinate.
|
||||
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...
|
||||
local Vec2Found = false
|
||||
local Vec2
|
||||
-- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
|
||||
|
||||
-- Get the bounding square.
|
||||
local BS = self:GetBoundingSquare()
|
||||
|
||||
self:T2( BS )
|
||||
local Nmax=1000 ; local n=0
|
||||
while n<Nmax do
|
||||
|
||||
while Vec2Found == false do
|
||||
Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) }
|
||||
self:T2( Vec2 )
|
||||
if self:IsVec2InZone( Vec2 ) then
|
||||
Vec2Found = true
|
||||
-- Random point in the bounding square.
|
||||
local Vec2={x=math.random(BS.x1, BS.x2), y=math.random(BS.y1, BS.y2)}
|
||||
|
||||
-- Check if this is in the polygon.
|
||||
if self:IsVec2InZone(Vec2) then
|
||||
return Vec2
|
||||
end
|
||||
|
||||
n=n+1
|
||||
end
|
||||
|
||||
self:T2( Vec2 )
|
||||
|
||||
return Vec2
|
||||
self:E("Could not find a random point in the polygon zone!")
|
||||
return nil
|
||||
end
|
||||
|
||||
--- 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
|
||||
|
||||
--- @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
|
||||
|
||||
|
||||
@ -2180,6 +2232,20 @@ do -- ZONE_AIRBASE
|
||||
|
||||
self._.ZoneAirbase = Airbase
|
||||
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.
|
||||
_EVENTDISPATCHER:CreateEventNewZone( self )
|
||||
@ -2194,9 +2260,9 @@ do -- ZONE_AIRBASE
|
||||
return self._.ZoneAirbase
|
||||
end
|
||||
|
||||
--- Returns the current location of the @{Wrapper.Group}.
|
||||
--- Returns the current location of the AIRBASE.
|
||||
-- @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()
|
||||
self:F( self.ZoneName )
|
||||
|
||||
@ -2214,24 +2280,6 @@ do -- ZONE_AIRBASE
|
||||
return ZoneVec2
|
||||
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.
|
||||
-- @param #ZONE_AIRBASE self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
|
||||
@ -471,6 +471,22 @@ do -- Types
|
||||
--@field #boolean lateActivated
|
||||
--@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 --
|
||||
|
||||
|
||||
@ -498,6 +514,10 @@ do -- Object
|
||||
--- @function [parent=#Object] isExist
|
||||
-- @param #Object self
|
||||
-- @return #boolean
|
||||
|
||||
--- @function [parent=#Object] isActive
|
||||
-- @param #Object self
|
||||
-- @return #boolean
|
||||
|
||||
--- @function [parent=#Object] destroy
|
||||
-- @param #Object self
|
||||
@ -1130,6 +1150,11 @@ do -- Unit
|
||||
-- @function [parent=#Unit] getAmmo
|
||||
-- @param #Unit self
|
||||
-- @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.
|
||||
-- @function [parent=#Unit] getSensors
|
||||
|
||||
@ -349,7 +349,6 @@ RANGE.Defaults={
|
||||
boxlength=3000,
|
||||
boxwidth=300,
|
||||
goodpass=20,
|
||||
goodhitrange=25,
|
||||
foulline=610,
|
||||
}
|
||||
|
||||
@ -2570,7 +2569,7 @@ function RANGE:_DisplayBombTargets(_unitname)
|
||||
end
|
||||
end
|
||||
|
||||
self:_DisplayMessageToGroup(_unit,_text, 60, true, true)
|
||||
self:_DisplayMessageToGroup(_unit,_text, 120, true, true)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -80,12 +80,14 @@
|
||||
-- @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 #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 #boolean respawnafterdestroyed If true, warehouse is respawned after it was destroyed. Assets are kept.
|
||||
-- @field #number respawndelay Delay before respawn in seconds.
|
||||
-- @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 Ops.FlightControl#FLIGHTCONTROL flightcontrol Flight control of this warehouse.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- Have your assets at the right place at the right time - or not!
|
||||
@ -1590,7 +1592,8 @@ WAREHOUSE = {
|
||||
autosavepath = nil,
|
||||
autosavefile = nil,
|
||||
saveparking = false,
|
||||
isunit = false,
|
||||
isUnit = false,
|
||||
isShip = false,
|
||||
lowfuelthresh = 0.15,
|
||||
respawnafterdestroyed=false,
|
||||
respawndelay = nil,
|
||||
@ -1599,6 +1602,8 @@ WAREHOUSE = {
|
||||
--- Item of the warehouse stock table.
|
||||
-- @type WAREHOUSE.Assetitem
|
||||
-- @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 #table template The spawn template 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 #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 #number rid The request ID of this asset.
|
||||
-- @field #boolean arrived If true, asset arrived at its destination.
|
||||
--
|
||||
-- @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.
|
||||
-- @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 #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}.
|
||||
-- @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.
|
||||
-- @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.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param Wrapper.Static#STATIC warehouse The physical structure representing the warehouse.
|
||||
-- @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 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/unit representing the warehouse.
|
||||
-- @return #WAREHOUSE self
|
||||
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.
|
||||
if type(warehouse)=="string" then
|
||||
local warehousename=warehouse
|
||||
local warehousename=warehouse
|
||||
warehouse=UNIT:FindByName(warehousename)
|
||||
if warehouse==nil then
|
||||
warehouse=STATIC:FindByName(warehousename, true)
|
||||
self.isunit=false
|
||||
else
|
||||
self.isunit=true
|
||||
end
|
||||
end
|
||||
|
||||
-- Nil check.
|
||||
if warehouse==nil then
|
||||
BASE:E("ERROR: Warehouse does not exist!")
|
||||
env.error("ERROR: Warehouse does not exist!")
|
||||
return nil
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
self.warehouse=warehouse
|
||||
@ -1899,14 +1923,19 @@ function WAREHOUSE:New(warehouse, alias)
|
||||
end
|
||||
|
||||
-- Define warehouse and default spawn zone.
|
||||
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)
|
||||
if self.isShip then
|
||||
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
|
||||
self:SetMarker(true)
|
||||
self:SetReportOff()
|
||||
self:SetRunwayRepairtime()
|
||||
--self:SetVerbosityLevel(0)
|
||||
|
||||
-- Add warehouse to database.
|
||||
_WAREHOUSEDB.Warehouses[self.uid]=self
|
||||
@ -2590,6 +2619,12 @@ function WAREHOUSE:SetSpawnZone(zone, maxdist)
|
||||
return self
|
||||
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.
|
||||
-- @param #WAREHOUSE self
|
||||
@ -2631,9 +2666,8 @@ end
|
||||
--- Check parking ID.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param Wrapper.Airbase#AIRBASE.ParkingSpot spot Parking spot.
|
||||
-- @param Wrapper.Airbase#AIRBASE airbase The airbase.
|
||||
-- @return #boolean If true, parking is valid.
|
||||
function WAREHOUSE:_CheckParkingValid(spot, airbase)
|
||||
function WAREHOUSE:_CheckParkingValid(spot)
|
||||
|
||||
if self.parkingIDs==nil then
|
||||
return true
|
||||
@ -2648,6 +2682,25 @@ function WAREHOUSE:_CheckParkingValid(spot, airbase)
|
||||
return false
|
||||
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.
|
||||
-- @param #WAREHOUSE self
|
||||
@ -3088,14 +3141,16 @@ end
|
||||
-- @param #WAREHOUSE self
|
||||
-- @return DCS#Vec3 The 3D vector of the warehouse.
|
||||
function WAREHOUSE:GetVec3()
|
||||
return self.warehouse:GetVec3()
|
||||
local vec3=self.warehouse:GetVec3()
|
||||
return vec3
|
||||
end
|
||||
|
||||
--- Get 2D vector of warehouse static.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @return DCS#Vec2 The 2D vector of the warehouse.
|
||||
function WAREHOUSE:GetVec2()
|
||||
return self.warehouse:GetVec2()
|
||||
local vec2=self.warehouse:GetVec2()
|
||||
return vec2
|
||||
end
|
||||
|
||||
|
||||
@ -3164,18 +3219,6 @@ function WAREHOUSE:GetAssignment(request)
|
||||
return tostring(request.assignment)
|
||||
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.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #number uid The unique ID of the warehouse.
|
||||
@ -3291,7 +3334,7 @@ end
|
||||
|
||||
--- Check if runway is operational.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @return #boolean If true, runway is operational.
|
||||
-- @return #boolean If `true`, runway is operational.
|
||||
function WAREHOUSE:IsRunwayOperational()
|
||||
if self.airbase then
|
||||
if self.runwaydestroyed then
|
||||
@ -3327,6 +3370,27 @@ function WAREHOUSE:GetRunwayRepairtime()
|
||||
return 0
|
||||
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
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -3537,7 +3601,7 @@ function WAREHOUSE:onafterStatus(From, Event, To)
|
||||
self:_PrintQueue(self.pending, "Queue pending")
|
||||
|
||||
-- Check fuel for all assets.
|
||||
self:_CheckFuel()
|
||||
--self:_CheckFuel()
|
||||
|
||||
-- Update warhouse marker on F10 map.
|
||||
self:_UpdateWarehouseMarkText()
|
||||
@ -3921,6 +3985,8 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu
|
||||
|
||||
-- Asset is not spawned.
|
||||
asset.spawned=false
|
||||
asset.requested=false
|
||||
asset.isReserved=false
|
||||
asset.iscargo=nil
|
||||
asset.arrived=nil
|
||||
|
||||
@ -3971,9 +4037,21 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu
|
||||
-- Destroy group if it is alive.
|
||||
if group:IsAlive()==true then
|
||||
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.
|
||||
group:Destroy() --(false)
|
||||
|
||||
local opsgroup=_DATABASE:GetOpsGroup(group:GetName())
|
||||
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
|
||||
|
||||
else
|
||||
@ -4099,6 +4177,8 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
|
||||
asset.skill=skill
|
||||
asset.assignment=assignment
|
||||
asset.spawned=false
|
||||
asset.requested=false
|
||||
asset.isReserved=false
|
||||
asset.life0=group:GetLife0()
|
||||
asset.damage=0
|
||||
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.
|
||||
-- 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)
|
||||
end
|
||||
|
||||
@ -4467,6 +4547,11 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request)
|
||||
return
|
||||
end
|
||||
|
||||
-- Trigger event.
|
||||
if spawngroup then
|
||||
self:__AssetSpawned(0.01, spawngroup, _assetitem, Request)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Init problem table.
|
||||
@ -4883,6 +4968,13 @@ function WAREHOUSE:onbeforeArrived(From, Event, To, group)
|
||||
local asset=self:FindAssetInDB(group)
|
||||
|
||||
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
|
||||
-- Asset already arrived (e.g. if multiple units trigger the event via landing).
|
||||
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.
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@ -5288,24 +5381,6 @@ function WAREHOUSE:onafterRunwayRepaired(From, Event, To)
|
||||
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.
|
||||
-- @param #WAREHOUSE self
|
||||
-- @param #string From From state.
|
||||
@ -5320,6 +5395,24 @@ function WAREHOUSE:onafterAssetSpawned(From, Event, To, group, asset, request)
|
||||
|
||||
-- Sete asset state to spawned.
|
||||
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.
|
||||
local n=0
|
||||
@ -5670,15 +5763,15 @@ function WAREHOUSE:_SpawnAssetRequest(Request)
|
||||
if asset.category==Group.Category.GROUND then
|
||||
|
||||
-- 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
|
||||
|
||||
-- Spawn air units.
|
||||
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
|
||||
_group=self:_SpawnAssetAircraft(_alias, asset, Request, nil, UnControlled)
|
||||
_group=self:_SpawnAssetAircraft(_alias, asset, Request, nil, UnControlled, Request.lateActivation)
|
||||
end
|
||||
|
||||
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!
|
||||
|
||||
-- Spawn naval assets.
|
||||
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone)
|
||||
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.spawnzone, Request.lateActivation)
|
||||
end
|
||||
|
||||
--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
|
||||
|
||||
-- Spawn naval assets.
|
||||
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.portzone)
|
||||
_group=self:_SpawnAssetGroundNaval(_alias, asset, Request, self.portzone, Request.lateActivation)
|
||||
|
||||
else
|
||||
self:E(self.lid.."ERROR: Unknown asset category!")
|
||||
end
|
||||
|
||||
-- Trigger event.
|
||||
if _group then
|
||||
self:__AssetSpawned(0.01, _group, asset, Request)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -5713,9 +5811,9 @@ end
|
||||
-- @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 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.
|
||||
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
|
||||
|
||||
@ -5758,6 +5856,9 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aiof
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Late activation.
|
||||
template.lateActivation=lateactivated
|
||||
|
||||
template.route.points[1].x = coord.x
|
||||
template.route.points[1].y = coord.z
|
||||
@ -5769,14 +5870,6 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aiof
|
||||
-- Spawn 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
|
||||
end
|
||||
|
||||
@ -5790,8 +5883,9 @@ end
|
||||
-- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias.
|
||||
-- @param #table parking Parking data for this asset.
|
||||
-- @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.
|
||||
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
|
||||
|
||||
@ -6043,18 +6137,10 @@ function WAREHOUSE:_RouteGround(group, request)
|
||||
end
|
||||
|
||||
for n,wp in ipairs(Waypoints) do
|
||||
env.info(n)
|
||||
local tf=self:_SimpleTaskFunctionWP("warehouse:_PassingWaypoint",group, n, #Waypoints)
|
||||
group:SetTaskWaypoint(wp, tf)
|
||||
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.
|
||||
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())))
|
||||
|
||||
-- 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.
|
||||
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)
|
||||
|
||||
if asset and request then
|
||||
|
||||
|
||||
-- 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)))
|
||||
|
||||
-- Set born to 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
|
||||
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.
|
||||
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!")
|
||||
--valid=false
|
||||
valid=false
|
||||
return false
|
||||
end
|
||||
|
||||
-- No ground assets directly to or from ships.
|
||||
@ -7641,7 +7699,7 @@ function WAREHOUSE:_SimpleTaskFunction(Function, group)
|
||||
local DCSScript = {}
|
||||
|
||||
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.
|
||||
else
|
||||
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 = {}
|
||||
|
||||
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.
|
||||
else
|
||||
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
|
||||
|
||||
-- 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.
|
||||
local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE
|
||||
@ -8045,10 +8103,10 @@ function WAREHOUSE:_GetIDsFromGroup(group)
|
||||
end
|
||||
|
||||
-- Debug info
|
||||
self:T(self.lid..string.format("Group Name = %s", tostring(name)))
|
||||
self:T(self.lid..string.format("Warehouse ID = %s", tostring(wid)))
|
||||
self:T(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("Group Name = %s", tostring(name)))
|
||||
self:T3(self.lid..string.format("Warehouse ID = %s", tostring(wid)))
|
||||
self:T3(self.lid..string.format("Asset ID = %s", tostring(aid)))
|
||||
self:T3(self.lid..string.format("Request ID = %s", tostring(rid)))
|
||||
|
||||
return wid,aid,rid
|
||||
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.
|
||||
function WAREHOUSE:_DebugMessage(text, duration)
|
||||
duration=duration or 20
|
||||
if duration>0 then
|
||||
if self.Debug and duration>0 then
|
||||
MESSAGE:New(text, duration):ToAllIf(self.Debug)
|
||||
end
|
||||
self:T(self.lid..text)
|
||||
@ -9047,11 +9105,11 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination)
|
||||
|
||||
-- Hot start.
|
||||
if asset.takeoffType and asset.takeoffType==COORDINATE.WaypointType.TakeOffParkingHot then
|
||||
env.info("FF hot")
|
||||
--env.info("FF hot")
|
||||
_type=COORDINATE.WaypointType.TakeOffParkingHot
|
||||
_action=COORDINATE.WaypointAction.FromParkingAreaHot
|
||||
else
|
||||
env.info("FF cold")
|
||||
--env.info("FF cold")
|
||||
end
|
||||
|
||||
|
||||
|
||||
@ -82,11 +82,19 @@ __Moose.Include( 'Scripts/Moose/Ops/OpsGroup.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/FlightGroup.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/NavyGroup.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/Platoon.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/Legion.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/Commander.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/OpsTransport.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Ops/CSAR.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_Air.lua' )
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -3628,36 +3628,39 @@ function AIRBOSS:_CheckAIStatus()
|
||||
-- Unit
|
||||
local unit=element.unit
|
||||
|
||||
-- Get lineup and distance to carrier.
|
||||
local lineup=self:_Lineup(unit, true)
|
||||
if unit and unit:IsAlive() then
|
||||
|
||||
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 distance=UTILS.MetersToNM(dist)
|
||||
local dist=unitcoord:Get2DDistance(self:GetCoord())
|
||||
|
||||
-- Altitude in ft.
|
||||
local alt=UTILS.MetersToFeet(unitcoord.y)
|
||||
-- Distance in NM.
|
||||
local distance=UTILS.MetersToNM(dist)
|
||||
|
||||
-- Check if parameters are right and flight is in the groove.
|
||||
if lineup<2 and distance<=0.75 and alt<500 and not element.ballcall then
|
||||
-- Altitude in ft.
|
||||
local alt=UTILS.MetersToFeet(unitcoord.y)
|
||||
|
||||
-- Paddles: Call the ball!
|
||||
self:RadioTransmission(self.LSORadio, self.LSOCall.CALLTHEBALL, nil, nil, nil, true)
|
||||
-- Check if parameters are right and flight is in the groove.
|
||||
if lineup<2 and distance<=0.75 and alt<500 and not element.ballcall then
|
||||
|
||||
-- Pilot: "405, Hornet Ball, 3.2"
|
||||
self:_LSOCallAircraftBall(element.onboard,self:_GetACNickname(unit:GetTypeName()), self:_GetFuelState(unit)/1000)
|
||||
-- Paddles: Call the ball!
|
||||
self:RadioTransmission(self.LSORadio, self.LSOCall.CALLTHEBALL, nil, nil, nil, true)
|
||||
|
||||
-- Paddles: Roger ball after 0.5 seconds.
|
||||
self:RadioTransmission(self.LSORadio, self.LSOCall.ROGERBALL, nil, nil, 0.5, true)
|
||||
-- Pilot: "405, Hornet Ball, 3.2"
|
||||
self:_LSOCallAircraftBall(element.onboard,self:_GetACNickname(unit:GetTypeName()), self:_GetFuelState(unit)/1000)
|
||||
|
||||
-- Flight element called the ball.
|
||||
element.ballcall=true
|
||||
-- Paddles: Roger ball after 0.5 seconds.
|
||||
self:RadioTransmission(self.LSORadio, self.LSOCall.ROGERBALL, nil, nil, 0.5, true)
|
||||
|
||||
-- This is for the whole flight. Maybe we need it.
|
||||
flight.ballcall=true
|
||||
-- Flight element called the ball.
|
||||
element.ballcall=true
|
||||
|
||||
-- This is for the whole flight. Maybe we need it.
|
||||
flight.ballcall=true
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@ -6263,6 +6266,11 @@ function AIRBOSS:_ScanCarrierZone()
|
||||
|
||||
-- Get flight group if possible.
|
||||
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.
|
||||
local actype=group:GetTypeName()
|
||||
@ -6276,10 +6284,10 @@ function AIRBOSS:_ScanCarrierZone()
|
||||
local putintomarshal=false
|
||||
|
||||
-- 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.ishelo then
|
||||
if flight.isHelo then
|
||||
else
|
||||
putintomarshal=true
|
||||
end
|
||||
@ -6320,10 +6328,7 @@ function AIRBOSS:_ScanCarrierZone()
|
||||
|
||||
else
|
||||
|
||||
-- Unknown new AI flight. Create a new flight group.
|
||||
if not self:_IsHuman(group) then
|
||||
self:_CreateFlightGroup(group)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
475
Moose Development/Moose/Ops/Brigade.lua
Normal file
475
Moose Development/Moose/Ops/Brigade.lua
Normal 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
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
2190
Moose Development/Moose/Ops/Chief.lua
Normal file
2190
Moose Development/Moose/Ops/Chief.lua
Normal file
File diff suppressed because it is too large
Load Diff
1037
Moose Development/Moose/Ops/Cohort.lua
Normal file
1037
Moose Development/Moose/Ops/Cohort.lua
Normal file
File diff suppressed because it is too large
Load Diff
1489
Moose Development/Moose/Ops/Commander.lua
Normal file
1489
Moose Development/Moose/Ops/Commander.lua
Normal file
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
2516
Moose Development/Moose/Ops/Legion.lua
Normal file
2516
Moose Development/Moose/Ops/Legion.lua
Normal file
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
2205
Moose Development/Moose/Ops/OpsTransport.lua
Normal file
2205
Moose Development/Moose/Ops/OpsTransport.lua
Normal file
File diff suppressed because it is too large
Load Diff
1203
Moose Development/Moose/Ops/OpsZone.lua
Normal file
1203
Moose Development/Moose/Ops/OpsZone.lua
Normal file
File diff suppressed because it is too large
Load Diff
202
Moose Development/Moose/Ops/Platoon.lua
Normal file
202
Moose Development/Moose/Ops/Platoon.lua
Normal 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.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -36,7 +36,6 @@
|
||||
-- @field #number modexcounter Counter to incease modex number for assets.
|
||||
-- @field #string callsignName Callsign name.
|
||||
-- @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 engageRange Mission range in meters.
|
||||
-- @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 radioModu Radio modulation the squad uses.
|
||||
-- @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
|
||||
--
|
||||
@ -64,37 +64,23 @@
|
||||
SQUADRON = {
|
||||
ClassName = "SQUADRON",
|
||||
verbose = 0,
|
||||
lid = nil,
|
||||
name = nil,
|
||||
templatename = nil,
|
||||
aircrafttype = nil,
|
||||
assets = {},
|
||||
missiontypes = {},
|
||||
repairtime = 0,
|
||||
maintenancetime= 0,
|
||||
livery = nil,
|
||||
skill = nil,
|
||||
modex = nil,
|
||||
modexcounter = 0,
|
||||
callsignName = nil,
|
||||
callsigncounter= 11,
|
||||
airwing = nil,
|
||||
Ngroups = nil,
|
||||
engageRange = nil,
|
||||
tankerSystem = nil,
|
||||
refuelSystem = nil,
|
||||
tacanChannel = {},
|
||||
}
|
||||
|
||||
--- SQUADRON class version.
|
||||
-- @field #string version
|
||||
SQUADRON.version="0.5.2"
|
||||
SQUADRON.version="0.8.0"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: Parking spots for squadrons?
|
||||
-- DONE: Parking spots for squadrons?
|
||||
-- DONE: Engage radius.
|
||||
-- DONE: Modex.
|
||||
-- DONE: Call signs.
|
||||
@ -112,97 +98,20 @@ SQUADRON.version="0.5.2"
|
||||
function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName)
|
||||
|
||||
-- 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.
|
||||
self:AddMissionCapability(AUFTRAG.Type.ORBIT)
|
||||
|
||||
-- Generalized attribute.
|
||||
self.attribute=self.templategroup:GetAttribute()
|
||||
|
||||
-- Aircraft type.
|
||||
self.aircrafttype=self.templategroup:GetTypeName()
|
||||
|
||||
|
||||
-- Refueling system.
|
||||
self.refuelSystem=select(2, self.templategroup:GetUnit(1):IsRefuelable())
|
||||
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 ---
|
||||
------------------------
|
||||
|
||||
--- Triggers the FSM event "Start". Starts the SQUADRON. Initializes parameters and starts event handlers.
|
||||
-- @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
|
||||
-- See COHORT class
|
||||
|
||||
return self
|
||||
end
|
||||
@ -211,68 +120,6 @@ end
|
||||
-- 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.
|
||||
-- @param #SQUADRON self
|
||||
-- @param #number nunits Number of units. Must be >=1 and <=4. Default 2.
|
||||
@ -284,6 +131,18 @@ function SQUADRON:SetGrouping(nunits)
|
||||
return self
|
||||
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.
|
||||
-- Spawning on runways is not supported.
|
||||
@ -302,7 +161,7 @@ function SQUADRON:SetTakeoffType(TakeoffType)
|
||||
return self
|
||||
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
|
||||
-- @return #SQUADRON self
|
||||
function SQUADRON:SetTakeoffCold()
|
||||
@ -310,7 +169,7 @@ function SQUADRON:SetTakeoffCold()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set takeoff type hot.
|
||||
--- Set takeoff type hot. All assets of this squadron will be spawned with engines on (hot).
|
||||
-- @param #SQUADRON self
|
||||
-- @return #SQUADRON self
|
||||
function SQUADRON:SetTakeoffHot()
|
||||
@ -318,115 +177,6 @@ function SQUADRON:SetTakeoffHot()
|
||||
return self
|
||||
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.
|
||||
-- @param #SQUADRON self
|
||||
-- @param #number LowFuel Low fuel threshold in percent. Default 25.
|
||||
@ -454,201 +204,17 @@ end
|
||||
-- @param Ops.AirWing#AIRWING Airwing The airwing.
|
||||
-- @return #SQUADRON self
|
||||
function SQUADRON:SetAirwing(Airwing)
|
||||
self.airwing=Airwing
|
||||
self.legion=Airwing
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add airwing asset to squadron.
|
||||
--- Get airwing.
|
||||
-- @param #SQUADRON self
|
||||
-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset.
|
||||
-- @return #SQUADRON self
|
||||
function SQUADRON:AddAsset(Asset)
|
||||
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
|
||||
-- @return Ops.AirWing#AIRWING The airwing.
|
||||
function SQUADRON:GetAirwing(Airwing)
|
||||
return self.legion
|
||||
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
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -685,10 +251,10 @@ function SQUADRON:onafterStatus(From, Event, To)
|
||||
local skill=self.skill and tostring(self.skill) or "N/A"
|
||||
|
||||
local NassetsTot=#self.assets
|
||||
local NassetsInS=self:CountAssetsInStock()
|
||||
local NassetsInS=self:CountAssets(true)
|
||||
local NassetsQP=0 ; local NassetsP=0 ; local NassetsQ=0
|
||||
if self.airwing then
|
||||
NassetsQP, NassetsP, NassetsQ=self.airwing:CountAssetsOnMission(nil, self)
|
||||
if self.legion then
|
||||
NassetsQP, NassetsP, NassetsQ=self.legion:CountAssetsOnMission(nil, self)
|
||||
end
|
||||
|
||||
-- Short info.
|
||||
@ -706,369 +272,10 @@ function SQUADRON:onafterStatus(From, Event, To)
|
||||
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
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- 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
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
-- @type TARGET
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @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 #table targets Table of target objects.
|
||||
-- @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 #table elements Table of target elements/units.
|
||||
-- @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
|
||||
|
||||
--- **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
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- # The TARGET Concept
|
||||
--
|
||||
-- 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.
|
||||
-- @field #string version
|
||||
TARGET.version="0.3.1"
|
||||
TARGET.version="0.5.2"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- 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
|
||||
@ -151,26 +157,23 @@ function TARGET:New(TargetObject)
|
||||
|
||||
-- Increase counter.
|
||||
_TARGETID=_TARGETID+1
|
||||
|
||||
-- Set UID.
|
||||
self.uid=_TARGETID
|
||||
|
||||
if TargetObject then
|
||||
|
||||
-- Add object.
|
||||
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
|
||||
-- Add object.
|
||||
self:AddObject(TargetObject)
|
||||
|
||||
-- Target Name.
|
||||
self.name=self:GetTargetName(Target)
|
||||
end
|
||||
|
||||
-- Target category.
|
||||
self.category=self:GetTargetCategory(Target)
|
||||
-- Defaults.
|
||||
self:SetPriority()
|
||||
self:SetImportance()
|
||||
|
||||
-- Log ID.
|
||||
self.lid=string.format("TARGET #%03d [%s] | ", _TARGETID, tostring(self.category))
|
||||
self.lid=string.format("TARGET #%03d | ", _TARGETID)
|
||||
|
||||
-- Start state.
|
||||
self:SetStartState("Stopped")
|
||||
@ -187,7 +190,7 @@ function TARGET:New(TargetObject)
|
||||
|
||||
self:AddTransition("*", "Damaged", "*") -- Target was damaged.
|
||||
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 ---
|
||||
@ -220,7 +223,6 @@ function TARGET:New(TargetObject)
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
|
||||
-- Start.
|
||||
self:__Start(-1)
|
||||
|
||||
@ -231,12 +233,24 @@ end
|
||||
-- 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 Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, AIRBASE or COORDINATE.
|
||||
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
|
||||
@ -247,31 +261,81 @@ function TARGET:AddObject(Object)
|
||||
for _,object in pairs(set.Set) do
|
||||
self:AddObject(object)
|
||||
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
|
||||
|
||||
---
|
||||
-- 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
|
||||
|
||||
--- 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.
|
||||
-- @param #TARGET self
|
||||
-- @return #boolean If true, target is alive.
|
||||
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
|
||||
|
||||
--- 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.
|
||||
-- @param #TARGET self
|
||||
-- @return #boolean If true, target is dead.
|
||||
function TARGET:IsDead()
|
||||
return self:Is("Dead")
|
||||
local is=self:Is("Dead")
|
||||
return is
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -322,6 +386,11 @@ function TARGET:onafterStatus(From, Event, To)
|
||||
damaged=true
|
||||
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
|
||||
|
||||
-- Target was damaged.
|
||||
@ -422,6 +491,8 @@ function TARGET:onafterObjectDead(From, Event, To, Target)
|
||||
|
||||
if self.Ndestroyed==self.Ntargets0 then
|
||||
|
||||
self.isDestroyed=true
|
||||
|
||||
self:Destroyed()
|
||||
|
||||
else
|
||||
@ -691,6 +762,13 @@ function TARGET:_AddObject(Object)
|
||||
target.Object=Object
|
||||
|
||||
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
|
||||
|
||||
@ -805,10 +883,25 @@ function TARGET:GetLife()
|
||||
return N
|
||||
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.
|
||||
-- @param #TARGET self
|
||||
-- @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)
|
||||
|
||||
if Target.Type==TARGET.ObjectType.GROUP then
|
||||
@ -955,6 +1048,12 @@ function TARGET:GetTargetName(Target)
|
||||
local coord=Target.Object --Core.Point#COORDINATE
|
||||
|
||||
return coord:ToStringMGRS()
|
||||
|
||||
elseif Target.Type==TARGET.ObjectType.ZONE then
|
||||
|
||||
local Zone=Target.Object --Core.Zone#ZONE
|
||||
|
||||
return Zone:GetName()
|
||||
|
||||
end
|
||||
|
||||
@ -965,7 +1064,48 @@ end
|
||||
-- @param #TARGET self
|
||||
-- @return #string Name of the target usually the first object.
|
||||
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
|
||||
|
||||
--- Get coordinate.
|
||||
@ -988,6 +1128,12 @@ function TARGET:GetCoordinate()
|
||||
return nil
|
||||
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.
|
||||
-- @param #TARGET self
|
||||
|
||||
@ -671,7 +671,7 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
else
|
||||
-- Add gender.
|
||||
if gender and gender~="female" then
|
||||
command=command..string.format(" --gender=%s", tostring(gender))
|
||||
command=command..string.format(" -g %s", tostring(gender))
|
||||
end
|
||||
-- Add culture.
|
||||
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 fakeZone = {}
|
||||
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)
|
||||
|
||||
return
|
||||
|
||||
@ -970,6 +970,15 @@ function UTILS.VecDot(a, b)
|
||||
return a.x*b.x + a.y*b.y + a.z*b.z
|
||||
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.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @return #number Norm of the vector.
|
||||
@ -977,6 +986,13 @@ function UTILS.VecNorm(a)
|
||||
return math.sqrt(UTILS.VecDot(a, a))
|
||||
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.
|
||||
-- @param DCS#Vec2 a 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
|
||||
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.
|
||||
-- @param #number h1 Heading one.
|
||||
-- @param #number h2 Heading two.
|
||||
@ -1095,6 +1122,22 @@ function UTILS.VecTranslate(a, distance, angle)
|
||||
return {x=TX, y=a.y, z=TY}
|
||||
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.
|
||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||
-- @param #number angle Rotation angle in degrees.
|
||||
@ -1115,6 +1158,25 @@ function UTILS.Rotate2D(a, angle)
|
||||
return A
|
||||
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.
|
||||
-- @param #number TACANChannel The TACAN channel, i.e. the 10 in "10X".
|
||||
|
||||
@ -142,7 +142,7 @@ AIRBASE.Caucasus = {
|
||||
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
||||
-- * AIRBASE.Nevada.Tonopah_Airport
|
||||
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
||||
--
|
||||
--
|
||||
-- @field Nevada
|
||||
AIRBASE.Nevada = {
|
||||
["Creech_AFB"] = "Creech AFB",
|
||||
@ -197,7 +197,7 @@ AIRBASE.Nevada = {
|
||||
-- * AIRBASE.Normandy.Funtington
|
||||
-- * AIRBASE.Normandy.Tangmere
|
||||
-- * AIRBASE.Normandy.Ford_AF
|
||||
--
|
||||
--
|
||||
-- @field Normandy
|
||||
AIRBASE.Normandy = {
|
||||
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
||||
@ -271,7 +271,7 @@ AIRBASE.Normandy = {
|
||||
-- * AIRBASE.PersianGulf.Sirri_Island
|
||||
-- * AIRBASE.PersianGulf.Tunb_Island_AFB
|
||||
-- * AIRBASE.PersianGulf.Tunb_Kochak
|
||||
--
|
||||
--
|
||||
-- @field PersianGulf
|
||||
AIRBASE.PersianGulf = {
|
||||
["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 #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 #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
|
||||
--
|
||||
@ -507,6 +514,17 @@ AIRBASE.TerminalType = {
|
||||
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.
|
||||
-- @type AIRBASE.Runway
|
||||
-- @field #number heading Heading of the runway in degrees.
|
||||
@ -537,6 +555,9 @@ function AIRBASE:Register(AirbaseName)
|
||||
-- Get descriptors.
|
||||
self.descriptors=self:GetDesc()
|
||||
|
||||
-- Debug info.
|
||||
--self:I({airbase=AirbaseName, descriptors=self.descriptors})
|
||||
|
||||
-- Category.
|
||||
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
|
||||
|
||||
@ -553,13 +574,15 @@ function AIRBASE:Register(AirbaseName)
|
||||
self.isShip=false
|
||||
self.category=Airbase.Category.HELIPAD
|
||||
_DATABASE:AddStatic(AirbaseName)
|
||||
end
|
||||
end
|
||||
else
|
||||
self:E("ERROR: Unknown airbase category!")
|
||||
end
|
||||
|
||||
-- Init parking spots.
|
||||
self:_InitParkingSpots()
|
||||
|
||||
-- Get 2D position vector.
|
||||
local vec2=self:GetVec2()
|
||||
|
||||
-- Init coordinate.
|
||||
@ -1024,6 +1047,8 @@ function AIRBASE:GetParkingSpotsTable(termtype)
|
||||
|
||||
spot.Free=_isfree(_spot) -- updated
|
||||
spot.TOAC=_spot.TO_AC -- updated
|
||||
spot.AirbaseName=self.AirbaseName
|
||||
spot.ClientSpot=nil --TODO
|
||||
|
||||
table.insert(spots, spot)
|
||||
|
||||
@ -1060,6 +1085,8 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC)
|
||||
|
||||
spot.Free=true -- updated
|
||||
spot.TOAC=_spot.TO_AC -- updated
|
||||
spot.AirbaseName=self.AirbaseName
|
||||
spot.ClientSpot=nil --TODO
|
||||
|
||||
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.
|
||||
-- @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)
|
||||
|
||||
|
||||
-- Init default
|
||||
scanradius=scanradius or 50
|
||||
if scanunits==nil then
|
||||
@ -1197,7 +1224,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
az = 17 -- width
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- 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()
|
||||
|
||||
@ -1325,7 +1352,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
||||
|
||||
-- Retrun spots we found, even if there were not enough.
|
||||
return validspots
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Check black and white lists.
|
||||
@ -1344,7 +1371,7 @@ function AIRBASE:_CheckParkingLists(TerminalID)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Check if a whitelist was defined.
|
||||
if self.parkingWhitelist and #self.parkingWhitelist>0 then
|
||||
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.Dubai_Intl 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
|
||||
|
||||
-- 1-->4, 2-->3, 3-->2, 4-->1
|
||||
|
||||
@ -1407,7 +1407,7 @@ function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, E
|
||||
DCSTask = {
|
||||
id = 'Escort',
|
||||
params = {
|
||||
groupId = FollowControllable:GetID(),
|
||||
groupId = FollowControllable and FollowControllable:GetID() or nil,
|
||||
pos = Vec3,
|
||||
lastWptIndexFlag = LastWaypointIndex and true or false,
|
||||
lastWptIndex = LastWaypointIndex,
|
||||
@ -1437,11 +1437,11 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Alti
|
||||
id = 'FireAtPoint',
|
||||
params = {
|
||||
point = Vec2,
|
||||
x=Vec2.x,
|
||||
y=Vec2.y,
|
||||
x = Vec2.x,
|
||||
y = Vec2.y,
|
||||
zoneRadius = Radius,
|
||||
radius = Radius,
|
||||
expendQty = 100, -- dummy value
|
||||
expendQty = 1, -- dummy value
|
||||
expendQtyEnabled = false,
|
||||
alt_type = ASL and 0 or 1
|
||||
}
|
||||
@ -1460,7 +1460,8 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Alti
|
||||
DCSTask.params.weaponType=WeaponType
|
||||
end
|
||||
|
||||
--self:I(DCSTask)
|
||||
--env.info("FF fireatpoint")
|
||||
--BASE:I(DCSTask)
|
||||
|
||||
return DCSTask
|
||||
end
|
||||
@ -2022,22 +2023,20 @@ do -- Patrol methods
|
||||
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 #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 )
|
||||
self:F2( Points )
|
||||
|
||||
local DCSTask = {
|
||||
id = 'Mission',
|
||||
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},
|
||||
},
|
||||
}
|
||||
|
||||
self:T3( { DCSTask } )
|
||||
|
||||
return DCSTask
|
||||
end
|
||||
|
||||
|
||||
@ -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.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return DCS#Position The 3D position vectors of the POSITIONABLE.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
-- @return DCS#Position The 3D position vectors of the POSITIONABLE or #nil if the groups not existing or alive.
|
||||
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
@ -339,9 +338,7 @@ end
|
||||
-- If the first @{Wrapper.Unit} of the group is inactive, it will return false.
|
||||
--
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean true if the group is alive and active.
|
||||
-- @return #boolean false if the group is alive but inactive.
|
||||
-- @return #nil if the group does not exist anymore.
|
||||
-- @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.
|
||||
function GROUP:IsAlive()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
@ -363,8 +360,7 @@ end
|
||||
|
||||
--- Returns if the group is activated.
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean true if group is activated.
|
||||
-- @return #nil The group is not existing or alive.
|
||||
-- @return #boolean `true` if group is activated or `#nil` The group is not existing or alive.
|
||||
function GROUP:IsActive()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
@ -412,7 +408,6 @@ function GROUP:Destroy( GenerateEvent, delay )
|
||||
self:F2( self.GroupName )
|
||||
|
||||
if delay and delay>0 then
|
||||
--SCHEDULER:New(nil, GROUP.Destroy, {self, GenerateEvent}, delay)
|
||||
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
|
||||
else
|
||||
|
||||
|
||||
@ -56,8 +56,7 @@ end
|
||||
-- If the Identifiable is not alive, nil is returned.
|
||||
-- If the Identifiable is alive, true is returned.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return #boolean true if Identifiable is alive.
|
||||
-- @return #nil if the Identifiable is not existing or is not alive.
|
||||
-- @return #boolean true if Identifiable is alive or `#nil` if the Identifiable is not existing or is not alive.
|
||||
function IDENTIFIABLE:IsAlive()
|
||||
self:F3( self.IdentifiableName )
|
||||
|
||||
@ -77,11 +76,8 @@ end
|
||||
--- Returns DCS Identifiable object name.
|
||||
-- The function provides access to non-activated objects too.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return #string The name of the DCS Identifiable.
|
||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
||||
-- @return #string The name of the DCS Identifiable or `#nil`.
|
||||
function IDENTIFIABLE:GetName()
|
||||
self:F2( self.IdentifiableName )
|
||||
|
||||
local IdentifiableName = self.IdentifiableName
|
||||
return IdentifiableName
|
||||
end
|
||||
@ -148,8 +144,7 @@ end
|
||||
|
||||
--- Returns coalition of the Identifiable.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return DCS#coalition.side The side of the coalition.
|
||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
||||
-- @return DCS#coalition.side The side of the coalition or `#nil` The DCS Identifiable is not existing or alive.
|
||||
function IDENTIFIABLE:GetCoalition()
|
||||
self:F2( self.IdentifiableName )
|
||||
|
||||
@ -190,8 +185,7 @@ end
|
||||
|
||||
--- Returns country of the Identifiable.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return DCS#country.id The country identifier.
|
||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
||||
-- @return DCS#country.id The country identifier or `#nil` The DCS Identifiable is not existing or alive.
|
||||
function IDENTIFIABLE:GetCountry()
|
||||
self:F2( self.IdentifiableName )
|
||||
|
||||
@ -222,8 +216,7 @@ end
|
||||
|
||||
--- Returns Identifiable descriptor. Descriptor type depends on Identifiable category.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return DCS#Object.Desc The Identifiable descriptor.
|
||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
||||
-- @return DCS#Object.Desc The Identifiable descriptor or `#nil` The DCS Identifiable is not existing or alive.
|
||||
function IDENTIFIABLE:GetDesc()
|
||||
self:F2( self.IdentifiableName )
|
||||
|
||||
@ -242,8 +235,7 @@ end
|
||||
--- Check if the Object has the attribute.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @param #string AttributeName The attribute name.
|
||||
-- @return #boolean true if the attribute exists.
|
||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
||||
-- @return #boolean true if the attribute exists or `#nil` The DCS Identifiable is not existing or alive.
|
||||
function IDENTIFIABLE:HasAttribute( AttributeName )
|
||||
self:F2( self.IdentifiableName )
|
||||
|
||||
@ -266,8 +258,10 @@ function IDENTIFIABLE:GetCallsign()
|
||||
return ''
|
||||
end
|
||||
|
||||
|
||||
--- Gets the threat level.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return #number Threat level.
|
||||
-- @return #string Type.
|
||||
function IDENTIFIABLE:GetThreatLevel()
|
||||
|
||||
return 0, "Scenery"
|
||||
end
|
||||
|
||||
@ -1368,8 +1368,6 @@ do -- Cargo
|
||||
return self.__.Cargo
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Remove cargo.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param Core.Cargo#CARGO Cargo
|
||||
@ -1414,17 +1412,15 @@ do -- Cargo
|
||||
return ItemCount
|
||||
end
|
||||
|
||||
-- --- Get Cargo Bay Free Volume in m3.
|
||||
-- -- @param #POSITIONABLE self
|
||||
-- -- @return #number CargoBayFreeVolume
|
||||
-- function POSITIONABLE:GetCargoBayFreeVolume()
|
||||
-- local CargoVolume = 0
|
||||
-- for CargoName, Cargo in pairs( self.__.Cargo ) do
|
||||
-- CargoVolume = CargoVolume + Cargo:GetVolume()
|
||||
-- end
|
||||
-- return self.__.CargoBayVolumeLimit - CargoVolume
|
||||
-- end
|
||||
--
|
||||
--- Get the number of infantry soldiers that can be embarked into an aircraft (airplane or helicopter).
|
||||
-- Returns `nil` for ground or ship units.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return #number Descent number of soldiers that fit into the unit. Returns `#nil` for ground and ship units.
|
||||
function POSITIONABLE:GetTroopCapacity()
|
||||
local DCSunit=self:GetDCSObject() --DCS#Unit
|
||||
local capacity=DCSunit:getDescentCapacity()
|
||||
return capacity
|
||||
end
|
||||
|
||||
--- Get Cargo Bay Free Weight in kg.
|
||||
-- @param #POSITIONABLE self
|
||||
@ -1443,55 +1439,97 @@ do -- Cargo
|
||||
return self.__.CargoBayWeightLimit - CargoWeight
|
||||
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.
|
||||
-- @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 )
|
||||
|
||||
if WeightLimit then
|
||||
if WeightLimit then
|
||||
---
|
||||
-- User defined value
|
||||
---
|
||||
self.__.CargoBayWeightLimit = WeightLimit
|
||||
elseif self.__.CargoBayWeightLimit~=nil then
|
||||
-- Value already set ==> Do nothing!
|
||||
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.
|
||||
if self:IsAir() then
|
||||
local Desc = self:GetDesc()
|
||||
self:F({Desc=Desc})
|
||||
|
||||
-- Max takeoff weight if DCS descriptors have unrealstic values.
|
||||
local Weights = {
|
||||
["C-17A"] = 35000, --77519 cannot be used, because it loads way too much apcs and infantry.,
|
||||
["C-130"] = 22000 --The real value cannot be used, because it loads way too much apcs and infantry.,
|
||||
-- C-17A
|
||||
-- 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
|
||||
local Desc = self:GetDesc()
|
||||
self:F({Desc=Desc})
|
||||
|
||||
-- Hard coded cargo weights in kg.
|
||||
local Weights = {
|
||||
["Type_071"] = 245000,
|
||||
["LHA_Tarawa"] = 500000,
|
||||
["Ropucha-class"] = 150000,
|
||||
["Dry-cargo ship-1"] = 70000,
|
||||
["Dry-cargo ship-2"] = 70000,
|
||||
["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.
|
||||
["LST_Mk2"] =2100000, -- Can carry 2100 tons according to wiki source!
|
||||
["Type_071"] = 245000,
|
||||
["LHA_Tarawa"] = 500000,
|
||||
["Ropucha-class"] = 150000,
|
||||
["Dry-cargo ship-1"] = 70000,
|
||||
["Dry-cargo ship-2"] = 70000,
|
||||
["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.
|
||||
["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
|
||||
local Desc = self:GetDesc()
|
||||
|
||||
-- Hard coded number of soldiers.
|
||||
local Weights = {
|
||||
["AAV7"] = 25,
|
||||
["Bedford_MWD"] = 8, -- new by kappa
|
||||
@ -1537,12 +1575,28 @@ do -- Cargo
|
||||
["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
|
||||
end
|
||||
end
|
||||
|
||||
self:F({CargoBayWeightLimit = self.__.CargoBayWeightLimit})
|
||||
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
|
||||
|
||||
--- Signal a flare at the position of the POSITIONABLE.
|
||||
|
||||
@ -524,6 +524,63 @@ function UNIT:IsTanker()
|
||||
return tanker, system
|
||||
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.
|
||||
-- @param Wrapper.Unit#UNIT self
|
||||
@ -999,7 +1056,7 @@ function UNIT:GetThreatLevel()
|
||||
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
|
||||
not Attributes["ATGM"] then ThreatLevel = 3
|
||||
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
|
||||
|
||||
ThreatText = ThreatLevels[ThreatLevel+1]
|
||||
|
||||
@ -79,9 +79,15 @@ Ops/Auftrag.lua
|
||||
Ops/OpsGroup.lua
|
||||
Ops/FlightGroup.lua
|
||||
Ops/NavyGroup.lua
|
||||
Ops/Cohort.lua
|
||||
Ops/Squadron.lua
|
||||
Ops/Platoon.lua
|
||||
Ops/Legion.lua
|
||||
Ops/AirWing.lua
|
||||
Ops/Brigade.lua
|
||||
Ops/Intelligence.lua
|
||||
Ops/Commander.lua
|
||||
Ops/Chief.lua
|
||||
Ops/CSAR.lua
|
||||
Ops/CTLD.lua
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user