diff --git a/Moose Development/Debugger/debugger.lua b/Moose Development/Debugger/debugger.lua index a5f606f74..c8150ad13 100644 --- a/Moose Development/Debugger/debugger.lua +++ b/Moose Development/Debugger/debugger.lua @@ -1953,7 +1953,7 @@ local function refct_from_id(id) -- refct = refct_from_id(CTypeID) unsigned = refct.unsigned, size = bit.band(bit.rshift(ctype.info, 16), 127), } - refct.bool, refct.const, refct.volatile, refct.unsigned = nil + refct.bool, refct.const, refct.volatile, refct.unsigned = nil, nil, nil, nil end if CT[4] then -- Merge sibling attributes onto this type. diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index d86c20bbe..08c4a7273 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -17,7 +17,9 @@ --- The AI_A2A_CAP class implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. --- +-- +-- ![Banner Image](..\Images\deprecated.png) +-- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) -- -- The AI_A2A_CAP is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event. diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index bbfaa868b..8e8e3df05 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -32,7 +32,9 @@ -- [DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0S4KMNUUJpaUs6zZHjLKNx) -- -- === --- +-- +-- ![Banner Image](..\Images\deprecated.png) +-- -- # QUICK START GUIDE -- -- There are basically two classes available to model an A2A defense system. diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index 8f85f3cd2..3d56e0227 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -19,6 +19,8 @@ --- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- The AI_A2A_GCI is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event. -- -- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index 71b392db1..6c7ce68cc 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -15,6 +15,8 @@ --- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) -- -- The AI_A2A_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_PATROL process can be started using the **Start** event. diff --git a/Moose Development/Moose/AI/AI_A2G_BAI.lua b/Moose Development/Moose/AI/AI_A2G_BAI.lua index 7a6a80fca..8a39ad0d2 100644 --- a/Moose Development/Moose/AI/AI_A2G_BAI.lua +++ b/Moose Development/Moose/AI/AI_A2G_BAI.lua @@ -15,7 +15,9 @@ -- @extends AI.AI_A2A_Engage#AI_A2A_Engage -- TODO: Documentation. This class does not exist, unable to determine what it extends. --- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders. --- +-- +-- ![Banner Image](..\Images\deprecated.png) +-- -- # Developer Note -- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE diff --git a/Moose Development/Moose/AI/AI_A2G_CAS.lua b/Moose Development/Moose/AI/AI_A2G_CAS.lua index 16c9cb976..d4c0fe417 100644 --- a/Moose Development/Moose/AI/AI_A2G_CAS.lua +++ b/Moose Development/Moose/AI/AI_A2G_CAS.lua @@ -18,6 +18,8 @@ -- -- # Developer Note -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE -- Therefore, this class is considered to be deprecated -- diff --git a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua index 0396e4e9f..5aff7ad4f 100644 --- a/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2G_Dispatcher.lua @@ -36,6 +36,8 @@ -- -- # QUICK START GUIDE -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- The following class is available to model an A2G defense system. -- -- AI_A2G_DISPATCHER is the main A2G defense class that models the A2G defense system. diff --git a/Moose Development/Moose/AI/AI_A2G_SEAD.lua b/Moose Development/Moose/AI/AI_A2G_SEAD.lua index 17f9f86f5..4ee600fcf 100644 --- a/Moose Development/Moose/AI/AI_A2G_SEAD.lua +++ b/Moose Development/Moose/AI/AI_A2G_SEAD.lua @@ -19,6 +19,8 @@ --- Implements the core functions to SEAD intruders. Use the Engage trigger to intercept intruders. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- The AI_A2G_SEAD is assigned a @{Wrapper.Group} and this must be done before the AI_A2G_SEAD process can be started using the **Start** event. -- -- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. diff --git a/Moose Development/Moose/AI/AI_Air.lua b/Moose Development/Moose/AI/AI_Air.lua index d3d11cf16..1b0bf8cac 100644 --- a/Moose Development/Moose/AI/AI_Air.lua +++ b/Moose Development/Moose/AI/AI_Air.lua @@ -15,6 +15,7 @@ --- The AI_AIR class implements the core functions to operate an AI @{Wrapper.Group}. -- +-- ![Banner Image](..\Images\deprecated.png) -- -- # 1) AI_AIR constructor -- diff --git a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua index 9e5939aa0..ad309f0c2 100644 --- a/Moose Development/Moose/AI/AI_Air_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Air_Dispatcher.lua @@ -36,6 +36,8 @@ -- -- # QUICK START GUIDE -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- The following class is available to model an AIR defense system. -- -- AI_AIR_DISPATCHER is the main AIR defense class that models the AIR defense system. diff --git a/Moose Development/Moose/AI/AI_Air_Engage.lua b/Moose Development/Moose/AI/AI_Air_Engage.lua index ff3327421..772f10b2e 100644 --- a/Moose Development/Moose/AI/AI_Air_Engage.lua +++ b/Moose Development/Moose/AI/AI_Air_Engage.lua @@ -13,12 +13,14 @@ --- @type AI_AIR_ENGAGE +--- @type AI_AIR_ENGAGE -- @extends AI.AI_AIR#AI_AIR --- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- The AI_AIR_ENGAGE is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_ENGAGE process can be started using the **Start** event. -- -- The AI will fly towards the random 3D point within the patrol zone, using a random speed within the given altitude and speed limits. diff --git a/Moose Development/Moose/AI/AI_Air_Patrol.lua b/Moose Development/Moose/AI/AI_Air_Patrol.lua index 2b4e1a937..b389d10ff 100644 --- a/Moose Development/Moose/AI/AI_Air_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Air_Patrol.lua @@ -15,6 +15,8 @@ --- The AI_AIR_PATROL class implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) -- -- The AI_AIR_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_AIR_PATROL process can be started using the **Start** event. diff --git a/Moose Development/Moose/AI/AI_Air_Squadron.lua b/Moose Development/Moose/AI/AI_Air_Squadron.lua index 6651a92a5..7356c1baa 100644 --- a/Moose Development/Moose/AI/AI_Air_Squadron.lua +++ b/Moose Development/Moose/AI/AI_Air_Squadron.lua @@ -13,7 +13,7 @@ --- @type AI_AIR_SQUADRON +--- @type AI_AIR_SQUADRON -- @extends Core.Base#BASE @@ -21,6 +21,8 @@ -- -- # Developer Note -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE -- Therefore, this class is considered to be deprecated -- diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua index 237bb9ab0..46c5c086a 100644 --- a/Moose Development/Moose/AI/AI_BAI.lua +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -38,6 +38,8 @@ --- Implements the core functions to provide BattleGround Air Interdiction in an Engage @{Core.Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. -- -- ![HoldAndEngage](..\Presentations\AI_BAI\Dia3.JPG) diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index 827a17764..f11a12c18 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -33,8 +33,9 @@ -- @field Wrapper.Group#GROUP Test -- @extends Core.Fsm#FSM_SET - ---- Monitors and manages as many replacement AI groups as there are +--- ![Banner Image](..\Images\deprecated.png) +-- +-- Monitors and manages as many replacement AI groups as there are -- CLIENTS in a SET\_CLIENT collection, which are not occupied by human players. -- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions. -- diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index 35604e9f9..8f1f1501b 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -39,6 +39,8 @@ --- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) -- -- The AI_CAP_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event. diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index d1ac6cdac..8fec1ca3f 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -38,6 +38,9 @@ -- @extends AI.AI_Patrol#AI_PATROL_ZONE --- Implements the core functions to provide Close Air Support in an Engage @{Core.Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. +-- +-- ![Banner Image](..\Images\deprecated.png) +-- -- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. -- -- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 0bd6ab9ea..eac91c668 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -9,12 +9,14 @@ -- @module AI.AI_Cargo -- @image Cargo.JPG --- @type AI_CARGO +--- @type AI_CARGO -- @extends Core.Fsm#FSM_CONTROLLABLE --- Base class for the dynamic cargo handling capability for AI groups. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Carriers can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI_CARGO module uses the @{Cargo.Cargo} capabilities within the MOOSE framework. -- CARGO derived objects must be declared within the mission to make the AI_CARGO object recognize the cargo. diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index fc9037fea..baf202d0d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -15,6 +15,8 @@ --- Brings a dynamic cargo handling capability for an AI vehicle group. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. -- -- The AI_CARGO_APC class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 3e9589d95..ff814d6be 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -14,6 +14,8 @@ --- Brings a dynamic cargo handling capability for an AI airplane group. +-- +-- ![Banner Image](..\Images\deprecated.png) -- -- Airplane carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation between airbases. -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 71b7f9f43..b63c76192 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -22,6 +22,8 @@ -- -- === -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- # The dispatcher concept. -- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 3d98522e1..ca0ec07d8 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -36,6 +36,8 @@ --- A dynamic cargo transportation capability for AI groups. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. -- -- The AI_CARGO_DISPATCHER_APC module is derived from the AI_CARGO_DISPATCHER module. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index d3a7c78ac..b70a43f4c 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -30,6 +30,8 @@ --- Brings a dynamic cargo handling capability for AI groups. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation. -- -- The AI_CARGO_DISPATCHER_AIRPLANE module is derived from the AI_CARGO_DISPATCHER module. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index b219c78b0..1c228e872 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -31,6 +31,8 @@ --- A dynamic cargo handling capability for AI helicopter groups. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation. -- -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua index 6fc670e40..f0afedeb3 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Ship.lua @@ -29,6 +29,8 @@ --- A dynamic cargo transportation capability for AI groups. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Naval vessels can be mobilized to semi-intelligently transport cargo within the simulation. -- -- The AI_CARGO_DISPATCHER_SHIP module is derived from the AI_CARGO_DISPATCHER module. diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 207a1ab8e..61294ba6d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -15,6 +15,8 @@ --- Brings a dynamic cargo handling capability for an AI helicopter group. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Helicopter carriers can be mobilized to intelligently transport infantry and other cargo within the simulation. -- -- The AI_CARGO_HELICOPTER class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. diff --git a/Moose Development/Moose/AI/AI_Cargo_Ship.lua b/Moose Development/Moose/AI/AI_Cargo_Ship.lua index 5639c52da..259c2009f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Ship.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Ship.lua @@ -14,6 +14,8 @@ --- Brings a dynamic cargo handling capability for an AI naval group. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Naval ships can be utilized to transport cargo around the map following naval shipping lanes. -- The AI_CARGO_SHIP class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. -- @{Cargo.Cargo} must be declared within the mission or warehouse to make the AI_CARGO_SHIP recognize the cargo. diff --git a/Moose Development/Moose/AI/AI_Escort.lua b/Moose Development/Moose/AI/AI_Escort.lua index ad325ed94..c32312f27 100644 --- a/Moose Development/Moose/AI/AI_Escort.lua +++ b/Moose Development/Moose/AI/AI_Escort.lua @@ -25,6 +25,8 @@ -- -- Allows you to interact with escorting AI on your flight and take the lead. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Each escorting group can be commanded with a complete set of radio commands (radio menu in your flight, and then F10). -- -- The radio commands will vary according the category of the group. The richest set of commands are with helicopters and airPlanes. diff --git a/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua b/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua index ff4c0ddfe..d2366568f 100644 --- a/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Escort_Dispatcher.lua @@ -23,6 +23,8 @@ -- -- # Developer Note -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE -- Therefore, this class is considered to be deprecated -- diff --git a/Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua b/Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua index 160c2beed..1b0370b54 100644 --- a/Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua +++ b/Moose Development/Moose/AI/AI_Escort_Dispatcher_Request.lua @@ -21,6 +21,8 @@ --- Models the assignment of AI escorts to player flights upon request using the radio menu. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- # Developer Note -- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE diff --git a/Moose Development/Moose/AI/AI_Escort_Request.lua b/Moose Development/Moose/AI/AI_Escort_Request.lua index 3251d3717..843f75160 100644 --- a/Moose Development/Moose/AI/AI_Escort_Request.lua +++ b/Moose Development/Moose/AI/AI_Escort_Request.lua @@ -25,6 +25,8 @@ -- -- Allows you to interact with escorting AI on your flight and take the lead. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Each escorting group can be commanded with a complete set of radio commands (radio menu in your flight, and then F10). -- -- The radio commands will vary according the category of the group. The richest set of commands are with helicopters and airPlanes. diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index 326d6365b..425585e90 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -40,6 +40,8 @@ --- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader. +-- +-- ![Banner Image](..\Images\deprecated.png) -- -- AI_FORMATION makes AI @{Wrapper.Group#GROUP}s fly in formation of various compositions. -- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 408e3b1a6..e8d21ed44 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -48,6 +48,8 @@ --- Implements the core functions to patrol a @{Core.Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) -- -- The AI_PATROL_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_PATROL_ZONE process can be started using the **Start** event. diff --git a/Moose Development/Moose/Actions/Act_Account.lua b/Moose Development/Moose/Actions/Act_Account.lua index a72a7b445..83fd6bf5c 100644 --- a/Moose Development/Moose/Actions/Act_Account.lua +++ b/Moose Development/Moose/Actions/Act_Account.lua @@ -1,6 +1,6 @@ --- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occurring on UNITs. -- --- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG) +-- ![Banner Image](..\Images\deprecated.png) -- -- === -- @@ -8,9 +8,11 @@ -- @image MOOSE.JPG do -- ACT_ACCOUNT - + --- # @{#ACT_ACCOUNT} FSM class, extends @{Core.Fsm#FSM_PROCESS} - -- + -- + -- ![Banner Image](..\Images\deprecated.png) + -- -- ## ACT_ACCOUNT state machine: -- -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. @@ -133,7 +135,7 @@ do -- ACT_ACCOUNT -- @param #string Event -- @param #string From -- @param #string To - function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To, Event ) + function ACT_ACCOUNT:onafterEvent( ProcessUnit, From, Event, To ) self:__NoMore( 1 ) end diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index 3b261cfb1..1f99b86e7 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -1,6 +1,8 @@ --- (SP) (MP) (FSM) Accept or reject process for player (task) assignments. -- -- === +-- +-- ![Banner Image](..\Images\deprecated.png) -- -- # @{#ACT_ASSIGN} FSM template class, extends @{Core.Fsm#FSM_PROCESS} -- diff --git a/Moose Development/Moose/Actions/Act_Assist.lua b/Moose Development/Moose/Actions/Act_Assist.lua index 2ae132ac1..2017c6ab9 100644 --- a/Moose Development/Moose/Actions/Act_Assist.lua +++ b/Moose Development/Moose/Actions/Act_Assist.lua @@ -1,5 +1,6 @@ --- (SP) (MP) (FSM) Route AI or players through waypoints or to zones. -- +-- ![Banner Image](..\Images\deprecated.png) -- ## ACT_ASSIST state machine: -- -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index b387b6584..548c6a846 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -2,6 +2,8 @@ -- -- === -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- # @{#ACT_ROUTE} FSM class, extends @{Core.Fsm#FSM_PROCESS} -- -- ## ACT_ROUTE state machine: diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 413beb514..76307dd78 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -2,6 +2,8 @@ -- -- === -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- # 1) MOOSE Cargo System. -- -- #### Those who have used the mission editor, know that the DCS mission editor provides cargo facilities. diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index c64016fd8..b9dcb2761 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -22,6 +22,9 @@ do -- CARGO_CRATE -- @type CARGO_CRATE -- @extends Cargo.Cargo#CARGO_REPRESENTABLE + --- + -- ![Banner Image](..\Images\deprecated.png) + -- --- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_CRATE objects to and from carriers. -- diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 45e1b5948..b756f456c 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -26,6 +26,8 @@ do -- CARGO_GROUP -- @extends Cargo.Cargo#CARGO_REPORTABLE --- Defines a cargo that is represented by a @{Wrapper.Group} object within the simulator. + -- + -- ![Banner Image](..\Images\deprecated.png) -- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers. -- -- The above cargo classes are used by the following AI_CARGO_ classes to allow AI groups to transport cargo: diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 81bc5d95e..90209e8c0 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -32,6 +32,8 @@ do -- CARGO_SLINGLOAD -- -- # Developer Note -- + -- ![Banner Image](..\Images\deprecated.png) + -- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE -- Therefore, this class is considered to be deprecated -- diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index a76469870..bc504d003 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -30,6 +30,8 @@ do -- CARGO_UNIT -- -- # Developer Note -- + -- ![Banner Image](..\Images\deprecated.png) + -- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE -- Therefore, this class is considered to be deprecated -- diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index b1a12e740..81cf8892d 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -974,7 +974,7 @@ do -- Scheduling -- @param #BASE self -- @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, ... }. + -- @param ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }. -- @return #string The Schedule ID of the planned schedule. function BASE:ScheduleOnce( Start, SchedulerFunction, ... ) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 4aec2393a..85bde137c 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -872,6 +872,8 @@ end -- @return Wrapper.Group#GROUP The found GROUP. function DATABASE:FindGroup( GroupName ) + if type(GroupName) ~= "string" or GroupName == "" then return end + local GroupFound = self.GROUPS[GroupName] if GroupFound == nil and GroupName ~= nil and self.Templates.Groups[GroupName] == nil then diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 4165bdc57..ff74527f2 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -206,7 +206,7 @@ end function MESSAGE:ToGroup( Group, Settings ) self:F( Group.GroupName ) - if Group then + if Group and Group:IsAlive() then if self.MessageType then local Settings = Settings or (Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() )) or _SETTINGS -- Core.Settings#SETTINGS @@ -231,7 +231,7 @@ end function MESSAGE:ToUnit( Unit, Settings ) self:F( Unit.IdentifiableName ) - if Unit then + if Unit and Unit:IsAlive() then if self.MessageType then local Settings = Settings or ( Unit and _DATABASE:GetPlayerSettings( Unit:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 4a5d26e22..a74356985 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -25,7 +25,7 @@ do -- COORDINATE - --- + --- Coordinate class -- @type COORDINATE -- @field #string ClassName Name of the class -- @field #number x Component of the 3D vector. @@ -59,6 +59,10 @@ do -- COORDINATE -- * @{#COORDINATE.SmokeOrange}(): To smoke the point in orange. -- * @{#COORDINATE.SmokeWhite}(): To smoke the point in white. -- * @{#COORDINATE.SmokeGreen}(): To smoke the point in green. + -- * @{#COORDINATE.SetSmokeOffsetDirection}(): To set an offset point direction for smoke. + -- * @{#COORDINATE.SetSmokeOffsetDistance}(): To set an offset point distance for smoke. + -- * @{#COORDINATE.SwitchSmokeOffsetOn}(): To set an offset point for smoke to on. + -- * @{#COORDINATE.SwitchSmokeOffsetOff}(): To set an offset point for smoke to off. -- -- ## 2.2) Flare -- @@ -2118,14 +2122,112 @@ do -- COORDINATE end - --- Smokes the point in a color. + --- Create colored smoke the point. The smoke we last up to 5 min (DCS limitation) but you can optionally specify a shorter duration or stop it manually. -- @param #COORDINATE self - -- @param Utilities.Utils#SMOKECOLOR SmokeColor - -- @param #string name (Optional) Name if you want to stop the smoke early (normal duration: 5mins) - function COORDINATE:Smoke( SmokeColor, name ) - self:F2( { SmokeColor } ) - self.firename = name or "Smoke-"..math.random(1,100000) - trigger.action.smoke( self:GetVec3(), SmokeColor, self.firename ) + -- @param #number SmokeColor Color of smoke, e.g. `SMOKECOLOR.Green` for green smoke. + -- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min. + -- @param #number Delay (Optional) Delay before the smoke is started in seconds. + -- @param #string Name (Optional) Name if you want to stop the smoke early (normal duration: 5mins) + -- @param #boolean Offset (Optional) If true, offset the smokle a bit. + -- @param #number Direction (Optional) If Offset is true this is the direction of the offset, 1-359 (degrees). Default random. + -- @param #number Distance (Optional) If Offset is true this is the distance of the offset in meters. Default random 10-20. + -- @return #COORDINATE self + function COORDINATE:Smoke( SmokeColor, Duration, Delay, Name, Offset,Direction,Distance) + self:F2( { SmokeColor, Name, Duration, Delay, Offset } ) + + SmokeColor=SmokeColor or SMOKECOLOR.Green + + if Delay and Delay>0 then + self:ScheduleOnce(Delay, COORDINATE.Smoke, self, SmokeColor, Duration, 0, Name, Direction,Distance) + else + + -- Create a name which is used to stop the smoke manually + self.firename = Name or "Smoke-"..math.random(1,100000) + + -- Create smoke + if Offset or self.SmokeOffset then + local Angle = Direction or self:GetSmokeOffsetDirection() + local Distance = Distance or self:GetSmokeOffsetDistance() + local newpos = self:Translate(Distance,Angle,true,false) + local newvec3 = newpos:GetVec3() + trigger.action.smoke( newvec3, SmokeColor, self.firename ) + else + trigger.action.smoke( self:GetVec3(), SmokeColor, self.firename ) + end + + -- Stop smoke + if Duration and Duration>0 then + self:ScheduleOnce(Duration, COORDINATE.StopSmoke, self, self.firename ) + end + end + + return self + end + + --- Get the offset direction when using `COORDINATE:Smoke()`. + -- @param #COORDINATE self + -- @return #number Direction in degrees. + function COORDINATE:GetSmokeOffsetDirection() + local direction = self.SmokeOffsetDirection or math.random(1,359) + return direction + end + + --- Set the offset direction when using `COORDINATE:Smoke()`. + -- @param #COORDINATE self + -- @param #number Direction (Optional) This is the direction of the offset, 1-359 (degrees). Default random. + -- @return #COORDINATE self + function COORDINATE:SetSmokeOffsetDirection(Direction) + if self then + self.SmokeOffsetDirection = Direction or math.random(1,359) + return self + else + COORDINATE.SmokeOffsetDirection = Direction or math.random(1,359) + end + end + + --- Get the offset distance when using `COORDINATE:Smoke()`. + -- @param #COORDINATE self + -- @return #number Distance Distance in meters. + function COORDINATE:GetSmokeOffsetDistance() + local distance = self.SmokeOffsetDistance or math.random(10,20) + return distance + end + + --- Set the offset distance when using `COORDINATE:Smoke()`. + -- @param #COORDINATE self + -- @param #number Distance (Optional) This is the distance of the offset in meters. Default random 10-20. + -- @return #COORDINATE self + function COORDINATE:SetSmokeOffsetDistance(Distance) + if self then + self.SmokeOffsetDistance = Distance or math.random(10,20) + return self + else + COORDINATE.SmokeOffsetDistance = Distance or math.random(10,20) + end + end + + --- Set the offset on when using `COORDINATE:Smoke()`. + -- @param #COORDINATE self + -- @return #COORDINATE self + function COORDINATE:SwitchSmokeOffsetOn() + if self then + self.SmokeOffset = true + return self + else + COORDINATE.SmokeOffset = true + end + end + + --- Set the offset off when using `COORDINATE:Smoke()`. + -- @param #COORDINATE self + -- @return #COORDINATE self + function COORDINATE:SwitchSmokeOffsetOff() + if self then + self.SmokeOffset = false + return self + else + COORDINATE.SmokeOffset = false + end end --- Stops smoking the point in a color. @@ -2137,49 +2239,83 @@ do -- COORDINATE --- Smoke the COORDINATE Green. -- @param #COORDINATE self - function COORDINATE:SmokeGreen() - self:F2() - self:Smoke( SMOKECOLOR.Green ) + -- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min. + -- @param #number Delay (Optional) Delay before the smoke is started in seconds. + -- @return #COORDINATE self + function COORDINATE:SmokeGreen(Duration, Delay) + self:Smoke( SMOKECOLOR.Green, Duration, Delay ) + return self end --- Smoke the COORDINATE Red. -- @param #COORDINATE self - function COORDINATE:SmokeRed() - self:F2() - self:Smoke( SMOKECOLOR.Red ) + -- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min. + -- @param #number Delay (Optional) Delay before the smoke is started in seconds. + -- @return #COORDINATE self + function COORDINATE:SmokeRed(Duration, Delay) + self:Smoke( SMOKECOLOR.Red, Duration, Delay ) + return self end --- Smoke the COORDINATE White. -- @param #COORDINATE self - function COORDINATE:SmokeWhite() - self:F2() - self:Smoke( SMOKECOLOR.White ) + -- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min. + -- @param #number Delay (Optional) Delay before the smoke is started in seconds. + -- @return #COORDINATE self + function COORDINATE:SmokeWhite(Duration, Delay) + self:Smoke( SMOKECOLOR.White, Duration, Delay ) + return self end --- Smoke the COORDINATE Orange. -- @param #COORDINATE self - function COORDINATE:SmokeOrange() - self:F2() - self:Smoke( SMOKECOLOR.Orange ) + -- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min. + -- @param #number Delay (Optional) Delay before the smoke is started in seconds. + -- @return #COORDINATE self + function COORDINATE:SmokeOrange(Duration, Delay) + self:Smoke( SMOKECOLOR.Orange, Duration, Delay ) + return self end --- Smoke the COORDINATE Blue. -- @param #COORDINATE self - function COORDINATE:SmokeBlue() - self:F2() - self:Smoke( SMOKECOLOR.Blue ) + -- @param #number Duration (Optional) Duration of the smoke in seconds. DCS stopps the smoke automatically after 5 min. + -- @param #number Delay (Optional) Delay before the smoke is started in seconds. + -- @return #COORDINATE self + function COORDINATE:SmokeBlue(Duration, Delay) + self:Smoke( SMOKECOLOR.Blue, Duration, Delay ) + return self end --- Big smoke and fire at the coordinate. -- @param #COORDINATE self - -- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke). - -- @param #number density (Optional) Smoke density. Number in [0,...,1]. Default 0.5. - -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. - function COORDINATE:BigSmokeAndFire( preset, density, name ) - self:F2( { preset=preset, density=density } ) - density=density or 0.5 - self.firename = name or "Fire-"..math.random(1,10000) - trigger.action.effectSmokeBig( self:GetVec3(), preset, density, self.firename ) + -- @param #number Preset Smoke preset (1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke). + -- @param #number Density (Optional) Smoke density. Number in [0,...,1]. Default 0.5. + -- @param #number Duration (Optional) Duration of the smoke and fire in seconds. + -- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds. + -- @param #string Name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. + -- @return #COORDINATE self + function COORDINATE:BigSmokeAndFire( Preset, Density, Duration, Delay, Name ) + self:F2( { preset=Preset, density=Density } ) + + Preset=Preset or BIGSMOKEPRESET.SmallSmokeAndFire + Density=Density or 0.5 + + if Delay and Delay>0 then + self:ScheduleOnce(Delay, COORDINATE.BigSmokeAndFire, self, Preset, Density, Duration, 0, Name) + else + + self.firename = Name or "Fire-"..math.random(1,10000) + + trigger.action.effectSmokeBig( self:GetVec3(), Preset, Density, self.firename ) + + -- Stop smoke + if Duration and Duration>0 then + self:ScheduleOnce(Duration, COORDINATE.StopBigSmokeAndFire, self, self.firename ) + end + end + + return self end --- Stop big smoke and fire at the coordinate. @@ -2192,82 +2328,98 @@ do -- COORDINATE --- Small smoke and fire at the coordinate. -- @param #COORDINATE self - -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. - -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. - function COORDINATE:BigSmokeAndFireSmall( density, name ) - self:F2( { density=density } ) - density=density or 0.5 - self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire, density, name) + -- @param #number Density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + -- @param #number Duration (Optional) Duration of the smoke and fire in seconds. + -- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds. + -- @param #string Name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. + -- @return #COORDINATE self + function COORDINATE:BigSmokeAndFireSmall( Density, Duration, Delay, Name ) + self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire, Density, Duration, Delay, Name) + return self end --- Medium smoke and fire at the coordinate. -- @param #COORDINATE self -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + -- @param #number Duration (Optional) Duration of the smoke and fire in seconds. + -- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds. -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. - function COORDINATE:BigSmokeAndFireMedium( density, name ) - self:F2( { density=density } ) - density=density or 0.5 - self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire, density, name) + -- @return #COORDINATE self + function COORDINATE:BigSmokeAndFireMedium( Density, Duration, Delay, Name ) + self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire, Density, Duration, Delay, Name) + return self end --- Large smoke and fire at the coordinate. -- @param #COORDINATE self -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + -- @param #number Duration (Optional) Duration of the smoke and fire in seconds. + -- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds. -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. - function COORDINATE:BigSmokeAndFireLarge( density, name ) - self:F2( { density=density } ) - density=density or 0.5 - self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire, density, name) + -- @return #COORDINATE self + function COORDINATE:BigSmokeAndFireLarge( Density, Duration, Delay, Name ) + self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire, Density, Duration, Delay, Name) + return self end --- Huge smoke and fire at the coordinate. -- @param #COORDINATE self -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + -- @param #number Duration (Optional) Duration of the smoke and fire in seconds. + -- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds. -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. - function COORDINATE:BigSmokeAndFireHuge( density, name ) - self:F2( { density=density } ) - density=density or 0.5 - self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire, density, name) + -- @return #COORDINATE self + function COORDINATE:BigSmokeAndFireHuge( Density, Duration, Delay, Name ) + self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire, Density, Duration, Delay, Name) + return self end --- Small smoke at the coordinate. -- @param #COORDINATE self -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + -- @param #number Duration (Optional) Duration of the smoke and fire in seconds. + -- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds. -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. - function COORDINATE:BigSmokeSmall( density, name ) - self:F2( { density=density } ) - density=density or 0.5 - self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke, density, name) + -- @return #COORDINATE self + function COORDINATE:BigSmokeSmall( Density, Duration, Delay, Name ) + self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke, Density, Duration, Delay, Name) + return self end --- Medium smoke at the coordinate. -- @param #COORDINATE self -- @param number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + -- @param #number Duration (Optional) Duration of the smoke and fire in seconds. + -- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds. -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. - function COORDINATE:BigSmokeMedium( density, name ) - self:F2( { density=density } ) - density=density or 0.5 - self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke, density, name) + -- @return #COORDINATE self + function COORDINATE:BigSmokeMedium( Density, Duration, Delay, Name ) + self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke, Density, Duration, Delay, Name) + return self end --- Large smoke at the coordinate. -- @param #COORDINATE self -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + -- @param #number Duration (Optional) Duration of the smoke and fire in seconds. + -- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds. -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. - function COORDINATE:BigSmokeLarge( density, name ) - self:F2( { density=density } ) - density=density or 0.5 - self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke, density,name) + -- @return #COORDINATE self + function COORDINATE:BigSmokeLarge( Density, Duration, Delay, Name ) + self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke, Density, Duration, Delay, Name) + return self end --- Huge smoke at the coordinate. -- @param #COORDINATE self -- @param #number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + -- @param #number Duration (Optional) Duration of the smoke and fire in seconds. + -- @param #number Delay (Optional) Delay before the smoke and fire is started in seconds. -- @param #string name (Optional) Name of the fire to stop it later again if not using the same COORDINATE object. Defaults to "Fire-" plus a random 5-digit-number. - function COORDINATE:BigSmokeHuge( density, name ) - self:F2( { density=density } ) - density=density or 0.5 - self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke, density,name) + -- @return #COORDINATE self + function COORDINATE:BigSmokeHuge( Density, Duration, Delay, Name ) + self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke, Density, Duration, Delay, Name) + return self end --- Flares the point in a color. @@ -2921,8 +3073,10 @@ do -- COORDINATE local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff) local sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff) - if sunrise == "N/R" then return false end - if sunrise == "N/S" then return true end + if type(sunrise) == "string" or type(sunset) == "string" then + if sunrise == "N/R" then return false end + if sunset == "N/S" then return true end + end local time=UTILS.ClockToSeconds(clock) @@ -2940,6 +3094,11 @@ do -- COORDINATE -- Todays sun set in sec. local sunset=self:GetSunset(true) + + if type(sunrise) == "string" or type(sunset) == "string" then + if sunrise == "N/R" then return false end + if sunset == "N/S" then return true end + end -- Seconds passed since midnight. local time=UTILS.SecondsOfToday() diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 8e01b8a73..ca7ec0a95 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -198,7 +198,7 @@ end -- env do -- radio - ---@type radio + --@type radio -- @field #radio.modulation modulation --- diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 0ebe76f4a..8ec4939d7 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -22,7 +22,7 @@ -- @module Functional.Mantis -- @image Functional.Mantis.jpg -- --- Last Update: Mar 2025 +-- Last Update: May 2025 ------------------------------------------------------------------------- --- **MANTIS** class, extends Core.Base#BASE @@ -62,7 +62,9 @@ -- @field #table FilterZones Table of Core.Zone#ZONE Zones Consider SAM groups in this zone(s) only for this MANTIS instance, must be handed as #table of Zone objects. -- @field #boolean SmokeDecoy If true, smoke short range SAM units as decoy if a plane is in firing range. -- @field #number SmokeDecoyColor Color to use, defaults to SMOKECOLOR.White --- @field #number checkcounter Counter for SAM Table refreshes +-- @field #number checkcounter Counter for SAM Table refreshes. +-- @field #number DLinkCacheTime Seconds after which cached contacts in DLink will decay. +-- @field #boolean logsamstatus Log SAM status in dcs.log every cycle if true -- @extends Core.Base#BASE @@ -74,10 +76,9 @@ -- -- * Moose derived Modular, Automatic and Network capable Targeting and Interception System. -- * Controls a network of SAM sites. Uses detection to switch on the SAM site closest to the enemy. --- * **Automatic mode** (default since 0.8) will set-up your SAM site network automatically for you --- * **Classic mode** behaves like before --- * Leverage evasiveness from SEAD, leverage attack range setting --- * Automatic setup of SHORAD based on groups of the class "short-range" +-- * **Automatic mode** (default) will set-up your SAM site network automatically for you. +-- * Leverage evasiveness from SEAD, leverage attack range setting. +-- * Automatic setup of SHORAD based on groups of the class "short-range". -- -- # 0. Base considerations and naming conventions -- @@ -133,10 +134,10 @@ -- -- # 0.1 Set-up in the mission editor -- --- Set up your SAM sites in the mission editor. Name the groups using a systematic approach like above. --- Set up your EWR system in the mission editor. Name the groups using a systematic approach like above. Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc. +-- Set up your SAM sites in the mission editor. Name the groups using a systematic approach like above.Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc. -- Search Radars usually have "SR" or "STR" in their names. Use the encyclopedia in the mission editor to inform yourself. --- Set up your SHORAD systems. They need to be **close** to (i.e. around) the SAM sites to be effective. Use **one** group per SAM location. SA-15 TOR systems offer a good missile defense. +-- Set up your SHORAD systems. They need to be **close** to (i.e. around) the SAM sites to be effective. Use **one unit ** per group (multiple groups) for the SAM location. +-- Else, evasive manoevers might club up all defenders in one place. Red SA-15 TOR systems offer a good missile defense. -- -- [optional] Set up your HQ. Can be any group, e.g. a command vehicle. -- @@ -188,7 +189,7 @@ -- -- ## 2.1 Auto mode features -- --- ### 2.1.1 You can now add Accept-, Reject- and Conflict-Zones to your setup, e.g. to consider borders or de-militarized zones: +-- ### 2.1.1 You can add Accept-, Reject- and Conflict-Zones to your setup, e.g. to consider borders or de-militarized zones: -- -- -- Parameters are tables of Core.Zone#ZONE objects! -- -- This is effectively a 3-stage filter allowing for zone overlap. A coordinate is accepted first when @@ -205,9 +206,6 @@ -- ### 2.1.3 SHORAD/Point defense will automatically be added from SAM sites of type "point" or if the range is less than 5km or if the type is AAA. -- -- ### 2.1.4 Advanced features --- --- -- Option to switch off auto mode **before** you start MANTIS (not recommended) --- mybluemantis.automode = false -- -- -- Option to set the scale of the activation range, i.e. don't activate at the fringes of max range, defaults below. -- -- also see engagerange below. @@ -220,6 +218,12 @@ -- -- -- For some scenarios, like Cold War, it might be useful not to activate SAMs if friendly aircraft are around to avoid death by friendly fire. -- mybluemantis.checkforfriendlies = true +-- +-- ### 2.1.6 Shoot & Scoot +-- +-- -- Option to make the (driveable) SHORAD units drive around and shuffle positions +-- -- We use a SET_ZONE for that, number of zones to consider defaults to three, Random is true for random coordinates and Formation is e.g. "Vee". +-- mybluemantis:AddScootZones(ZoneSet, Number, Random, Formation) -- -- # 3. Default settings [both modes unless stated otherwise] -- @@ -242,26 +246,8 @@ -- E.g. mymantis:SetAdvancedMode( true, 90 ) -- -- Use this option if you want to make use of or allow advanced SEAD tactics. --- --- # 5. Integrate SHORAD [classic mode, not necessary in automode, not recommended for manual setup] --- --- You can also choose to integrate Mantis with @{Functional.Shorad#SHORAD} for protection against HARMs and AGMs manually. When SHORAD detects a missile fired at one of MANTIS' SAM sites, it will activate SHORAD systems in --- the given defense checkradius around that SAM site. Create a SHORAD object first, then integrate with MANTIS like so: --- --- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart() --- myshorad = SHORAD:New("BlueShorad", "Blue SHORAD", SamSet, 22000, 600, "blue") --- -- now set up MANTIS --- mymantis = MANTIS:New("BlueMantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs") --- mymantis:AddShorad(myshorad,720) --- mymantis:Start() -- --- If you systematically name your SHORAD groups starting with "Blue SHORAD" you'll need exactly **one** SHORAD instance to manage all SHORAD groups. --- --- (Optionally) you can remove the link later on with --- --- mymantis:RemoveShorad() --- --- # 6. Integrated SEAD +-- # 5. Integrated SEAD -- -- MANTIS is using @{Functional.Sead#SEAD} internally to both detect and evade HARM attacks. No extra efforts needed to set this up! -- Once a HARM attack is detected, MANTIS (via SEAD) will shut down the radars of the attacked SAM site and take evasive action by moving the SAM @@ -336,6 +322,8 @@ MANTIS = { SmokeDecoy = false, SmokeDecoyColor = SMOKECOLOR.White, checkcounter = 1, + DLinkCacheTime = 120, + logsamstatus = false, } --- Advanced state enumerator @@ -374,7 +362,7 @@ MANTIS.radiusscale[MANTIS.SamType.POINT] = 3 MANTIS.SamData = { ["Hawk"] = { Range=35, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km ["NASAMS"] = { Range=14, Blindspot=0, Height=7, Type="Short", Radar="NSAMS" }, -- AIM 120B - ["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot" }, + ["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot str" }, ["Rapier"] = { Range=10, Blindspot=0, Height=3, Type="Short", Radar="rapier" }, ["SA-2"] = { Range=40, Blindspot=7, Height=25, Type="Medium", Radar="S_75M_Volhov" }, ["SA-3"] = { Range=18, Blindspot=6, Height=18, Type="Short", Radar="5p73 s-125 ln" }, @@ -382,7 +370,8 @@ MANTIS.SamData = { ["SA-6"] = { Range=25, Blindspot=0, Height=8, Type="Medium", Radar="1S91" }, ["SA-10"] = { Range=119, Blindspot=0, Height=18, Type="Long" , Radar="S-300PS 4"}, ["SA-11"] = { Range=35, Blindspot=0, Height=20, Type="Medium", Radar="SA-11" }, - ["Roland"] = { Range=5, Blindspot=0, Height=5, Type="Point", Radar="Roland" }, + ["Roland"] = { Range=6, Blindspot=0, Height=5, Type="Short", Radar="Roland" }, + ["Gepard"] = { Range=5, Blindspot=0, Height=4, Type="Point", Radar="Gepard" }, ["HQ-7"] = { Range=12, Blindspot=0, Height=3, Type="Short", Radar="HQ-7" }, ["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Strela", Point="true" }, ["SA-8"] = { Range=10, Blindspot=0, Height=5, Type="Short", Radar="Osa 9A33" }, @@ -393,6 +382,7 @@ MANTIS.SamData = { ["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" }, ["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Point", Radar="Linebacker", Point="true" }, ["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" }, + ["HEMTT_C-RAM_Phalanx"] = { Range=2, Blindspot=0, Height=2, Type="Point", Radar="HEMTT_C-RAM_Phalanx", Point="true" }, -- units from HDS Mod, multi launcher options is tricky ["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"}, ["SA-17"] = { Range=50, Blindspot=3, Height=30, Type="Medium", Radar="SA-17" }, @@ -625,7 +615,8 @@ do self.advAwacs = false end - + self:SetDLinkCacheTime() + -- Set the string id for output to DCS.log file. self.lid=string.format("MANTIS %s | ", self.name) @@ -658,6 +649,8 @@ do table.insert(self.ewr_templates,awacs) end + self.logsamstatus = false + self:T({self.ewr_templates}) self.SAM_Group = SET_GROUP:New():FilterPrefixes(self.SAM_Templates_Prefix):FilterCoalitions(self.Coalition) @@ -689,7 +682,7 @@ do -- TODO Version -- @field #string version - self.version="0.9.27" + self.version="0.9.30" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) --- FSM Functions --- @@ -1039,6 +1032,16 @@ do end return self end + + --- Function to set how long INTEL DLINK remembers contacts. + -- @param #MANTIS self + -- @param #number seconds Remember this many seconds, at least 5 seconds. + -- @return #MANTIS self + function MANTIS:SetDLinkCacheTime(seconds) + self.DLinkCacheTime = math.abs(seconds or 120) + if self.DLinkCacheTime < 5 then self.DLinkCacheTime = 5 end + return self + end --- Function to set the detection interval -- @param #MANTIS self @@ -1431,7 +1434,9 @@ do --IntelTwo:SetClusterRadius(5000) IntelTwo:Start() - local IntelDlink = INTEL_DLINK:New({IntelOne,IntelTwo},self.name.." DLINK",22,300) + local CacheTime = self.DLinkCacheTime or 120 + local IntelDlink = INTEL_DLINK:New({IntelOne,IntelTwo},self.name.." DLINK",22,CacheTime) + IntelDlink:__Start(1) self:SetUsingDLink(IntelDlink) @@ -1493,7 +1498,7 @@ do elseif chm then SAMData = self.SamDataCH end - --self:T("Looking to auto-match for "..grpname) + --self:I("Looking to auto-match for "..grpname) for _,_unit in pairs(units) do local unit = _unit -- Wrapper.Unit#UNIT local type = string.lower(unit:GetTypeName()) @@ -1694,7 +1699,9 @@ do local grpname = group:GetName() local grpcoord = group:GetCoordinate() local grprange, grpheight,type,blind = self:_GetSAMRange(grpname) - local radaralive = group:IsSAM() + -- TODO the below might stop working at some point after some hours, needs testing + --local radaralive = group:IsSAM() + local radaralive = true table.insert( SAM_Tbl, {grpname, grpcoord, grprange, grpheight, blind, type}) -- make the table lighter, as I don't really use the zone here table.insert( SEAD_Grps, grpname ) if type == MANTIS.SamType.LONG and radaralive then @@ -1856,7 +1863,7 @@ do end --end alive end --end check end --for loop - if self.debug or self.verbose then + if self.debug or self.verbose or self.logsamstatus then for _,_status in pairs(self.SamStateTracker) do if _status == "GREEN" then instatusgreen=instatusgreen+1 @@ -1877,8 +1884,9 @@ do -- @param #MANTIS self -- @param Functional.Detection#DETECTION_AREAS detection Detection object -- @param #boolean dlink + -- @param #boolean reporttolog -- @return #MANTIS self - function MANTIS:_Check(detection,dlink) + function MANTIS:_Check(detection,dlink,reporttolog) self:T(self.lid .. "Check") --get detected set local detset = detection:GetDetectedItemCoordinates() @@ -1905,7 +1913,8 @@ do local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates, i.3=firing range, i.4=firing height instatusred, instatusgreen, activeshorads = self:_CheckLoop(samset,detset,dlink,self.maxclassic) end - if self.debug or self.verbose then + + local function GetReport() local statusreport = REPORT:New("\nMANTIS Status "..self.name) statusreport:Add("+-----------------------------+") statusreport:Add(string.format("+ SAM in RED State: %2d",instatusred)) @@ -1914,7 +1923,15 @@ do statusreport:Add(string.format("+ SHORAD active: %2d",activeshorads)) end statusreport:Add("+-----------------------------+") + return statusreport + end + + if self.debug or self.verbose then + local statusreport = GetReport() MESSAGE:New(statusreport:Text(),10):ToAll():ToLog() + elseif reporttolog == true then + local statusreport = GetReport() + MESSAGE:New(statusreport:Text(),10):ToLog() end return self end @@ -2022,7 +2039,7 @@ do self:T({From, Event, To}) -- check detection if not self.state2flag then - self:_Check(self.Detection,self.DLink) + self:_Check(self.Detection,self.DLink,self.logsamstatus) end local EWRAlive = self:_CheckAnyEWRAlive() diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index f52363ba2..42626071c 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -53,6 +53,8 @@ -- -- # Developer Note -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE. -- Therefore, this class is considered to be deprecated and superseded by the [Functional.Fox](https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/Documentation/Functional.Fox.html) class, which provides the same functionality. -- diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 62b85e828..6c0143823 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -603,7 +603,7 @@ RANGE.MenuF10Root = nil --- Range script version. -- @field #string version -RANGE.version = "2.8.0" +RANGE.version = "2.8.1" -- TODO list: -- TODO: Verbosity level for messages. @@ -2032,10 +2032,10 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV -- Smoke impact point of bomb. if playerData and playerData.smokebombimpact and insidezone then - if playerData and playerData.delaysmoke then - timer.scheduleFunction( self._DelayedSmoke, { coord = impactcoord, color = playerData.smokecolor }, timer.getTime() + self.TdelaySmoke ) + if playerData.delaysmoke then + impactcoord:Smoke(playerData.smokecolor, 30, self.TdelaySmoke) else - impactcoord:Smoke( playerData.smokecolor ) + impactcoord:Smoke(playerData.smokecolor, 30) end end @@ -2102,7 +2102,12 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV result.attackHdg = attackHdg result.attackVel = attackVel result.attackAlt = attackAlt - result.date=os and os.date() or "n/a" + if os and os.date then + result.date=os.date() + else + self:E(self.lid.."os or os.date() not available") + result.date = "n/a" + end -- Add to table. table.insert( _results, result ) @@ -2635,13 +2640,6 @@ end -- Display Messages ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Start smoking a coordinate with a delay. --- @param #table _args Argements passed. -function RANGE._DelayedSmoke( _args ) - _args.coord:Smoke(_args.color) - --trigger.action.smoke( _args.coord:GetVec3(), _args.color ) -end - --- Display top 10 stafing results of a specific player. -- @param #RANGE self -- @param #string _unitName Name of the player unit. diff --git a/Moose Development/Moose/Functional/ZoneGoalCargo.lua b/Moose Development/Moose/Functional/ZoneGoalCargo.lua index 4397f621c..10b91e12f 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCargo.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCargo.lua @@ -7,6 +7,8 @@ -- -- # Developer Note -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Note while this class still works, it is no longer supported as the original author stopped active development of MOOSE -- Therefore, this class is considered to be deprecated -- diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index f53f953d0..107808898 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -2798,7 +2798,7 @@ function ATIS:onafterBroadcast( From, Event, To ) end _RUNACT = subtitle - alltext = alltext .. ";\n" .. subtitle + --alltext = alltext .. ";\n" .. subtitle -- Runway length. if self.rwylength then diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 0eadc412c..d6d7b03a7 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -1731,10 +1731,10 @@ AIRBOSS.Difficulty = { -- @field #table trapsheet Groove data table recorded every 0.5 seconds. -- @field #boolean trapon If true, save trap sheets. -- @field #string debriefschedulerID Debrief scheduler ID. --- +-- -- @field Sound.SRS#MSRS SRS -- @field Sound.SRS#MSRSQUEUE SRSQ --- +-- -- @extends #AIRBOSS.FlightGroup --- Main group level radio menu: F10 Other/Airboss. @@ -1912,6 +1912,9 @@ function AIRBOSS:New( carriername, alias ) -- Set max section members. Default 2. self:SetMaxSectionSize() + -- Set max section distance. Default 100 meters. + self:SetMaxSectionDistance() + -- Set max flights per stack. Default is 2. self:SetMaxFlightsPerStack() @@ -2539,7 +2542,7 @@ function AIRBOSS:AddRecoveryWindow( starttime, stoptime, case, holdingoffset, tu return self end if Tstop <= Tnow then - string.format( "WARNING: Recovery stop time %s already over. Tnow=%s! Recovery window rejected.", UTILS.SecondsToClock( Tstop ), UTILS.SecondsToClock( Tnow ) ) + string.format( "WARNING: Recovery stop time %s already over. Tnow=%s! Recovery window rejected.", UTILS.SecondsToClock( Tstop ), UTILS.SecondsToClock( Tnow ) ) return self end @@ -3066,7 +3069,7 @@ end -- @param #number Port Port of the SRS server, defaults to 5002. -- @param #string Culture (Optional, Airboss Culture) Culture, defaults to "en-US". -- @param #string Gender (Optional, Airboss Gender) Gender, e.g. "male" or "female". Defaults to "male". --- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings. +-- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings. -- @param #string GoogleCreds (Optional) Path to Google credentials, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourgooglekey.json". -- @param #number Volume (Optional) E.g. 0.75. Defaults to 1.0 (loudest). -- @param #table AltBackend (Optional) See MSRS for details. @@ -3088,8 +3091,7 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum self.SRS:SetVolume(Volume or 1) --self.SRS:SetModulations(Modulations) if GoogleCreds then - self.SRS:SetProviderOptionsGoogle(GoogleCreds,GoogleCreds) - self.SRS:SetProvider(MSRS.Provider.GOOGLE) + self.SRS:SetGoogle(GoogleCreds) end if Voice then self.SRS:SetVoice(Voice) @@ -3098,10 +3100,10 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum -- SRSQUEUE self.SRSQ = MSRSQUEUE:New("AIRBOSS") self.SRSQ:SetTransmitOnlyWithPlayers(true) - if not self.PilotRadio then + if not self.PilotRadio then self:SetSRSPilotVoice() end - return self + return self end --- Set LSO radio frequency and modulation. Default frequency is 264 MHz AM. @@ -3344,6 +3346,22 @@ function AIRBOSS:SetMaxSectionSize( nmax ) return self end +--- Set maximum distance up to which section members are allowed (default: 100 meters). +-- @param #AIRBOSS self +-- @param #number dmax Max distance in meters (default 100 m). Minimum is 10 m, maximum is 5000 m. +-- @return #AIRBOSS self +function AIRBOSS:SetMaxSectionDistance( dmax ) + if dmax then + if dmax < 10 then + dmax = 10 + elseif dmax > 5000 then + dmax = 5000 + end + end + self.maxsectiondistance = dmax or 100 + return self +end + --- Set max number of flights per stack. All members of a section count as one "flight". -- @param #AIRBOSS self -- @param #number nmax Number of max allowed flights per stack. Default is two. Minimum is one, maximum is 4. @@ -3623,7 +3641,6 @@ function AIRBOSS:onafterStart( From, Event, To ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self._PlayerLeft ) self:HandleEvent( EVENTS.MissionEnd ) self:HandleEvent( EVENTS.RemoveUnit ) - self:HandleEvent( EVENTS.UnitLost, self.OnEventRemoveUnit ) -- self.StatusScheduler=SCHEDULER:New(self) -- self.StatusScheduler:Schedule(self, self._Status, {}, 1, 0.5) @@ -8724,13 +8741,13 @@ function AIRBOSS:OnEventRemoveUnit( EventData ) -- Nil checks. if EventData == nil then - self:E( self.lid .. "ERROR: EventData=nil in event REMOVEUNIT!" ) - self:E( EventData ) + self:T( self.lid .. "ERROR: EventData=nil in event REMOVEUNIT!" ) + self:T( EventData ) return end if EventData.IniUnit == nil then - self:E( self.lid .. "ERROR: EventData.IniUnit=nil in event REMOVEUNIT!" ) - self:E( EventData ) + self:T( self.lid .. "ERROR: EventData.IniUnit=nil in event REMOVEUNIT!" ) + self:T( EventData ) return end @@ -11598,7 +11615,7 @@ function AIRBOSS:GetHeadingIntoWind_old( vdeck, magnetic, coord ) local function adjustDegreesForWindSpeed(windSpeed) local degreesAdjustment = 0 -- the windspeeds are in m/s - + -- +0 degrees at 15m/s = 37kts -- +0 degrees at 14m/s = 35kts -- +0 degrees at 13m/s = 33kts @@ -11613,7 +11630,7 @@ function AIRBOSS:GetHeadingIntoWind_old( vdeck, magnetic, coord ) -- +20 degrees at 4m/s = 26kts -- +20 degrees at 3m/s = 26kts -- +30 degrees at 2m/s = 26kts 1s - + if windSpeed > 0 and windSpeed < 3 then degreesAdjustment = 30 elseif windSpeed >= 3 and windSpeed < 5 then @@ -11625,7 +11642,7 @@ function AIRBOSS:GetHeadingIntoWind_old( vdeck, magnetic, coord ) elseif windSpeed >= 13 then degreesAdjustment = 0 end - + return degreesAdjustment end @@ -11684,60 +11701,60 @@ function AIRBOSS:GetHeadingIntoWind_new( vdeck, magnetic, coord ) local h=self:GetHeading(magnetic) return h, math.min(vdeck, Vmax) end - + -- Convert wind speed to knots. vwind=UTILS.MpsToKnots(vwind) - + -- Wind to in knots. local windto=(windfrom+180)%360 - + -- Offset angle in rad. We also define the rotation to be clock-wise, which requires a minus sign. local alpha=math.rad(-Offset) - + -- Constant. local C = math.sqrt(math.cos(alpha)^2 / math.sin(alpha)^2 + 1) - + -- Upper limit of desired speed due to max boat speed. local vdeckMax=vwind + math.cos(alpha) * Vmax - + -- Lower limit of desired speed due to min boat speed. local vdeckMin=vwind + math.cos(alpha) * Vmin - - + + -- Speed of ship so it matches the desired speed. local v=0 - - -- Angle wrt. to wind TO-direction + + -- Angle wrt. to wind TO-direction local theta=0 if vdeck>vdeckMax then -- Boat cannot go fast enough - + -- Set max speed. v=Vmax - + -- Calculate theta. theta = math.asin(v/(vwind*C)) - math.asin(-1/C) - + elseif vdeckvwind then -- Too little wind - + -- Set theta to 90° theta=math.pi/2 - + -- Set speed. v = math.sqrt(vdeck^2 - vwind^2) - + else -- Normal case theta = math.asin(vdeck * math.sin(alpha) / vwind) @@ -11746,9 +11763,9 @@ function AIRBOSS:GetHeadingIntoWind_new( vdeck, magnetic, coord ) -- Magnetic heading. local magvar= magnetic and self.magvar or 0 - + -- Ship heading so cross wind is min for the given wind. - local intowind = (540 + (windto - magvar + math.deg(theta) )) % 360 + local intowind = (540 + (windto - magvar + math.deg(theta) )) % 360 return intowind, v end @@ -12206,7 +12223,7 @@ function AIRBOSS:_LSOgrade( playerData ) -- Normal laning part at the beginning local Gb = GXX .. " " .. GIM - -- Number of deviations that occurred at the the beginning of the landing (XX or IM). These are graded like in non-VTOL landings, i.e. on deviations is + -- Number of deviations that occurred at the the beginning of the landing (XX or IM). These are graded like in non-VTOL landings, i.e. on deviations is local N=nXX+nIM local nL=count(Gb, '_')/2 local nS=count(Gb, '%(') @@ -12224,7 +12241,7 @@ function AIRBOSS:_LSOgrade( playerData ) if nL>0 or nLv>1 then -- Larger deviations at XX or IM or at least one larger deviation IC or AR==> "No grade" 2.0 points. - -- In other words, we allow one larger deviation at IC+AR + -- In other words, we allow one larger deviation at IC+AR grade="--" points=2.0 elseif nN>0 or nNv>1 or nLv==1 then @@ -13720,7 +13737,7 @@ function AIRBOSS:CarrierTurnIntoWind( time, vdeck, uturn ) local deltaH = self:_GetDeltaHeading( hdg, hiw ) -- Debug output - self:I( self.lid .. string.format( "Carrier steaming into the wind (%.1f kts). Heading=%03d-->%03d (Delta=%.1f), Speed=%.1f knots, Distance=%.1f NM, Time=%d sec", + self:I( self.lid .. string.format( "Carrier steaming into the wind (%.1f kts). Heading=%03d-->%03d (Delta=%.1f), Speed=%.1f knots, Distance=%.1f NM, Time=%d sec", UTILS.MpsToKnots( vwind ), hdg, hiw, deltaH, speedknots, distNM, speedknots, time ) ) -- Current coordinate. @@ -14932,12 +14949,12 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p if radio == nil or call == nil then return end - + if not self.SRS then - + -- Create a new radio transmission item. local transmission = {} -- #AIRBOSS.Radioitem - + transmission.radio = radio transmission.call = call transmission.Tplay = timer.getAbsTime() + (delay or 0) @@ -14945,49 +14962,49 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p transmission.isplaying = false transmission.Tstarted = nil transmission.loud = loud and call.loud - + -- Player onboard number if sender has one. if self:_IsOnboard( call.modexsender ) then self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall ) end - + -- Play onboard number if receiver has one. if self:_IsOnboard( call.modexreceiver ) then self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall ) end - + -- Add transmission to the right queue. local caller = "" if radio.alias == "LSO" then - + table.insert( self.RQLSO, transmission ) - + caller = "LSOCall" - + -- Schedule radio queue checks. if not self.RQLid then self:T( self.lid .. string.format( "Starting LSO radio queue." ) ) self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 ) end - + elseif radio.alias == "MARSHAL" then - + table.insert( self.RQMarshal, transmission ) - + caller = "MarshalCall" - + if not self.RQMid then self:T( self.lid .. string.format( "Starting Marhal radio queue." ) ) self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 ) end - + end - + -- Append radio click sound at the end of the transmission. if click then self:RadioTransmission( radio, self[caller].CLICK, false, delay ) end - + else -- SRS transmission @@ -14998,7 +15015,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p local voice = nil local gender = nil local culture = nil - + if radio.alias == "AIRBOSS" then frequency = self.AirbossRadio.frequency modulation = self.AirbossRadio.modulation @@ -15006,13 +15023,13 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p gender = self.AirbossRadio.gender culture = self.AirbossRadio.culture end - + if radio.alias == "MARSHAL" then voice = self.MarshalRadio.voice gender = self.MarshalRadio.gender culture = self.MarshalRadio.culture end - + if radio.alias == "LSO" then frequency = self.LSORadio.frequency modulation = self.LSORadio.modulation @@ -15020,7 +15037,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p gender = self.LSORadio.gender culture = self.LSORadio.culture end - + if pilotcall then voice = self.PilotRadio.voice gender = self.PilotRadio.gender @@ -15034,16 +15051,16 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p modulation = self.AirbossRadio.modulation radio.alias = "AIRBOSS" end - + local volume = nil - + if loud then volume = 1.0 end - + --local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle local text = call.subtitle - self:T(self.lid..text) + self:T(self.lid..text) local srstext = self:_GetNiceSRSText(text) self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, nil, 0.1, nil, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) end @@ -15063,11 +15080,11 @@ function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture ) self.PilotRadio.voice = Voice or MSRS.Voices.Microsoft.David self.PilotRadio.gender = Gender or "male" self.PilotRadio.culture = Culture or "en-US" - + if (not Voice) and self.SRS and self.SRS:GetProvider() == MSRS.Provider.GOOGLE then self.PilotRadio.voice = MSRS.Voices.Google.Standard.en_US_Standard_J end - + return self end @@ -15381,44 +15398,44 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio -- SCHEDULER:New(nil, self.MessageToPlayer, {self, playerData, message, sender, receiver, duration, clear}, delay) self:ScheduleOnce( delay, self.MessageToPlayer, self, playerData, message, sender, receiver, duration, clear ) else - + if not self.SRS then -- Wait until previous sound finished. local wait = 0 - + -- Onboard number to get the attention. if receiver == playerData.onboard then - + -- Which voice over number to use. if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then - + -- User sound of board number. wait = wait + self:_Number2Sound( playerData, sender, receiver ) - + end end - + -- Negative. if string.find( text:lower(), "negative" ) then local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" ) USERSOUND:New( filename ):ToGroup( playerData.group, wait ) wait = wait + self.MarshalCall.NEGATIVE.duration end - + -- Affirm. if string.find( text:lower(), "affirm" ) then local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" ) USERSOUND:New( filename ):ToGroup( playerData.group, wait ) wait = wait + self.MarshalCall.AFFIRMATIVE.duration end - + -- Roger. if string.find( text:lower(), "roger" ) then local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" ) USERSOUND:New( filename ):ToGroup( playerData.group, wait ) wait = wait + self.MarshalCall.ROGER.duration end - + -- Play click sound to end message. if wait > 0 then local filename = self:_RadioFilename( self.MarshalCall.CLICK ) @@ -15431,7 +15448,7 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio local voice = self.MarshalRadio.voice local gender = self.MarshalRadio.gender local culture = self.MarshalRadio.culture - + if not sender then sender = "AIRBOSS" end if string.find(sender,"AIRBOSS" ) then @@ -17049,7 +17066,7 @@ function AIRBOSS:_RemoveSectionMember( playerData, sectionmember ) return false end ---- Set all flights within 100 meters to be part of my section. +--- Set all flights within maxsectiondistance meters to be part of my section (default: 100 meters). -- @param #AIRBOSS self -- @param #string _unitName Name of the player unit. function AIRBOSS:_SetSection( _unitName ) @@ -17067,7 +17084,7 @@ function AIRBOSS:_SetSection( _unitName ) local mycoord = _unit:GetCoordinate() -- Max distance up to which section members are allowed. - local dmax = 100 + local dmax = self.maxsectiondistance -- Check if player is in Marshal or pattern queue already. local text diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 98d42aa37..c6df7238c 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1403,6 +1403,8 @@ function AUFTRAG:NewINTERCEPT(Target) end --- **[AIR]** Create a CAP mission. +-- Assinged groups are tasked to execute a CAP mission. This consists of a DCS orbit task combined with an enroute "search and engage in zone" task. +-- **Note** that currently DCS only supports *circular* zones for the task. -- @param #AUFTRAG self -- @param Core.Zone#ZONE_RADIUS ZoneCAP Circular CAP zone. Detected targets in this zone will be engaged. -- @param #number Altitude Altitude at which to orbit in feet. Default is 10,000 ft. @@ -1717,7 +1719,7 @@ end --- **[AIR]** Create a STRIKE mission. Flight will attack the closest map object to the specified coordinate. -- @param #AUFTRAG self --- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC or TARGET object. +-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC, SET_GROUP, SET_UNIT, SET_STATIC or TARGET object. -- @param #number Altitude Engage altitude in feet. Default 2000 ft. -- @param #number EngageWeaponType Which weapon to use. Defaults to auto, ie ENUMS.WeaponFlag.Auto. See ENUMS.WeaponFlag for options. -- @return #AUFTRAG self @@ -1749,7 +1751,7 @@ end --- **[AIR]** Create a BOMBING mission. Flight will drop bombs a specified coordinate. -- See [DCS task bombing](https://wiki.hoggitworld.com/view/DCS_task_bombing). -- @param #AUFTRAG self --- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC or TARGET object. +-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC, SET_GROUP, SET_UNIT, SET_STATIC or TARGET object. -- @param #number Altitude Engage altitude in feet. Default 25000 ft. -- @param #number EngageWeaponType Which weapon to use. Defaults to auto, ie ENUMS.WeaponFlag.Auto. See ENUMS.WeaponFlag for options. -- @return #AUFTRAG self @@ -6108,10 +6110,13 @@ function AUFTRAG:GetDCSMissionTask() -- BOMBING Mission -- --------------------- - local DCStask=CONTROLLABLE.TaskBombing(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType, Divebomb) + local coords = self.engageTarget:GetCoordinates() + for _, coord in pairs(coords) do + local DCStask = CONTROLLABLE.TaskBombing(nil, coord:GetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) + + table.insert(DCStasks, DCStask) + end - table.insert(DCStasks, DCStask) - elseif self.type==AUFTRAG.Type.STRAFING then ---------------------- @@ -6311,9 +6316,12 @@ function AUFTRAG:GetDCSMissionTask() -- STRIKE Mission -- -------------------- - local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, self:GetTargetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) + local coords = self.engageTarget:GetCoordinates() + for _, coord in pairs(coords) do + local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, coord:GetVec2(), self.engageAsGroup, self.engageWeaponExpend, self.engageQuantity, self.engageDirection, self.engageAltitude, self.engageWeaponType) - table.insert(DCStasks, DCStask) + table.insert(DCStasks, DCStask) + end elseif self.type==AUFTRAG.Type.TANKER or self.type==AUFTRAG.Type.RECOVERYTANKER then diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index e7805f25a..09af31eb5 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -31,7 +31,7 @@ -- @image OPS_CSAR.jpg --- --- Last Update Jan 2025 +-- Last Update May 2025 ------------------------------------------------------------------------- --- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM @@ -263,6 +263,7 @@ CSAR = { rescuedpilots = 0, limitmaxdownedpilots = true, maxdownedpilots = 10, + useFIFOLimitReplacement = false, -- If true, it will remove the oldest downed pilot when a new one is added, if the limit is reached. allheligroupset = nil, topmenuname = "CSAR", ADFRadioPwr = 1000, @@ -313,7 +314,7 @@ CSAR.AircraftType["CH-47Fbl1"] = 31 --- CSAR class version. -- @field #string version -CSAR.version="1.0.30" +CSAR.version="1.0.33" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -468,7 +469,7 @@ function CSAR:New(Coalition, Template, Alias) -- added 1.0.15 self.allowbronco = false -- set to true to use the Bronco mod as a CSAR plane - self.ADFRadioPwr = 1000 + self.ADFRadioPwr = 500 -- added 1.0.16 self.PilotWeight = 80 @@ -1144,19 +1145,8 @@ function CSAR:_EventHandler(EventData) self:T("Double Ejection!") return self end - - -- limit no of pilots in the field. - if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then - self:T("Maxed Downed Pilot!") - return self - end - - - -- TODO: Over water check --- EVENTS.LandingAfterEjection NOT triggered by DCS, so handle csarUsePara = true case - -- might create dual pilots in edge cases - - local wetfeet = false - + + local initdcscoord = nil local initcoord = nil if _event.id == EVENTS.Ejection then @@ -1168,6 +1158,36 @@ function CSAR:_EventHandler(EventData) initcoord = COORDINATE:NewFromVec3(initdcscoord) self:T({initdcscoord}) end + + -- Remove downed pilot if already exists to replace with new one. + if _event.IniPlayerName then + local PilotTable = self.downedPilots --#CSAR.DownedPilot + local _foundPilot = nil + for _,_pilot in pairs(PilotTable) do + if _pilot.player == _event.IniPlayerName and _pilot.alive == true then + _foundPilot = _pilot + break + end + end + if _foundPilot then + self:T("Downed pilot already exists!") + _foundPilot.group:Destroy(false) + self:_RemoveNameFromDownedPilots(_foundPilot.name) + self:_CheckDownedPilotTable() + end + end + + -- limit no of pilots in the field. + if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then + self:T("Maxed Downed Pilot!") + return self + end + + + -- TODO: Over water check --- EVENTS.LandingAfterEjection NOT triggered by DCS, so handle csarUsePara = true case + -- might create dual pilots in edge cases + + local wetfeet = false --local surface = _unit:GetCoordinate():GetSurfaceType() local surface = initcoord:GetSurfaceType() @@ -2116,56 +2136,50 @@ end --- (Internal) Determine distance to closest MASH. -- @param #CSAR self -- @param Wrapper.Unit#UNIT _heli Helicopter #UNIT --- @return #CSAR self +-- @return #number Distance in meters +-- @return #string MASH Name as string function CSAR:_GetClosestMASH(_heli) self:T(self.lid .. " _GetClosestMASH") local _mashset = self.mash -- Core.Set#SET_GROUP - local _mashes = _mashset:GetSetObjects() -- #table + local MashSets = {} + --local _mashes = _mashset.Set-- #table + table.insert(MashSets,_mashset.Set) + table.insert(MashSets,self.zonemashes.Set) + table.insert(MashSets,self.staticmashes.Set) local _shortestDistance = -1 local _distance = 0 local _helicoord = _heli:GetCoordinate() - - local function GetCloseAirbase(coordinate,Coalition,Category) - - local a=coordinate:GetVec3() - local distmin=math.huge - local airbase=nil - for DCSairbaseID, DCSairbase in pairs(world.getAirbases(Coalition)) do - local b=DCSairbase:getPoint() - - local c=UTILS.VecSubstract(a,b) - local dist=UTILS.VecNorm(c) - - if dist 0 then - self:_AddBeaconToGroup(group,frequency,bname) + --self:_AddBeaconToGroup(group,frequency,bname) + else + if frequency > 0 then + trigger.action.stopRadioTransmission(bname) + end end end end @@ -2402,11 +2421,26 @@ function CSAR:_ReachedPilotLimit() local limit = self.maxdownedpilots local islimited = self.limitmaxdownedpilots local count = self:_CountActiveDownedPilots() - if islimited and (count >= limit) then - return true - else - return false - end + if islimited and (count >= limit) then + if self.useFIFOLimitReplacement then + local oldIndex = -1 + local oldDownedPilot = nil + for _index, _downedpilot in pairs(self.downedPilots) do + oldIndex = _index + oldDownedPilot = _downedpilot + break + end + if oldDownedPilot then + oldDownedPilot.group:Destroy(false) + oldDownedPilot.alive = false + self:_CheckDownedPilotTable() + return false + end + end + return true + else + return false + end end --- User - Function to add onw SET_GROUP Set-up for pilot filtering and assignment. @@ -2454,9 +2488,10 @@ function CSAR:onafterStart(From, Event, To) self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() - local staticmashes = SET_STATIC:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterOnce() - local zonemashes = SET_ZONE:New():FilterPrefixes(self.mashprefix):FilterOnce() + self.staticmashes = SET_STATIC:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterOnce() + self.zonemashes = SET_ZONE:New():FilterPrefixes(self.mashprefix):FilterOnce() + --[[ if staticmashes:Count() > 0 then for _,_mash in pairs(staticmashes.Set) do self.mash:AddObject(_mash) @@ -2464,10 +2499,13 @@ function CSAR:onafterStart(From, Event, To) end if zonemashes:Count() > 0 then + self:T("Adding zones to self.mash SET") for _,_mash in pairs(zonemashes.Set) do self.mash:AddObject(_mash) end + self:T("Objects in SET: "..self.mash:Count()) end + --]] if not self.coordinate then local csarhq = self.mash:GetRandom() diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index cd0bc8403..5b2ecc4ef 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -20,11 +20,12 @@ -- -- ### Author: **Applevangelist** (Moose Version), ***Ciribob*** (original), Thanks to: Shadowze, Cammel (testing), bbirchnz (additional code!!) -- ### Repack addition for crates: **Raiden** +-- ### Additional cool features: **Lekaa** -- -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update April 2025 +-- Last Update May 2025 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -865,6 +866,7 @@ do -- my_ctld.TroopUnloadDistGroundHook = 15 -- On the ground, unload troops this far behind the Chinook -- my_ctld.TroopUnloadDistHoverHook = 5 -- When hovering, unload troops this far behind the Chinook -- my_ctld.showstockinmenuitems = false -- When set to true, the menu lines will also show the remaining items in stock (that is, if you set any), downside is that the menu for all will be build every 30 seconds anew. +-- my_ctld.onestepmenu = false -- When set to true, the menu will create Drop and build, Get and load, Pack and remove, Pack and load, Pack. it will be a 1 step solution. -- -- ## 2.1 CH-47 Chinook support -- @@ -1412,7 +1414,7 @@ CTLD.FixedWingTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.2.33" +CTLD.version="1.3.34" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1591,6 +1593,7 @@ function CTLD:New(Coalition, Prefixes, Alias) self.subcats = {} self.subcatsTroop = {} self.showstockinmenuitems = false + self.onestepmenu = false -- disallow building in loadzones self.nobuildinloadzones = true @@ -2913,10 +2916,10 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack) if drop then text = string.format("Crates for %s have been dropped!",cratename) self:__CratesDropped(1, Group, Unit, droppedcargo) + else + self:_SendMessage(text, 10, false, Group) end - self:_SendMessage(text, 10, false, Group) self:_RefreshLoadCratesMenu(Group, Unit) - return self end @@ -3564,7 +3567,7 @@ end function CTLD:IsFixedWing(Unit) local typename = Unit:GetTypeName() or "none" for _,_name in pairs(self.FixedWingTypes or {}) do - if typename == _name or string.find(typename,_name,1,true) then + if _name and (typename==_name or string.find(typename,_name,1,true))then return true end end @@ -3576,11 +3579,14 @@ end -- @param Wrapper.Unit#UNIT Unit -- @return #boolean Outcome function CTLD:IsHook(Unit) - if Unit and string.find(Unit:GetTypeName(),"CH.47") then - return true - else - return false - end + if not Unit then return false end + local typeName = Unit:GetTypeName() + if not typeName then return false end + if string.find(typeName, "CH.47") then + return true + else + return false + end end --- (Internal) Function to set troops positions of a template to a nice circle @@ -3763,89 +3769,103 @@ end -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit function CTLD:_UnloadCrates(Group, Unit) - self:T(self.lid .. " _UnloadCrates") - - if not self.dropcratesanywhere then -- #1570 - -- check if we are in DROP zone - local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) - if not inzone then - self:_SendMessage("You are not close enough to a drop zone!", 10, false, Group) - if not self.debug then - return self - end - end - end - -- Door check - if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then - self:_SendMessage("You need to open the door(s) to drop cargo!", 10, false, Group) - if not self.debug then return self end - end - -- check for hover unload - local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters - local IsHerc = self:IsFixedWing(Unit) - local IsHook = self:IsHook(Unit) - if IsHerc and (not IsHook) then - -- no hover but airdrop here - hoverunload = self:IsCorrectFlightParameters(Unit) - end - -- check if we\'re landed - local grounded = not self:IsUnitInAir(Unit) - -- Get what we have loaded - local unitname = Unit:GetName() - if self.Loaded_Cargo[unitname] and (grounded or hoverunload) then - local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo - -- looking for crate - local cargotable = loadedcargo.Cargo - for _,_cargo in pairs (cargotable) do - local cargo = _cargo -- #CTLD_CARGO - local type = cargo:GetType() -- #CTLD_CARGO.Enum - if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and type ~= CTLD_CARGO.Enum.GCLOADABLE and (not cargo:WasDropped() or self.allowcratepickupagain) then - -- unload crates - self:_GetCrates(Group, Unit, cargo, 1, true) - cargo:SetWasDropped(true) - cargo:SetHasMoved(true) - end - end - -- cleanup load list - local loaded = {} -- #CTLD.LoadedCargo - loaded.Troopsloaded = 0 - loaded.Cratesloaded = 0 - loaded.Cargo = {} + self:T(self.lid .. " _UnloadCrates") - for _,_cargo in pairs (cargotable) do - local cargo = _cargo -- #CTLD_CARGO - local type = cargo:GetType() -- #CTLD_CARGO.Enum - local size = cargo:GetCratesNeeded() - if type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS then - table.insert(loaded.Cargo,_cargo) - loaded.Troopsloaded = loaded.Troopsloaded + size - end - if type == CTLD_CARGO.Enum.GCLOADABLE and not cargo:WasDropped() then - table.insert(loaded.Cargo,_cargo) - loaded.Cratesloaded = loaded.Cratesloaded + size + if not self.dropcratesanywhere then -- #1570 + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) + if not inzone then + self:_SendMessage("You are not close enough to a drop zone!", 10, false, Group) + if not self.debug then + return self + end end end - self.Loaded_Cargo[unitname] = nil - self.Loaded_Cargo[unitname] = loaded - - self:_UpdateUnitCargoMass(Unit) - self:_RefreshDropCratesMenu(Group,Unit) - else - if IsHerc then - self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group) + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to drop cargo!", 10, false, Group) + if not self.debug then return self end + end + local hoverunload = self:IsCorrectHover(Unit) + local IsHerc = self:IsFixedWing(Unit) + local IsHook = self:IsHook(Unit) + if IsHerc and (not IsHook) then + hoverunload = self:IsCorrectFlightParameters(Unit) + end + local grounded = not self:IsUnitInAir(Unit) + local unitname = Unit:GetName() + if self.Loaded_Cargo[unitname] and (grounded or hoverunload) then + local loadedcargo = self.Loaded_Cargo[unitname] or {} + local cargotable = loadedcargo.Cargo + local droppedCount = {} + local neededMap = {} + for _,_cargo in pairs (cargotable) do + local cargo = _cargo + local type = cargo:GetType() + if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and type ~= CTLD_CARGO.Enum.GCLOADABLE and (not cargo:WasDropped() or self.allowcratepickupagain) then + self:_GetCrates(Group, Unit, cargo, 1, true) + cargo:SetWasDropped(true) + cargo:SetHasMoved(true) + local cname = cargo:GetName() or "Unknown" + droppedCount[cname] = (droppedCount[cname] or 0) + 1 + if not neededMap[cname] then + neededMap[cname] = cargo:GetCratesNeeded() or 1 + end + end + end + for cname,count in pairs(droppedCount) do + local needed = neededMap[cname] or 1 + if needed > 1 then + local full = math.floor(count/needed) + local left = count % needed + if full > 0 and left == 0 then + self:_SendMessage(string.format("Dropped %d %s.",full,cname),10,false,Group) + elseif full > 0 and left > 0 then + self:_SendMessage(string.format("Dropped %d %s(s), with %d leftover crate(s).",full,cname,left),10,false,Group) + else + self:_SendMessage(string.format("Dropped %d/%d crate(s) of %s.",count,needed,cname),15,false,Group) + end + else + self:_SendMessage(string.format("Dropped %d %s(s).",count,cname),10,false,Group) + end + end + local loaded = {} + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} + for _,_cargo in pairs (cargotable) do + local cargo = _cargo + local type = cargo:GetType() + local size = cargo:GetCratesNeeded() + if type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS then + table.insert(loaded.Cargo,_cargo) + loaded.Troopsloaded = loaded.Troopsloaded + size + end + if type == CTLD_CARGO.Enum.GCLOADABLE and not cargo:WasDropped() then + table.insert(loaded.Cargo,_cargo) + loaded.Cratesloaded = loaded.Cratesloaded + size + end + end + self.Loaded_Cargo[unitname] = nil + self.Loaded_Cargo[unitname] = loaded + + self:_UpdateUnitCargoMass(Unit) + self:_RefreshDropCratesMenu(Group,Unit) else - self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) - end + if IsHerc then + self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group) + else + self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) + end + end + return self end - return self -end --- (Internal) Function to build nearby crates. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit -- @param #boolean Engineering If true build is by an engineering team. -function CTLD:_BuildCrates(Group, Unit,Engineering) +-- @param #boolean MultiDrop If true and not engineering or FOB, vary position a bit. +function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop) self:T(self.lid .. " _BuildCrates") -- avoid users trying to build from flying Hercs if self:IsFixedWing(Unit) and self.enableFixedWing and not Engineering then @@ -3939,12 +3959,13 @@ function CTLD:_BuildCrates(Group, Unit,Engineering) if build.CanBuild then self:_CleanUpCrates(crates,build,number) if self.buildtime and self.buildtime > 0 then - local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate()) + local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate(),MultiDrop) buildtimer:Start(self.buildtime) self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) self:__CratesBuildStarted(1,Group,Unit) + self:_RefreshDropTroopsMenu(Group,Unit) else - self:_BuildObjectFromCrates(Group,Unit,build) + self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop) end end end @@ -3983,13 +4004,14 @@ function CTLD:_PackCratesNearby(Group, Unit) _Group:Destroy() -- if a match is found destroy the Wrapper.Group#GROUP near the player self:_GetCrates(Group, Unit, _entry, nil, false, true) -- spawn the appropriate crates near the player self:_RefreshLoadCratesMenu(Group,Unit) -- call the refresher to show the crates in the menu - return self + return true end end end end end - return self + self:_SendMessage("Nothing to pack at this distance pilot!",10,false,Group) + return false end --- (Internal) Function to repair nearby vehicles / FOBs @@ -4082,7 +4104,8 @@ end -- @param #CTLD.Buildable Build -- @param #boolean Repair If true this is a repair and not a new build -- @param Core.Point#COORDINATE RepairLocation Location for repair (e.g. where the destroyed unit was) -function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) +-- @param #boolean MultiDrop if true and not a repair, vary location a bit if not a FOB +function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation,MultiDrop) self:T(self.lid .. " _BuildObjectFromCrates") -- Spawn-a-crate-content if Group and Group:IsAlive() or (RepairLocation and not Repair) then @@ -4099,7 +4122,7 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) if type(temptable) == "string" then temptable = {temptable} end - local zone = nil + local zone = nil -- Core.Zone#ZONE_RADIUS if RepairLocation and not Repair then -- timed build zone = ZONE_RADIUS:New(string.format("Build zone-%d",math.random(1,10000)),RepairLocation:GetVec2(),100) @@ -4108,6 +4131,10 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) end --local randomcoord = zone:GetRandomCoordinate(35):GetVec2() local randomcoord = Build.Coord or zone:GetRandomCoordinate(35):GetVec2() + if MultiDrop and (not Repair) and canmove then + -- coordinate may be the same, avoid + local randomcoord = zone:GetRandomCoordinate(35):GetVec2() + end if Repair then randomcoord = RepairLocation:GetVec2() end @@ -4199,310 +4226,459 @@ function CTLD:_CleanUpCrates(Crates,Build,Number) return self end +--- (Internal) Helper - Drop **all** loaded crates nearby and build them. +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +function CTLD:_DropAndBuild(Group,Unit) + if self.nobuildinloadzones then + if self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) then + self:_SendMessage("You cannot build in a loading area, Pilot!",10,false,Group) + return self + end + end + self:_UnloadCrates(Group,Unit) + timer.scheduleFunction(function() self:_BuildCrates(Group,Unit,false,true) end,{},timer.getTime()+1) + end + + --- (Internal) Helper - Drop a **single** crate set and build it. +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +-- @param number setIndex Index of the crate-set to drop + function CTLD:_DropSingleAndBuild(Group,Unit,setIndex) + if self.nobuildinloadzones then + if self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) then + self:_SendMessage("You cannot build in a loading area, Pilot!",10,false,Group) + return self + end + end + self:_UnloadSingleCrateSet(Group,Unit,setIndex) + timer.scheduleFunction(function() self:_BuildCrates(Group,Unit,false) end,{},timer.getTime()+1) + end + +--- (Internal) Helper - Pack crates near the unit and load them. +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +function CTLD:_PackAndLoad(Group,Unit) + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to load cargo!",10,false,Group) + return self + end + if not self:_PackCratesNearby(Group,Unit) then + return self + end + timer.scheduleFunction(function() self:_LoadCratesNearby(Group,Unit) end,{},timer.getTime()+1) + return self + end + +--- (Internal) Helper - Pack crates near the unit and then remove them. +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +function CTLD:_PackAndRemove(Group,Unit) + if not self:_PackCratesNearby(Group,Unit) then + return self + end + timer.scheduleFunction(function() self:_RemoveCratesNearby(Group,Unit) end,{},timer.getTime()+1) + return self +end + +--- (Internal) Helper - get and load in one step +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +function CTLD:_GetAndLoad(Group,Unit,cargoObj) + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to load cargo!",10,false,Group) + return self + end + self:_GetCrates(Group,Unit,cargoObj) + + timer.scheduleFunction(function() self:_LoadSingleCrateSet(Group,Unit,cargoObj.Name) end,{},timer.getTime()+1) +end + +-- @param Wrapper.Group#GROUP Group The player’s group that triggered the action +-- @param Wrapper.Unit#UNIT Unit The unit performing the pack-and-load +function CTLD:_GetAllAndLoad(Group,Unit) + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to load cargo!",10,false,Group) + return self + end + + timer.scheduleFunction(function() self:_LoadCratesNearby(Group,Unit) end,{},timer.getTime()+1) +end + --- (Internal) Housekeeping - Function to refresh F10 menus. -- @param #CTLD self -- @return #CTLD self function CTLD:_RefreshF10Menus() - self:T(self.lid .. " _RefreshF10Menus") - - -- 1) Gather all the pilot groups from our Set - local PlayerSet = self.PilotGroups - local PlayerTable = PlayerSet:GetSetObjects() - - -- 2) Rebuild the self.CtldUnits table - local _UnitList = {} - for _, groupObj in pairs(PlayerTable) do - local firstUnit = groupObj:GetFirstUnitAlive() - if firstUnit then - if firstUnit:IsPlayer() then - if firstUnit:IsHelicopter() or (self.enableFixedWing and self:IsFixedWing(firstUnit)) then - local _unit = firstUnit:GetName() - _UnitList[_unit] = _unit + self:T(self.lid .. " _RefreshF10Menus") + self.onestepmenu = self.onestepmenu or false -- hybrid toggle (default = false) + + -- 1) Gather all the pilot groups from our Set + local PlayerSet = self.PilotGroups + local PlayerTable = PlayerSet:GetSetObjects() + + -- 2) Rebuild the self.CtldUnits table + local _UnitList = {} + for _, groupObj in pairs(PlayerTable) do + local firstUnit = groupObj:GetFirstUnitAlive() + if firstUnit then + if firstUnit:IsPlayer() then + if firstUnit:IsHelicopter() or (self.enableFixedWing and self:IsFixedWing(firstUnit)) then + local _unit = firstUnit:GetName() + _UnitList[_unit] = _unit + end end end end - end - - -- 3) CA Units - if self.allowCATransport and self.CATransportSet then - for _,_clientobj in pairs(self.CATransportSet.Set) do - local client = _clientobj -- Wrapper.Client#CLIENT - if client:IsGround() then - local cname = client:GetName() - self:T(self.lid.."Adding: "..cname) - _UnitList[cname] = cname + + -- 3) CA Units + if self.allowCATransport and self.CATransportSet then + for _,_clientobj in pairs(self.CATransportSet.Set) do + local client = _clientobj -- Wrapper.Client#CLIENT + if client:IsGround() then + local cname = client:GetName() + self:T(self.lid.."Adding: "..cname) + _UnitList[cname] = cname + end end end + + self.CtldUnits = _UnitList + + -- subcats? + if self.usesubcats then + for _id,_cargo in pairs(self.Cargo_Crates) do + local entry = _cargo -- #CTLD_CARGO + if not self.subcats[entry.Subcategory] then + self.subcats[entry.Subcategory] = entry.Subcategory + end + end + for _id,_cargo in pairs(self.Cargo_Statics) do + local entry = _cargo -- #CTLD_CARGO + if not self.subcats[entry.Subcategory] then + self.subcats[entry.Subcategory] = entry.Subcategory + end + end + for _id,_cargo in pairs(self.Cargo_Troops) do + local entry = _cargo -- #CTLD_CARGO + if not self.subcatsTroop[entry.Subcategory] then + self.subcatsTroop[entry.Subcategory] = entry.Subcategory + end + end + end + + local menucount = 0 + local menus = {} + for _, _unitName in pairs(self.CtldUnits) do + if (not self.MenusDone[_unitName]) or (self.showstockinmenuitems == true) then + self:T(self.lid.."Menu not done yet for ".._unitName) + local _unit = UNIT:FindByName(_unitName) + if not _unit and self.allowCATransport then + _unit = CLIENT:FindByName(_unitName) + end + if _unit and _unit:IsAlive() then + local _group = _unit:GetGroup() + if _group then + self:T(self.lid.."Unit and Group exist") + local capabilities = self:_GetUnitCapabilities(_unit) + local cantroops = capabilities.troops + local cancrates = capabilities.crates + local unittype = _unit:GetTypeName() + local isHook = self:IsHook(_unit) + local nohookswitch = true + --local nohookswitch = not (isHook and self.enableChinookGCLoading) + -- Clear old topmenu if it existed + if _group.CTLDTopmenu then + _group.CTLDTopmenu:Remove() + _group.CTLDTopmenu = nil + end + local toptroops = nil + local topcrates = nil + local topmenu = MENU_GROUP:New(_group, "CTLD", nil) + _group.CTLDTopmenu = topmenu + + if cantroops then + local toptroops = MENU_GROUP:New(_group, "Manage Troops", topmenu) + local troopsmenu = MENU_GROUP:New(_group, "Load troops", toptroops) + _group.MyTopTroopsMenu = toptroops + + if self.usesubcats then + local subcatmenus = {} + for catName, _ in pairs(self.subcatsTroop) do + subcatmenus[catName] = MENU_GROUP:New(_group, catName, troopsmenu) + end + for _, cargoObj in pairs(self.Cargo_Troops) do + if not cargoObj.DontShowInMenu then + local stock = cargoObj:GetStock() + local menutext = cargoObj.Name + if (stock >= 0) and (self.showstockinmenuitems == true) then menutext = menutext.." ["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, menutext, subcatmenus[cargoObj.Subcategory], self._LoadTroops, self, _group, _unit, cargoObj) + end + end + else + for _, cargoObj in pairs(self.Cargo_Troops) do + if not cargoObj.DontShowInMenu then + local stock = cargoObj:GetStock() + local menutext = cargoObj.Name + if (stock >= 0) and (self.showstockinmenuitems == true) then menutext = menutext.." ["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, menutext, troopsmenu, self._LoadTroops, self, _group, _unit, cargoObj) + end + end + end + local dropTroopsMenu=MENU_GROUP:New(_group,"Drop Troops",toptroops):Refresh() + MENU_GROUP_COMMAND:New(_group,"Drop ALL troops",dropTroopsMenu,self._UnloadTroops,self,_group,_unit):Refresh() + MENU_GROUP_COMMAND:New(_group,"Extract troops",toptroops,self._ExtractTroops,self,_group,_unit):Refresh() + local uName=_unit:GetName() + local loadedData=self.Loaded_Cargo[uName] + if loadedData and loadedData.Cargo then + for i,cargoObj in ipairs(loadedData.Cargo) do + if cargoObj and (cargoObj:GetType()==CTLD_CARGO.Enum.TROOPS or cargoObj:GetType()==CTLD_CARGO.Enum.ENGINEERS) and not cargoObj:WasDropped() then + local name=cargoObj:GetName() or "Unknown" + local needed=cargoObj:GetCratesNeeded() or 1 + local cID=cargoObj:GetID() + local line=string.format("Drop: %s",name,needed,cID) + MENU_GROUP_COMMAND:New(_group,line,dropTroopsMenu,self._UnloadSingleTroopByID,self,_group,_unit,cID):Refresh() + end + end + end + end + if cancrates then + local topcrates = MENU_GROUP:New(_group, "Manage Crates", topmenu) + _group.MyTopCratesMenu = topcrates + + -- Build the “Get Crates” sub-menu items + local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates) + + if self.onestepmenu then + if self.usesubcats then + local subcatmenus = {} + for catName,_ in pairs(self.subcats) do + subcatmenus[catName] = MENU_GROUP:New(_group,catName,cratesmenu) + end + for _,cargoObj in pairs(self.Cargo_Crates) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + local mSet = MENU_GROUP:New(_group,txt,subcatmenus[cargoObj.Subcategory]) + MENU_GROUP_COMMAND:New(_group,"Get",mSet,self._GetCrates,self,_group,_unit,cargoObj) + MENU_GROUP_COMMAND:New(_group,"Get and Load",mSet,self._GetAndLoad,self,_group,_unit,cargoObj) + end + end + for _,cargoObj in pairs(self.Cargo_Statics) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + local mSet = MENU_GROUP:New(_group,txt,subcatmenus[cargoObj.Subcategory]) + MENU_GROUP_COMMAND:New(_group,"Get",mSet,self._GetCrates,self,_group,_unit,cargoObj) + MENU_GROUP_COMMAND:New(_group,"Get and Load",mSet,self._GetAndLoad,self,_group,_unit,cargoObj) + end + end + else + for _,cargoObj in pairs(self.Cargo_Crates) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + local mSet = MENU_GROUP:New(_group,txt,cratesmenu) + MENU_GROUP_COMMAND:New(_group,"Get",mSet,self._GetCrates,self,_group,_unit,cargoObj) + MENU_GROUP_COMMAND:New(_group,"Get and Load",mSet,self._GetAndLoad,self,_group,_unit,cargoObj) + end + end + for _,cargoObj in pairs(self.Cargo_Statics) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + local mSet = MENU_GROUP:New(_group,txt,cratesmenu) + MENU_GROUP_COMMAND:New(_group,"Get",mSet,self._GetCrates,self,_group,_unit,cargoObj) + MENU_GROUP_COMMAND:New(_group,"Get and Load",mSet,self._GetAndLoad,self,_group,_unit,cargoObj) + + end + end + end + else + if self.usesubcats then + local subcatmenus = {} + for catName, _ in pairs(self.subcats) do + subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) -- fixed variable case + end + for _, cargoObj in pairs(self.Cargo_Crates) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, txt, subcatmenus[cargoObj.Subcategory], self._GetCrates, self, _group, _unit, cargoObj) + end + end + for _, cargoObj in pairs(self.Cargo_Statics) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, txt, subcatmenus[cargoObj.Subcategory], self._GetCrates, self, _group, _unit, cargoObj) + end + end + else + for _, cargoObj in pairs(self.Cargo_Crates) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, txt, cratesmenu, self._GetCrates, self, _group, _unit, cargoObj) + end + end + for _, cargoObj in pairs(self.Cargo_Statics) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, txt, cratesmenu, self._GetCrates, self, _group, _unit, cargoObj) + end + end + end + end + + local loadCratesMenu=MENU_GROUP:New(_group,"Load Crates",topcrates) + _group.MyLoadCratesMenu=loadCratesMenu + MENU_GROUP_COMMAND:New(_group,"Load ALL",loadCratesMenu,self._LoadCratesNearby,self,_group,_unit) + MENU_GROUP_COMMAND:New(_group,"Show loadable crates",loadCratesMenu,self._RefreshLoadCratesMenu,self,_group,_unit) + + local dropCratesMenu = MENU_GROUP:New(_group,"Drop Crates",topcrates) + topcrates.DropCratesMenu = dropCratesMenu + + if not self.nobuildmenu then + MENU_GROUP_COMMAND:New(_group, "Build crates", topcrates, self._BuildCrates, self, _group, _unit) + MENU_GROUP_COMMAND:New(_group, "Repair", topcrates, self._RepairCrates, self, _group, _unit):Refresh() + end + + local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates) + MENU_GROUP_COMMAND:New(_group, "Remove crates nearby", removecratesmenu, self._RemoveCratesNearby, self, _group, _unit) + + if self.onestepmenu then + local mPack=MENU_GROUP:New(_group,"Pack crates",topcrates) + MENU_GROUP_COMMAND:New(_group,"Pack",mPack,self._PackCratesNearby,self,_group,_unit) + MENU_GROUP_COMMAND:New(_group,"Pack and Load",mPack,self._PackAndLoad,self,_group,_unit) + MENU_GROUP_COMMAND:New(_group,"Pack and Remove",mPack,self._PackAndRemove,self,_group,_unit) + MENU_GROUP_COMMAND:New(_group, "List crates nearby", topcrates, self._ListCratesNearby, self, _group, _unit) + else + MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit) + MENU_GROUP_COMMAND:New(_group, "List crates nearby", topcrates, self._ListCratesNearby, self, _group, _unit) + end + + local uName = _unit:GetName() + local loadedData = self.Loaded_Cargo[uName] + if loadedData and loadedData.Cargo then + local cargoByName = {} + for _, cgo in pairs(loadedData.Cargo) do + if cgo and (not cgo:WasDropped()) then + local cname = cgo:GetName() + local cneeded = cgo:GetCratesNeeded() + cargoByName[cname] = cargoByName[cname] or { count=0, needed=cneeded } + cargoByName[cname].count = cargoByName[cname].count + 1 + end + end + for name, info in pairs(cargoByName) do + local line = string.format("Drop %s (%d/%d)", name, info.count, info.needed) + MENU_GROUP_COMMAND:New(_group, line, dropCratesMenu, self._UnloadSingleCrateSet, self, _group, _unit, name) + end + end + end + + ----------------------------------------------------- + -- Misc sub‐menus + ----------------------------------------------------- + MENU_GROUP_COMMAND:New(_group, "List boarded cargo", topmenu, self._ListCargo, self, _group, _unit) + MENU_GROUP_COMMAND:New(_group, "Inventory", topmenu, self._ListInventory, self, _group, _unit) + MENU_GROUP_COMMAND:New(_group, "List active zone beacons", topmenu, self._ListRadioBeacons, self, _group, _unit) + + local smoketopmenu = MENU_GROUP:New(_group, "Smokes, Flares, Beacons", topmenu) + MENU_GROUP_COMMAND:New(_group, "Smoke zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, false) + local smokeself = MENU_GROUP:New(_group, "Drop smoke now", smoketopmenu) + MENU_GROUP_COMMAND:New(_group, "Red smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Red) + MENU_GROUP_COMMAND:New(_group, "Blue smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Blue) + MENU_GROUP_COMMAND:New(_group, "Green smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Green) + MENU_GROUP_COMMAND:New(_group, "Orange smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Orange) + MENU_GROUP_COMMAND:New(_group, "White smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.White) + + MENU_GROUP_COMMAND:New(_group, "Flare zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, true) + MENU_GROUP_COMMAND:New(_group, "Fire flare now", smoketopmenu, self.SmokePositionNow, self, _unit, true) + MENU_GROUP_COMMAND:New(_group, "Drop beacon now", smoketopmenu, self.DropBeaconNow, self, _unit):Refresh() + + if self:IsFixedWing(_unit) then + MENU_GROUP_COMMAND:New(_group, "Show flight parameters", topmenu, self._ShowFlightParams, self, _group, _unit):Refresh() + else + MENU_GROUP_COMMAND:New(_group, "Show hover parameters", topmenu, self._ShowHoverParams, self, _group, _unit):Refresh() + end + + -- Mark we built the menu + self.MenusDone[_unitName] = true + self:_RefreshLoadCratesMenu(_group,_unit) + self:_RefreshDropCratesMenu(_group,_unit) + + end -- if _group + end -- if _unit + else + self:T(self.lid .. " Menus already done for this group!") + end + end -- for all pilot units + + return self end - self.CtldUnits = _UnitList - - -- subcats? - if self.usesubcats then - for _id,_cargo in pairs(self.Cargo_Crates) do - local entry = _cargo -- #CTLD_CARGO - if not self.subcats[entry.Subcategory] then - self.subcats[entry.Subcategory] = entry.Subcategory - end - end - for _id,_cargo in pairs(self.Cargo_Statics) do - local entry = _cargo -- #CTLD_CARGO - if not self.subcats[entry.Subcategory] then - self.subcats[entry.Subcategory] = entry.Subcategory - end - end - for _id,_cargo in pairs(self.Cargo_Troops) do - local entry = _cargo -- #CTLD_CARGO - if not self.subcatsTroop[entry.Subcategory] then - self.subcatsTroop[entry.Subcategory] = entry.Subcategory - end - end - end - - local menucount = 0 - local menus = {} - for _, _unitName in pairs(self.CtldUnits) do - if (not self.MenusDone[_unitName]) or (self.showstockinmenuitems == true) then - self:T(self.lid.."Menu not done yet for ".._unitName) - local _unit = UNIT:FindByName(_unitName) - if not _unit and self.allowCATransport then - _unit = CLIENT:FindByName(_unitName) - end - if _unit and _unit:IsAlive() then - local _group = _unit:GetGroup() - if _group then - self:T(self.lid.."Unit and Group exist") - local capabilities = self:_GetUnitCapabilities(_unit) - local cantroops = capabilities.troops - local cancrates = capabilities.crates - local unittype = _unit:GetTypeName() - local isHook = self:IsHook(_unit) - local nohookswitch = true - --local nohookswitch = not (isHook and self.enableChinookGCLoading) - -- Clear old topmenu if it existed - if _group.CTLDTopmenu then - _group.CTLDTopmenu:Remove() - _group.CTLDTopmenu = nil - end - local toptroops = nil - local topcrates = nil - local topmenu = MENU_GROUP:New(_group, "CTLD", nil) - _group.CTLDTopmenu = topmenu - - if cantroops then - local toptroops = MENU_GROUP:New(_group, "Manage Troops", topmenu) - local troopsmenu = MENU_GROUP:New(_group, "Load troops", toptroops) - _group.MyTopTroopsMenu = toptroops - - if self.usesubcats then - local subcatmenus = {} - for catName, _ in pairs(self.subcatsTroop) do - subcatmenus[catName] = MENU_GROUP:New(_group, catName, troopsmenu) - end - for _, cargoObj in pairs(self.Cargo_Troops) do - if not cargoObj.DontShowInMenu then - local stock = cargoObj:GetStock() - local menutext = cargoObj.Name - if (stock >= 0) and (self.showstockinmenuitems == true) then menutext = menutext.." ["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, menutext, subcatmenus[cargoObj.Subcategory], self._LoadTroops, self, _group, _unit, cargoObj) - end - end - else - for _, cargoObj in pairs(self.Cargo_Troops) do - if not cargoObj.DontShowInMenu then - local stock = cargoObj:GetStock() - local menutext = cargoObj.Name - if (stock >= 0) and (self.showstockinmenuitems == true) then menutext = menutext.." ["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, menutext, troopsmenu, self._LoadTroops, self, _group, _unit, cargoObj) - - end - end - end - local dropTroopsMenu=MENU_GROUP:New(_group,"Drop Troops",toptroops):Refresh() - MENU_GROUP_COMMAND:New(_group,"Drop ALL troops",dropTroopsMenu,self._UnloadTroops,self,_group,_unit):Refresh() - MENU_GROUP_COMMAND:New(_group,"Extract troops",toptroops,self._ExtractTroops,self,_group,_unit):Refresh() - local uName=_unit:GetName() - local loadedData=self.Loaded_Cargo[uName] - if loadedData and loadedData.Cargo then - for i,cargoObj in ipairs(loadedData.Cargo) do - if cargoObj and (cargoObj:GetType()==CTLD_CARGO.Enum.TROOPS or cargoObj:GetType()==CTLD_CARGO.Enum.ENGINEERS) and not cargoObj:WasDropped() then - local name=cargoObj:GetName() or "Unknown" - local needed=cargoObj:GetCratesNeeded() or 1 - local cID=cargoObj:GetID() - local line=string.format("Drop: %s",name,needed,cID) - MENU_GROUP_COMMAND:New(_group,line,dropTroopsMenu,self._UnloadSingleTroopByID,self,_group,_unit,cID):Refresh() - end - end - end - end - if cancrates then - local topcrates = MENU_GROUP:New(_group, "Manage Crates", topmenu) - _group.MyTopCratesMenu = topcrates - - -- Build the “Get Crates” sub-menu items - local cratesmenu = MENU_GROUP:New(_group, "Get Crates", topcrates) - if self.usesubcats then - local subcatmenus = {} - for catName, _ in pairs(self.subcats) do - subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) - end - for _, cargoObj in pairs(self.Cargo_Crates) do - if not cargoObj.DontShowInMenu then - local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) - if cargoObj.Location then txt = txt.."[R]" end - local stock = cargoObj:GetStock() - if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, txt, subcatmenus[cargoObj.Subcategory], self._GetCrates, self, _group, _unit, cargoObj) - end - end - for _, cargoObj in pairs(self.Cargo_Statics) do - if not cargoObj.DontShowInMenu then - local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) - if cargoObj.Location then txt = txt.."[R]" end - local stock = cargoObj:GetStock() - if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, txt, subcatmenus[cargoObj.Subcategory], self._GetCrates, self, _group, _unit, cargoObj) - end - end - else - for _, cargoObj in pairs(self.Cargo_Crates) do - if not cargoObj.DontShowInMenu then - local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) - if cargoObj.Location then txt = txt.."[R]" end - local stock = cargoObj:GetStock() - if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, txt, cratesmenu, self._GetCrates, self, _group, _unit, cargoObj) - end - end - for _, cargoObj in pairs(self.Cargo_Statics) do - if not cargoObj.DontShowInMenu then - local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) - if cargoObj.Location then txt = txt.."[R]" end - local stock = cargoObj:GetStock() - if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, txt, cratesmenu, self._GetCrates, self, _group, _unit, cargoObj) - end - end - end - - local loadCratesMenu=MENU_GROUP:New(_group,"Load Crates",topcrates) - _group.MyLoadCratesMenu=loadCratesMenu - MENU_GROUP_COMMAND:New(_group,"Load ALL",loadCratesMenu,self._LoadCratesNearby,self,_group,_unit) - MENU_GROUP_COMMAND:New(_group,"Show loadable crates",loadCratesMenu,self._RefreshLoadCratesMenu,self,_group,_unit) - - local dropCratesMenu=MENU_GROUP:New(_group,"Drop Crates",topcrates) - topcrates.DropCratesMenu=dropCratesMenu - - if not self.nobuildmenu then - MENU_GROUP_COMMAND:New(_group, "Build crates", topcrates, self._BuildCrates, self, _group, _unit) - MENU_GROUP_COMMAND:New(_group, "Repair", topcrates, self._RepairCrates, self, _group, _unit):Refresh() - end - - local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates) - MENU_GROUP_COMMAND:New(_group, "Remove crates nearby", removecratesmenu, self._RemoveCratesNearby, self, _group, _unit) - - MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit) - MENU_GROUP_COMMAND:New(_group, "List crates nearby", topcrates, self._ListCratesNearby, self, _group, _unit) - - local uName = _unit:GetName() - local loadedData = self.Loaded_Cargo[uName] - if loadedData and loadedData.Cargo then - local cargoByName = {} - for _, cgo in pairs(loadedData.Cargo) do - if cgo and (not cgo:WasDropped()) then - local cname = cgo:GetName() - local cneeded = cgo:GetCratesNeeded() - cargoByName[cname] = cargoByName[cname] or { count=0, needed=cneeded } - cargoByName[cname].count = cargoByName[cname].count + 1 - end - end - for name, info in pairs(cargoByName) do - local line = string.format("Drop %s (%d/%d)", name, info.count, info.needed) - MENU_GROUP_COMMAND:New(_group, line, dropCratesMenu, self._UnloadSingleCrateSet, self, _group, _unit, name) - end - end - end - - - - ----------------------------------------------------- - -- Misc sub‐menus - ----------------------------------------------------- - MENU_GROUP_COMMAND:New(_group, "List boarded cargo", topmenu, self._ListCargo, self, _group, _unit) - MENU_GROUP_COMMAND:New(_group, "Inventory", topmenu, self._ListInventory, self, _group, _unit) - MENU_GROUP_COMMAND:New(_group, "List active zone beacons", topmenu, self._ListRadioBeacons, self, _group, _unit) - - local smoketopmenu = MENU_GROUP:New(_group, "Smokes, Flares, Beacons", topmenu) - MENU_GROUP_COMMAND:New(_group, "Smoke zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, false) - local smokeself = MENU_GROUP:New(_group, "Drop smoke now", smoketopmenu) - MENU_GROUP_COMMAND:New(_group, "Red smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Red) - MENU_GROUP_COMMAND:New(_group, "Blue smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Blue) - MENU_GROUP_COMMAND:New(_group, "Green smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Green) - MENU_GROUP_COMMAND:New(_group, "Orange smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Orange) - MENU_GROUP_COMMAND:New(_group, "White smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.White) - - MENU_GROUP_COMMAND:New(_group, "Flare zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, true) - MENU_GROUP_COMMAND:New(_group, "Fire flare now", smoketopmenu, self.SmokePositionNow, self, _unit, true) - MENU_GROUP_COMMAND:New(_group, "Drop beacon now", smoketopmenu, self.DropBeaconNow, self, _unit):Refresh() - - if self:IsFixedWing(_unit) then - MENU_GROUP_COMMAND:New(_group, "Show flight parameters", topmenu, self._ShowFlightParams, self, _group, _unit):Refresh() - else - MENU_GROUP_COMMAND:New(_group, "Show hover parameters", topmenu, self._ShowHoverParams, self, _group, _unit):Refresh() - end - - -- Mark we built the menu - self.MenusDone[_unitName] = true - self:_RefreshLoadCratesMenu(_group, _unit) - self:_RefreshDropCratesMenu(_group,_unit) - - end -- if _group - end -- if _unit - else - self:T(self.lid .. " Menus already done for this group!") - end - end -- for all pilot units - - return self -end - --- (Internal) Function to refresh the menu for load crates. Triggered from land/getcrate/pack and more -- @param #CTLD self -- @param Wrapper.Group#GROUP Group The calling group. -- @param Wrapper.Unit#UNIT Unit The calling unit. -- @return #CTLD self -function CTLD:_RefreshLoadCratesMenu(Group, Unit) - if not Group.MyLoadCratesMenu then return end - Group.MyLoadCratesMenu:RemoveSubMenus() - - local d = self.CrateDistance or 35 - local nearby, n = self:_FindCratesNearby(Group, Unit, d, true, true) - if n == 0 then - MENU_GROUP_COMMAND:New(Group, "No crates found! Rescan?", Group.MyLoadCratesMenu, function() self:_RefreshLoadCratesMenu(Group, Unit) end) - return - end - MENU_GROUP_COMMAND:New(Group, "Load ALL", Group.MyLoadCratesMenu, self._LoadCratesNearby, self, Group, Unit) - local cargoByName = {} - for _, crate in pairs(nearby) do - local cName = crate:GetName() - cargoByName[cName] = cargoByName[cName] or {} - table.insert(cargoByName[cName], crate) - end - - for cName, cList in pairs(cargoByName) do - local needed = cList[1]:GetCratesNeeded() or 1 - local found = #cList - - local line - if found >= needed then - line = string.format("Load %s", cName) - else - MENU_GROUP_COMMAND:New(Group, "Rescan?", Group.MyLoadCratesMenu, function() self:_RefreshLoadCratesMenu(Group, Unit) end) - line = string.format("Load %s (%d/%d)", cName, found, needed) +function CTLD:_RefreshLoadCratesMenu(Group,Unit) + if not Group.MyLoadCratesMenu then return end + Group.MyLoadCratesMenu:RemoveSubMenus() + + local d=self.CrateDistance or 35 + local nearby,n=self:_FindCratesNearby(Group,Unit,d,true,true) + if n==0 then + MENU_GROUP_COMMAND:New(Group,"No crates found! Rescan?",Group.MyLoadCratesMenu,function() self:_RefreshLoadCratesMenu(Group,Unit) end) + return + end + MENU_GROUP_COMMAND:New(Group,"Load ALL",Group.MyLoadCratesMenu,self._LoadCratesNearby,self,Group,Unit) + + local cargoByName={} + for _,crate in pairs(nearby) do + local name=crate:GetName() + cargoByName[name]=cargoByName[name] or{} + table.insert(cargoByName[name],crate) + end + + local lineIndex=1 + for cName,list in pairs(cargoByName) do + local needed=list[1]:GetCratesNeeded() or 1 + table.sort(list,function(a,b)return a:GetID()=needed then + label=string.format("%d. Load %s",lineIndex,cName) + i=i+needed + else + label=string.format("%d. Load %s (%d/%d)",lineIndex,cName,left,needed) + i=#list+1 + end + MENU_GROUP_COMMAND:New(Group,label,Group.MyLoadCratesMenu,self._LoadSingleCrateSet,self,Group,Unit,cName) + lineIndex=lineIndex+1 + end end - MENU_GROUP_COMMAND:New(Group, line, Group.MyLoadCratesMenu, self._LoadSingleCrateSet, self, Group, Unit, cName) end -end + --- -- Loads exactly `CratesNeeded` crates for one cargoName in range. @@ -4745,78 +4921,133 @@ end -- @param Wrapper.Unit#UNIT Unit The calling unit. -- @return #CTLD self function CTLD:_RefreshDropCratesMenu(Group, Unit) - if not Group.CTLDTopmenu then return end - local topCrates = Group.MyTopCratesMenu - if not topCrates then return end - if topCrates.DropCratesMenu then - topCrates.DropCratesMenu:RemoveSubMenus() - else - topCrates.DropCratesMenu = MENU_GROUP:New(Group, "Drop Crates", topCrates) - end - local dropCratesMenu = topCrates.DropCratesMenu - local loadedData = self.Loaded_Cargo[Unit:GetName()] - if not loadedData or not loadedData.Cargo then - MENU_GROUP_COMMAND:New(Group,"No crates to drop!",dropCratesMenu,function() end) - return - end - - local cargoByName={} - local dropableCrates=0 - for _,cObj in ipairs(loadedData.Cargo) do - if cObj and not cObj:WasDropped() then - local cType=cObj:GetType() - if cType~=CTLD_CARGO.Enum.TROOPS and cType~=CTLD_CARGO.Enum.ENGINEERS and cType~=CTLD_CARGO.Enum.GCLOADABLE then - local name=cObj:GetName()or"Unknown" - cargoByName[name]=cargoByName[name]or{} - table.insert(cargoByName[name],cObj) - dropableCrates=dropableCrates+1 + if not Group.CTLDTopmenu then return end + local topCrates = Group.MyTopCratesMenu + if not topCrates then return end + if topCrates.DropCratesMenu then + topCrates.DropCratesMenu:RemoveSubMenus() + else + topCrates.DropCratesMenu = MENU_GROUP:New(Group, "Drop Crates", topCrates) + end + + local dropCratesMenu = topCrates.DropCratesMenu + local loadedData = self.Loaded_Cargo[Unit:GetName()] + if not loadedData or not loadedData.Cargo then + MENU_GROUP_COMMAND:New(Group,"No crates to drop!",dropCratesMenu,function() end) + return + end + + local cargoByName={} + local dropableCrates=0 + for _,cObj in ipairs(loadedData.Cargo) do + if cObj and not cObj:WasDropped() then + local cType=cObj:GetType() + if cType~=CTLD_CARGO.Enum.TROOPS and cType~=CTLD_CARGO.Enum.ENGINEERS and cType~=CTLD_CARGO.Enum.GCLOADABLE then + local name=cObj:GetName()or"Unknown" + cargoByName[name]=cargoByName[name]or{} + table.insert(cargoByName[name],cObj) + dropableCrates=dropableCrates+1 + end + end + end + + if dropableCrates==0 then + MENU_GROUP_COMMAND:New(Group,"No crates to drop!",dropCratesMenu,function() end) + return + end + + ---------------------------------------------------------------------- + -- DEFAULT (“classic”) versus ONE-STEP behaviour + ---------------------------------------------------------------------- + if not self.onestepmenu then + -------------------------------------------------------------------- + -- classic menu + -------------------------------------------------------------------- + MENU_GROUP_COMMAND:New(Group,"Drop ALL crates",dropCratesMenu,self._UnloadCrates,self,Group,Unit) + + self.CrateGroupList=self.CrateGroupList or{} + self.CrateGroupList[Unit:GetName()]={} + + local lineIndex=1 + for cName,list in pairs(cargoByName) do + local needed=list[1]:GetCratesNeeded() or 1 + table.sort(list,function(a,b)return a:GetID()=needed then + local chunk={} + for n=i,i+needed-1 do + table.insert(chunk,list[n]) + end + local label=string.format("%d. %s",lineIndex,cName) + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) + i=i+needed + else + local chunk={} + for n=i,#list do + table.insert(chunk,list[n]) + end + local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) + i=#list+1 + end + lineIndex=lineIndex+1 + end + end + + else + -------------------------------------------------------------------- + -- one-step (enhanced) menu + -------------------------------------------------------------------- + local mAll=MENU_GROUP:New(Group,"Drop ALL crates",dropCratesMenu) + MENU_GROUP_COMMAND:New(Group,"Drop",mAll,self._UnloadCrates,self,Group,Unit) + MENU_GROUP_COMMAND:New(Group,"Drop and build",mAll,self._DropAndBuild,self,Group,Unit) + + self.CrateGroupList=self.CrateGroupList or{} + self.CrateGroupList[Unit:GetName()]={} + + local lineIndex=1 + for cName,list in pairs(cargoByName) do + local needed=list[1]:GetCratesNeeded() or 1 + table.sort(list,function(a,b)return a:GetID()=needed then + local chunk={} + for n=i,i+needed-1 do + table.insert(chunk,list[n]) + end + local label=string.format("%d. %s",lineIndex,cName) + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + local mSet=MENU_GROUP:New(Group,label,dropCratesMenu) + MENU_GROUP_COMMAND:New(Group,"Drop",mSet,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) + MENU_GROUP_COMMAND:New(Group,"Drop and build",mSet,self._DropSingleAndBuild,self,Group,Unit,setIndex) + i=i+needed + else + local chunk={} + for n=i,#list do + table.insert(chunk,list[n]) + end + local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) + i=#list+1 + end + lineIndex=lineIndex+1 + end end end end - if dropableCrates==0 then - MENU_GROUP_COMMAND:New(Group,"No crates to drop!",dropCratesMenu,function() end) - return - end - - MENU_GROUP_COMMAND:New(Group,"Drop ALL crates",dropCratesMenu,self._UnloadCrates,self,Group,Unit) - self.CrateGroupList=self.CrateGroupList or{} - self.CrateGroupList[Unit:GetName()]={} - - local lineIndex=1 - for cName,list in pairs(cargoByName) do - local needed=list[1]:GetCratesNeeded() or 1 - table.sort(list,function(a,b)return a:GetID()=needed then - local chunk={} - for n=i,i+needed-1 do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s",lineIndex,cName) - table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - i=i+needed - else - local chunk={} - for n=i,#list do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) - table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - i=#list+1 - end - lineIndex=lineIndex+1 - end - end -end - --- (Internal) Function to unload a single Troop group by ID. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group The calling group. @@ -4864,7 +5095,7 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID) return self end - -- Drop ONLY the FIRST cargo in that chunk + -- Drop the FIRST cargo in that chunk local foundCargo = chunk[1] if not foundCargo then self:_SendMessage(string.format("No troop cargo at chunk %d!", chunkID), 10, false, Group) @@ -5662,6 +5893,7 @@ function CTLD:IsUnitInZone(Unit,Zonetype) if Zonetype == CTLD.CargoZoneType.SHIP then self:T("Checking Type Ship: "..zonename) local ZoneUNIT = UNIT:FindByName(zonename) + if not ZoneUNIT then return false end zonecoord = ZoneUNIT:GetCoordinate() zoneradius = czone.shiplength zonewidth = czone.shipwidth @@ -5749,6 +5981,7 @@ function CTLD:SmokeZoneNearBy(Unit, Flare) end end local zonecoord = zone:GetCoordinate() + if zonecoord then local active = CZone.active local color = CZone.color local distance = self:_GetDistance(zonecoord,unitcoord) @@ -5765,6 +5998,7 @@ function CTLD:SmokeZoneNearBy(Unit, Flare) self:_SendMessage(string.format("Roger, %s zone %s!",txt, zonename), 10, false, Group) smoked = true end + end end end if not smoked then @@ -6983,13 +7217,17 @@ end -- right subtype? if Event == subtype and not task:IsDone() then local targetzone = task.Target:GetObject() -- Core.Zone#ZONE should be a zone in this case .... - --self:T2({Name=Groupname,Property=task:GetProperty("ExtractName")}) - local okaygroup = string.find(Groupname,task:GetProperty("ExtractName"),1,true) - if targetzone and targetzone.ClassName and string.match(targetzone.ClassName,"ZONE") and okaygroup then - if task.Clients:HasUniqueID(playername) then - -- success - task:__Success(-1) + self:T2({Name=Groupname,Property=task:GetProperty("ExtractName")}) + if task:GetProperty("ExtractName") then + local okaygroup = string.find(Groupname,task:GetProperty("ExtractName"),1,true) + if targetzone and targetzone.ClassName and string.match(targetzone.ClassName,"ZONE") and okaygroup then + if task.Clients:HasUniqueID(playername) then + -- success + task:__Success(-1) + end end + else + self:T({Text="'ExtractName' Property not set",Name=Groupname,Property=task.Type}) end end end diff --git a/Moose Development/Moose/Ops/EasyGCICAP.lua b/Moose Development/Moose/Ops/EasyGCICAP.lua index 4fcd7c2cb..89a3198d2 100644 --- a/Moose Development/Moose/Ops/EasyGCICAP.lua +++ b/Moose Development/Moose/Ops/EasyGCICAP.lua @@ -70,6 +70,7 @@ -- @field #boolean DespawnAfterLanding -- @field #boolean DespawnAfterHolding -- @field #list ListOfAuftrag +-- @field #string defaulttakeofftype Take off type -- @extends Core.Fsm#FSM --- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown. @@ -223,7 +224,8 @@ EASYGCICAP = { ReadyFlightGroups = {}, DespawnAfterLanding = false, DespawnAfterHolding = true, - ListOfAuftrag = {} + ListOfAuftrag = {}, + defaulttakeofftype = "hot", } --- Internal Squadron data type @@ -259,7 +261,7 @@ EASYGCICAP = { --- EASYGCICAP class version. -- @field #string version -EASYGCICAP.version="0.1.18" +EASYGCICAP.version="0.1.22" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -312,6 +314,7 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName) self.DespawnAfterLanding = false self.DespawnAfterHolding = true self.ListOfAuftrag = {} + self.defaulttakeofftype = "hot" -- Set some string id for output to DCS.log file. self.lid=string.format("EASYGCICAP %s | ", self.alias) @@ -400,6 +403,16 @@ function EASYGCICAP:SetDefaultRepeatOnFailure(Retries) return self end +--- Add default take off type for the airwings. +-- @param #EASYGCICAP self +-- @param #string Takeoff Can be "hot", "cold", or "air" - default is "hot". +-- @return #EASYGCICAP self +function EASYGCICAP:SetDefaultTakeOffType(Takeoff) + self:T(self.lid.."SetDefaultTakeOffType") + self.defaulttakeofftype = Takeoff or "hot" + return self +end + --- Set default CAP Speed in knots -- @param #EASYGCICAP self -- @param #number Speed Speed defaults to 300 @@ -569,6 +582,13 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) local DespawnAfterLanding = self.DespawnAfterLanding local DespawnAfterHolding = self.DespawnAfterHolding + -- Check STATIC name + local check = STATIC:FindByName(Airbasename,false) + if check == nil then + MESSAGE:New(self.lid.."There's no warehouse static on the map (wrong naming?) for airbase "..tostring(Airbasename).."!",30,"CHECK"):ToAllIf(self.debug):ToLog() + return + end + -- Create Airwing local CAP_Wing = AIRWING:New(Airbasename,Alias) CAP_Wing:SetVerbosityLevel(0) @@ -596,9 +616,8 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) if #self.ManagedREC > 0 then CAP_Wing:SetNumberRecon(1) end - --local PatrolCoordinateKutaisi = ZONE:New(CapZoneName):GetCoordinate() - --CAP_Wing:AddPatrolPointCAP(PatrolCoordinateKutaisi,self.capalt,UTILS.KnotsToAltKIAS(self.capspeed,self.capalt),self.capdir,self.capleg) - CAP_Wing:SetTakeoffHot() + + CAP_Wing:SetTakeoffType(self.defaulttakeofftype) CAP_Wing:SetLowFuelThreshold(0.3) CAP_Wing.RandomAssetScore = math.random(50,100) CAP_Wing:Start() @@ -606,6 +625,9 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) local Intel = self.Intel local TankerInvisible = self.TankerInvisible + local engagerange = self.engagerange + local GoZoneSet = self.GoZoneSet + local NoGoZoneSet = self.NoGoZoneSet function CAP_Wing:onbeforeFlightOnMission(From, Event, To, Flightgroup, Mission) local flightgroup = Flightgroup -- Ops.FlightGroup#FLIGHTGROUP @@ -619,7 +641,7 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias) flightgroup:GetGroup():SetOptionRadarUsingForContinousSearch() if Mission.type ~= AUFTRAG.Type.TANKER and Mission.type ~= AUFTRAG.Type.AWACS and Mission.type ~= AUFTRAG.Type.RECON then flightgroup:SetDetection(true) - flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet) + flightgroup:SetEngageDetectedOn(engagerange,{"Air"},GoZoneSet,NoGoZoneSet) flightgroup:SetOutOfAAMRTB() if CapFormation then flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation) @@ -801,6 +823,11 @@ function EASYGCICAP:_SetCAPPatrolPoints() self:T(self.lid.."_SetCAPPatrolPoints") for _,_data in pairs(self.ManagedCP) do local data = _data --#EASYGCICAP.CapPoint + self:T("Airbasename = "..data.AirbaseName) + if not self.wings[data.AirbaseName] then + MESSAGE:New(self.lid.."You are trying to create a CAP point for which there is no wing! "..tostring(data.AirbaseName),30,"CHECK"):ToAllIf(self.debug):ToLog() + return + end local Wing = self.wings[data.AirbaseName][1] -- Ops.Airwing#AIRWING local Coordinate = data.Coordinate local Altitude = data.Altitude diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 1d77397e0..41b2fb35a 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -259,7 +259,7 @@ function FLIGHTGROUP:New(group) local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #FLIGHTGROUP -- Set some string id for output to DCS.log file. - self.lid=string.format("FLIGHTGROUP %s | ", self.groupname) + self.lid=string.format("FLIGHTGROUP %s | ", self.groupname or "N/A") -- Defaults self:SetDefaultROE() @@ -2002,6 +2002,9 @@ function FLIGHTGROUP:onafterElementAirborne(From, Event, To, Element) -- Debug info. self:T2(self.lid..string.format("Element airborne %s", Element.name)) + + -- Set parking spot to free. Also for FC. This is usually done after taxiing but doing it here in case the group is teleported. + self:_SetElementParkingFree(Element) -- Set element status. self:_UpdateStatus(Element, OPSGROUP.ElementStatus.AIRBORNE) diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index f9996cf91..f582f0675 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -2324,7 +2324,7 @@ INTEL_DLINK = { verbose = 0, lid = nil, alias = nil, - cachetime = 300, + cachetime = 120, interval = 20, contacts = {}, clusters = {}, @@ -2333,7 +2333,7 @@ INTEL_DLINK = { --- Version string -- @field #string version -INTEL_DLINK.version = "0.0.1" +INTEL_DLINK.version = "0.0.2" --- Function to instantiate a new object -- @param #INTEL_DLINK self @@ -2384,15 +2384,15 @@ function INTEL_DLINK:New(Intels, Alias, Interval, Cachetime) self.alias="SPECTRE" end - -- Cache time - self.cachetime = Cachetime or 300 - -- Interval self.interval = Interval or 20 -- Set some string id for output to DCS.log file. self.lid=string.format("INTEL_DLINK %s | ", self.alias) + -- Cache time + self:SetDLinkCacheTime(Cachetime or 120) + -- Start State. self:SetStartState("Stopped") @@ -2477,6 +2477,16 @@ function INTEL_DLINK:onafterStart(From, Event, To) return self end + --- Function to set how long INTEL DLINK remembers contacts. + -- @param #INTEL_DLINK self + -- @param #number seconds Remember this many seconds. Defaults to 180. + -- @return #INTEL_DLINK self + function INTEL_DLINK:SetDLinkCacheTime(seconds) + self.cachetime = math.abs(seconds or 120) + self:I(self.lid.."Caching for "..self.cachetime.." seconds.") + return self + end + --- Function to collect data from the various #INTEL -- @param #INTEL_DLINK self -- @param #string From The From state diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 268dfd008..0a62c204e 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -5589,10 +5589,13 @@ function OPSGROUP:onafterUnpauseMission(From, Event, To) -- Debug info. self:T(self.lid..string.format("Unpausing mission %s [%s]", mission:GetName(), mission:GetType())) + -- Set state of mission, e.g. for not teleporting again + mission.unpaused=true + -- Start mission. self:MissionStart(mission) - -- Remove mission from + -- Remove mission from pausedmissions queue for i,mid in pairs(self.pausedmissions) do --self:T(self.lid..string.format("Checking paused mission", mid)) if mid==mission.auftragsnummer then @@ -6232,7 +6235,7 @@ function OPSGROUP:RouteToMission(mission, delay) end -- Check if group is mobile. Note that some immobile units report a speed of 1 m/s = 3.6 km/h. - if self.speedMax<=3.6 or mission.teleport then + if (self.speedMax<=3.6 or mission.teleport) and not mission.unpaused then -- Teleport to waypoint coordinate. Mission will not be paused. self:Teleport(waypointcoord, nil, true) diff --git a/Moose Development/Moose/Ops/OpsZone.lua b/Moose Development/Moose/Ops/OpsZone.lua index bbcf4f97b..9a47b16ae 100644 --- a/Moose Development/Moose/Ops/OpsZone.lua +++ b/Moose Development/Moose/Ops/OpsZone.lua @@ -53,7 +53,8 @@ -- @field #number threatlevelCapture Threat level necessary to capture a zone. -- @field Core.Set#SET_UNIT ScanUnitSet Set of scanned units. -- @field Core.Set#SET_GROUP ScanGroupSet Set of scanned groups. --- @extends Core.Fsm#FSM +-- @field #number UpdateSeconds Run status every this many seconds. +-- @extends Core.Fsm#FSM --- *Gentlemen, when the enemy is committed to a mistake we must not interrupt him too soon.* --- Horation Nelson -- @@ -77,6 +78,7 @@ OPSZONE = { Tnut = 0, chiefs = {}, Missions = {}, + UpdateSeconds = 120, } --- OPSZONE.MISSION @@ -97,7 +99,7 @@ OPSZONE.ZoneType={ --- OPSZONE class version. -- @field #string version -OPSZONE.version="0.6.1" +OPSZONE.version="0.6.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -733,7 +735,8 @@ function OPSZONE:onafterStart(From, Event, To) self.timerStatus=self.timerStatus or TIMER:New(OPSZONE.Status, self) -- Status update. - self.timerStatus:Start(1, 120) + local EveryUpdateIn = self.UpdateSeconds or 120 + self.timerStatus:Start(1, EveryUpdateIn) -- Handle base captured event. if self.airbase then diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index 673c5095c..7c912235f 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update Jan 2025 +-- @date Last Update May 2025 do @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.25" +PLAYERTASK.version="0.1.27" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -556,6 +556,7 @@ end -- @param #PLAYERTASK self -- @param #SET_BASE CaptureSquadGroupNamePrefix The prefix of the group name that needs to capture the zone. -- @param #number Coalition The coalition that needs to capture the zone. +-- @param #boolean CheckClientInZone If true, a CLIENT assigned to this task also needs to be in the zone for the task to be successful. -- @return #PLAYERTASK self -- @usage -- -- We can use either STATIC, SET_STATIC, SCENERY or SET_SCENERY as target objects. @@ -570,20 +571,20 @@ end -- -- -- We set CaptureSquadGroupNamePrefix the group name prefix as set in the ME or the spawn of the group that need to be present at the OpsZone like a capture squad, -- -- and set the capturing Coalition in order to trigger a successful task. --- mytask:AddOpsZoneCaptureSuccessCondition("capture-squad", coalition.side.BLUE) +-- mytask:AddOpsZoneCaptureSuccessCondition("capture-squad", coalition.side.BLUE, false) -- -- playerTaskManager:AddPlayerTaskToQueue(mytask) -function PLAYERTASK:AddOpsZoneCaptureSuccessCondition(CaptureSquadGroupNamePrefix, Coalition) +function PLAYERTASK:AddOpsZoneCaptureSuccessCondition(CaptureSquadGroupNamePrefix, Coalition, CheckClientInZone) local task = self task:AddConditionSuccess( function(target) if target:IsInstanceOf("OPSZONE") then - return task:_CheckCaptureOpsZoneSuccess(target, CaptureSquadGroupNamePrefix, Coalition, true) + return task:_CheckCaptureOpsZoneSuccess(target, CaptureSquadGroupNamePrefix, Coalition, CheckClientInZone or true) elseif target:IsInstanceOf("SET_OPSZONE") then local successes = 0 local isClientInZone = false target:ForEachZone(function(opszone) - if task:_CheckCaptureOpsZoneSuccess(opszone, CaptureSquadGroupNamePrefix, Coalition) then + if task:_CheckCaptureOpsZoneSuccess(opszone, CaptureSquadGroupNamePrefix, Coalition, CheckClientInZone or true) then successes = successes + 1 end @@ -979,6 +980,12 @@ function PLAYERTASK:onafterStatus(From, Event, To) if status == "Stopped" then return self end + -- update marker in case target is moving + if self.TargetMarker then + local coordinate = self.Target:GetCoordinate() + self.TargetMarker:UpdateCoordinate(coordinate,0.5) + end + -- Check Target status local targetdead = false @@ -1902,7 +1909,7 @@ PLAYERTASKCONTROLLER.Messages = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASKCONTROLLER.version="0.1.69" +PLAYERTASKCONTROLLER.version="0.1.70" --- Create and run a new TASKCONTROLLER instance. -- @param #PLAYERTASKCONTROLLER self @@ -1944,7 +1951,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) self.taskinfomenu = false self.activehasinfomenu = false self.MenuName = nil - self.menuitemlimit = 5 + self.menuitemlimit = 6 self.holdmenutime = 30 self.MarkerReadOnly = false @@ -2415,7 +2422,7 @@ function PLAYERTASKCONTROLLER:EnablePrecisionBombing(FlightGroup,LaserCode,Holdi end ) else - self:E(self.lid.."No FLIGHTGROUP object passed or FLIGHTGROUP is not alive!") + self:E(self.lid.."No OPSGROUP/SET_OPSGROUP object passed or object is not alive!") end else self.autolase = nil @@ -2574,7 +2581,7 @@ function PLAYERTASKCONTROLLER:SetMenuOptions(InfoMenu,ItemLimit,HoldTime) if self.activehasinfomenu then self:EnableTaskInfoMenu() end - self.menuitemlimit = ItemLimit or 5 + self.menuitemlimit = ItemLimit+1 or 6 self.holdmenutime = HoldTime or 30 return self end @@ -3479,7 +3486,7 @@ end -- @param #PLAYERTASKCONTROLLER self -- @param Ops.PlayerTask#PLAYERTASK PlayerTask -- @param #boolean Silent If true, make no "has new task" announcement --- @param #boolen TaskFilter If true, apply the white/black-list task filters here, also +-- @param #boolean TaskFilter If true, apply the white/black-list task filters here, also -- @return #PLAYERTASKCONTROLLER self -- @usage -- Example to create a PLAYERTASK of type CTLD and give Players 10 minutes to complete: @@ -3703,6 +3710,7 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) else CoordText = Coordinate:ToStringA2A(Client,nil,self.ShowMagnetic) end + --self:I("CoordText = "..CoordText) -- Threat Level local ThreatLevel = task.Target:GetThreatLevelMax() --local ThreatLevelText = "high" @@ -3837,7 +3845,8 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) Text = string.gsub(Text,"9","niner") CoordText = "MGRS;"..Text if self.PathToGoogleKey then - CoordText = string.format("%s",CoordText) + --CoordText = string.format("%s",CoordText) + --doesn't seem to work any longer end --self:I(self.lid.." | ".. CoordText) end @@ -3855,10 +3864,12 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client) CoordText = string.gsub(ttstext," BR, "," Bee, Arr, ") end elseif task:HasFreetext() then + -- add tts freetext local brieftxt = self.gettext:GetEntry("BRIEFING",self.locale) ttstext = ttstext .. string.format("; %s: ",brieftxt)..task:GetFreetextTTS() end + --self:I("**** TTS Text ****\n"..ttstext.."\n*****") self.SRSQueue:NewTransmission(ttstext,nil,self.SRS,nil,2) end else @@ -4357,7 +4368,7 @@ function PLAYERTASKCONTROLLER:SwitchDetectStatics(OnOff) return self end ---- [User] Add accept zone to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +--- [User] Add an accept zone to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. -- @param #PLAYERTASKCONTROLLER self -- @param Core.Zone#ZONE AcceptZone Add a zone to the accept zone set. -- @return #PLAYERTASKCONTROLLER self @@ -4371,7 +4382,7 @@ function PLAYERTASKCONTROLLER:AddAcceptZone(AcceptZone) return self end ---- [User] Add accept SET_ZONE to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +--- [User] Add an accept SET_ZONE to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. -- @param #PLAYERTASKCONTROLLER self -- @param Core.Set#SET_ZONE AcceptZoneSet Add a SET_ZONE to the accept zone set. -- @return #PLAYERTASKCONTROLLER self @@ -4385,7 +4396,7 @@ function PLAYERTASKCONTROLLER:AddAcceptZoneSet(AcceptZoneSet) return self end ---- [User] Add reject zone to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +--- [User] Add a reject zone to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. -- @param #PLAYERTASKCONTROLLER self -- @param Core.Zone#ZONE RejectZone Add a zone to the reject zone set. -- @return #PLAYERTASKCONTROLLER self @@ -4399,7 +4410,7 @@ function PLAYERTASKCONTROLLER:AddRejectZone(RejectZone) return self end ---- [User] Add reject SET_ZONE to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +--- [User] Add a reject SET_ZONE to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. -- @param #PLAYERTASKCONTROLLER self -- @param Core.Set#SET_ZONE RejectZoneSet Add a zone to the reject zone set. -- @return #PLAYERTASKCONTROLLER self @@ -4413,9 +4424,37 @@ function PLAYERTASKCONTROLLER:AddRejectZoneSet(RejectZoneSet) return self end ---- [User] Remove accept zone from INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +--- [User] Add a conflict zone to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. -- @param #PLAYERTASKCONTROLLER self --- @param Core.Zone#ZONE AcceptZone Add a zone to the accept zone set. +-- @param Core.Zone#ZONE ConflictZone Add a zone to the conflict zone set. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:AddConflictZone(ConflictZone) + self:T(self.lid.."AddConflictZone") + if self.Intel then + self.Intel:AddConflictZone(ConflictZone) + else + self:E(self.lid.."*****NO detection has been set up (yet)!") + end + return self +end + +--- [User] Add a conflict SET_ZONE to INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Set#SET_ZONE ConflictZoneSet Add a zone to the conflict zone set. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:AddConflictZoneSet(ConflictZoneSet) + self:T(self.lid.."AddConflictZoneSet") + if self.Intel then + self.Intel.conflictzoneset:AddSet(ConflictZoneSet) + else + self:E(self.lid.."*****NO detection has been set up (yet)!") + end + return self +end + +--- [User] Remove an accept zone from INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Zone#ZONE AcceptZone Remove this zone from the accept zone set. -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:RemoveAcceptZone(AcceptZone) self:T(self.lid.."RemoveAcceptZone") @@ -4427,11 +4466,11 @@ function PLAYERTASKCONTROLLER:RemoveAcceptZone(AcceptZone) return self end ---- [User] Remove reject zone from INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +--- [User] Remove a reject zone from INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. -- @param #PLAYERTASKCONTROLLER self --- @param Core.Zone#ZONE RejectZone Add a zone to the reject zone set. +-- @param Core.Zone#ZONE RejectZone Remove this zone from the reject zone set. -- @return #PLAYERTASKCONTROLLER self -function PLAYERTASKCONTROLLER:RemoveRejectZoneSet(RejectZone) +function PLAYERTASKCONTROLLER:RemoveRejectZone(RejectZone) self:T(self.lid.."RemoveRejectZone") if self.Intel then self.Intel:RemoveRejectZone(RejectZone) @@ -4441,6 +4480,20 @@ function PLAYERTASKCONTROLLER:RemoveRejectZoneSet(RejectZone) return self end +--- [User] Remove a conflict zone from INTEL detection. You need to set up detection with @{#PLAYERTASKCONTROLLER.SetupIntel}() **before** using this. +-- @param #PLAYERTASKCONTROLLER self +-- @param Core.Zone#ZONE ConflictZone Remove this zone from the conflict zone set. +-- @return #PLAYERTASKCONTROLLER self +function PLAYERTASKCONTROLLER:RemoveConflictZone(ConflictZone) + self:T(self.lid.."RemoveConflictZone") + if self.Intel then + self.Intel:RemoveConflictZone(ConflictZone) + else + self:E(self.lid.."*****NO detection has been set up (yet)!") + end + return self +end + --- [User] Set the top menu name to a custom string. -- @param #PLAYERTASKCONTROLLER self -- @param #string Name The name to use as the top menu designation. diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index fcc108087..4863f5202 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -1715,6 +1715,26 @@ function TARGET:GetAverageCoordinate() return nil end + +--- Get coordinates of all targets. (e.g. for a SET_STATIC) +-- @param #TARGET self +-- @return #table Table with coordinates of all targets. +function TARGET:GetCoordinates() + local coordinates={} + + for _,_target in pairs(self.targets) do + local target=_target --#TARGET.Object + + local coordinate=self:GetTargetCoordinate(target) + if coordinate then + table.insert(coordinates, coordinate) + end + + end + + return coordinates +end + --- Get heading of target. -- @param #TARGET self -- @return #number Heading of the target in degrees. @@ -1968,6 +1988,21 @@ function TARGET:GetObject(RefCoordinate, Coalitions) return nil end +--- Get all target objects. +-- @param #TARGET self +-- @return #table List of target objects. +function TARGET:GetObjects() + local objects={} + + for _,_target in pairs(self.targets) do + local target=_target --#TARGET.Object + + table.insert(objects, target.Object) + end + + return objects +end + --- Count alive objects. -- @param #TARGET self -- @param #TARGET.Object Target Target objective. diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 59ab36a3c..68da47300 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -29,6 +29,8 @@ --- Governs multiple missions, the tasking and the reporting. +-- +-- ![Banner Image](..\Images\deprecated.png) -- -- Command centers govern missions, communicates the task assignments between human players of the coalition, and manages the menu flow. -- It can assign a random task to a player when requested. diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 81dd1f882..16025282a 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -5,6 +5,8 @@ -- The @{#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. -- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- 1.1) DETECTION_MANAGER constructor: -- ----------------------------------- -- * @{#DETECTION_MANAGER.New}(): Create a new DETECTION_MANAGER instance. diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 6fa3da747..8cd08f087 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -28,6 +28,8 @@ --- Models goals to be achieved and can contain multiple tasks to be executed to achieve the goals. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- A mission contains multiple tasks and can be of different task types. -- These tasks need to be assigned to human players to be executed. -- diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 452f88242..04d6a4598 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -12,6 +12,8 @@ -- -- === -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- # 1) Tasking from a player perspective. -- -- Tasking can be controlled by using the "other" menu in the radio menu of the player group. diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 428e629c9..203b94e01 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -18,6 +18,8 @@ --- -- # TASKINFO class, extends @{Core.Base#BASE} -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- ## The TASKINFO class implements the methods to contain information and display information of a task. -- -- # Developer Note diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index 9309526bb..43d6e1f5a 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -20,6 +20,9 @@ do -- TASK_A2A --- Defines Air To Air tasks for a @{Core.Set} of Target Units, -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. + -- + -- ![Banner Image](..\Images\deprecated.png) + -- -- The TASK_A2A is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses: -- -- * **None**: Start of the process diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index d592ad4a6..b3c4b0568 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -30,6 +30,8 @@ do -- TASK_A2A_DISPATCHER -- @extends Tasking.DetectionManager#DETECTION_MANAGER --- Orchestrates the dynamic dispatching of tasks upon groups of detected units determined a @{Core.Set} of EWR installation groups. + -- + -- ![Banner Image](..\Images\deprecated.png) -- -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia3.JPG) -- diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 84bdcf360..f39c4968c 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -20,6 +20,9 @@ do -- TASK_A2G --- The TASK_A2G class defines Air To Ground tasks for a @{Core.Set} of Target Units, -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. + -- + -- ![Banner Image](..\Images\deprecated.png) + -- -- The TASK_A2G is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses: -- -- * **None**: Start of the process diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index a98477d86..9fc410de1 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -33,6 +33,8 @@ do -- TASK_A2G_DISPATCHER -- @extends Tasking.DetectionManager#DETECTION_MANAGER --- Orchestrates dynamic **A2G Task Dispatching** based on the detection results of a linked @{Functional.Detection} object. + -- + -- ![Banner Image](..\Images\deprecated.png) -- -- It uses the Tasking System within the MOOSE framework, which is a multi-player Tasking Orchestration system. -- It provides a truly dynamic battle environment for pilots and ground commanders to engage upon, diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index be894d805..6bfcfffa1 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -10,6 +10,8 @@ -- -- === -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- ## Test Missions: -- -- Test missions can be located on the main GITHUB site. @@ -1176,7 +1178,7 @@ do -- TASK_CARGO end - ---@param Color Might be SMOKECOLOR.Blue, SMOKECOLOR.Red SMOKECOLOR.Orange, SMOKECOLOR.White or SMOKECOLOR.Green + --@param Color Might be SMOKECOLOR.Blue, SMOKECOLOR.Red SMOKECOLOR.Orange, SMOKECOLOR.White or SMOKECOLOR.Green function TASK_CARGO:SetSmokeColor(SmokeColor) -- Makes sure Coloe is set if SmokeColor == nil then diff --git a/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua index c9c476efa..fa334dcf4 100644 --- a/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Capture_Dispatcher.lua @@ -76,6 +76,8 @@ do -- TASK_CAPTURE_DISPATCHER --- Implements the dynamic dispatching of capture zone tasks. -- + -- ![Banner Image](..\Images\deprecated.png) + -- -- The **TASK_CAPTURE_DISPATCHER** allows you to setup various tasks for let human -- players capture zones in a co-operation effort. -- diff --git a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua b/Moose Development/Moose/Tasking/Task_Capture_Zone.lua index 33b464a35..4c2372e8d 100644 --- a/Moose Development/Moose/Tasking/Task_Capture_Zone.lua +++ b/Moose Development/Moose/Tasking/Task_Capture_Zone.lua @@ -20,6 +20,8 @@ do -- TASK_ZONE_GOAL --- # TASK_ZONE_GOAL class, extends @{Tasking.Task#TASK} -- + -- ![Banner Image](..\Images\deprecated.png) + -- -- The TASK_ZONE_GOAL class defines the task to protect or capture a protection zone. -- The TASK_ZONE_GOAL is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses: -- diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index e27a225c0..ca71d64ef 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -44,6 +44,8 @@ -- -- === -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- Please read through the @{Tasking.Task_CARGO} process to understand the mechanisms of tasking and cargo tasking and handling. -- -- The cargo will be a downed pilot, which is located somwhere on the battlefield. Use the menus system and facilities to diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index f78b2d5cd..b79a098bd 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -2,6 +2,8 @@ -- -- The **TASK_CARGO_DISPATCHER** allows you to setup various tasks for let human -- players transport cargo as part of a task. +-- +-- ![Banner Image](..\Images\deprecated.png) -- -- The cargo dispatcher will implement for you mechanisms to create cargo transportation tasks: -- diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index 6293f03fa..3a30eaf22 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -1,5 +1,7 @@ --- **Tasking** - Models tasks for players to transport cargo. -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- **Specific features:** -- -- * Creates a task to transport #Cargo.Cargo to and between deployment zones. diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua index 127b455ad..7cd71c485 100644 --- a/Moose Development/Moose/Tasking/Task_Manager.lua +++ b/Moose Development/Moose/Tasking/Task_Manager.lua @@ -2,6 +2,8 @@ -- -- === -- +-- ![Banner Image](..\Images\deprecated.png) +-- -- 1) @{Tasking.Task_Manager#TASK_MANAGER} class, extends @{Core.Fsm#FSM} -- === -- The @{Tasking.Task_Manager#TASK_MANAGER} class defines the core functions to report tasks to groups. diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 361e786d5..f0c7b4aaf 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -12,27 +12,35 @@ -- @module Utilities.Utils -- @image MOOSE.JPG ---- +--- Smoke color enum `trigger.smokeColor`. -- @type SMOKECOLOR --- @field Green --- @field Red --- @field White --- @field Orange --- @field Blue +-- @field #number Green Green smoke (0) +-- @field #number Red Red smoke (1) +-- @field #number White White smoke (2) +-- @field #number Orange Orange smoke (3) +-- @field #number Blue Blue smoke (4) SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR ---- +--- Flare colur enum `trigger.flareColor`. -- @type FLARECOLOR --- @field Green --- @field Red --- @field White --- @field Yellow +-- @field #number Green (0) +-- @field #number Red Red flare (1) +-- @field #number White White flare (2) +-- @field #number Yellow Yellow flare (3) FLARECOLOR = trigger.flareColor -- #FLARECOLOR --- Big smoke preset enum. -- @type BIGSMOKEPRESET +-- @field #number SmallSmokeAndFire Small moke and fire (1) +-- @field #number MediumSmokeAndFire Medium smoke and fire (2) +-- @field #number LargeSmokeAndFire Large smoke and fire (3) +-- @field #number HugeSmokeAndFire Huge smoke and fire (4) +-- @field #number SmallSmoke Small smoke (5) +-- @field #number MediumSmoke Medium smoke (6) +-- @field #number LargeSmoke Large smoke (7) +-- @field #number HugeSmoke Huge smoke (8) BIGSMOKEPRESET = { SmallSmokeAndFire=1, MediumSmokeAndFire=2, @@ -351,7 +359,7 @@ end -- @return #string Table as a string. UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - lookup_table = {} + local lookup_table = {} local function _Serialize( tbl ) @@ -490,7 +498,7 @@ end --- Counts the number of elements in a table. -- @param #table T Table to count --- @return #int Number of elements in the table +-- @return #number Number of elements in the table function UTILS.TableLength(T) local count = 0 for _ in pairs(T or {}) do count = count + 1 end @@ -1905,6 +1913,13 @@ end function UTILS.GetReportingName(Typename) local typename = string.lower(Typename) + + -- special cases - Shark and Manstay have "A-50" in the name + if string.find(typename,"ka-50",1,true) then + return "Shark" + elseif string.find(typename,"a-50",1,true) then + return "Mainstay" + end for name, value in pairs(ENUMS.ReportingName.NATO) do local svalue = string.lower(value) @@ -2137,9 +2152,9 @@ function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal) local cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude)) if rising and cosH > 1 then - return "N/S" -- The sun never rises on this location on the specified date + return "N/R" -- The sun never rises on this location on the specified date elseif cosH < -1 then - return "N/R" -- The sun never sets on this location on the specified date + return "N/S" -- The sun never sets on this location on the specified date end -- Finish calculating H and convert into hours diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 21d69a296..aaab918bd 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -513,6 +513,7 @@ AIRBASE.TheChannel = { -- * AIRBASE.Syria.Hatzor -- * AIRBASE.Syria.Palmashim -- * AIRBASE.Syria.Tel_Nof +-- * AIRBASE.Syria.Marka -- --@field Syria AIRBASE.Syria={ @@ -586,6 +587,7 @@ AIRBASE.Syria={ ["Hatzor"] = "Hatzor", ["Palmashim"] = "Palmashim", ["Tel_Nof"] = "Tel Nof", + ["Marka"] = "Marka", } --- Airbases of the Mariana Islands map: @@ -790,9 +792,14 @@ AIRBASE.Sinai = { -- * AIRBASE.Kola.Vidsel -- * AIRBASE.Kola.Vuojarvi -- * AIRBASE.Kola.Andoya --- * AIRBASE.Kola.Alakourtti +-- * AIRBASE.Kola.Alakurtti -- * AIRBASE.Kola.Kittila -- * AIRBASE.Kola.Bardufoss +-- * AIRBASE.Kola.Alta +-- * AIRBASE.Kola.Sodankyla +-- * AIRBASE.Kola.Enontekio +-- * AIRBASE.Kola.Evenes +-- * AIRBASE.Kola.Hosio -- -- @field Kola AIRBASE.Kola = { @@ -815,9 +822,14 @@ AIRBASE.Kola = { ["Vidsel"] = "Vidsel", ["Vuojarvi"] = "Vuojarvi", ["Andoya"] = "Andoya", - ["Alakourtti"] = "Alakourtti", + ["Alakurtti"] = "Alakurtti", ["Kittila"] = "Kittila", ["Bardufoss"] = "Bardufoss", + ["Alta"] = "Alta", + ["Sodankyla"] = "Sodankyla", + ["Enontekio"] = "Enontekio", + ["Evenes"] = "Evenes", + ["Hosio"] = "Hosio", } --- Airbases of the Afghanistan map diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index fa597e461..44f313f27 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -912,15 +912,18 @@ function GROUP:GetVelocityVec3() if DCSGroup and DCSGroup:isExist() then local GroupUnits = DCSGroup:getUnits() - local GroupCount = #GroupUnits + local GroupCount = 0 local VelocityVec3 = { x = 0, y = 0, z = 0 } for _, DCSUnit in pairs( GroupUnits ) do - local UnitVelocityVec3 = DCSUnit:getVelocity() - VelocityVec3.x = VelocityVec3.x + UnitVelocityVec3.x - VelocityVec3.y = VelocityVec3.y + UnitVelocityVec3.y - VelocityVec3.z = VelocityVec3.z + UnitVelocityVec3.z + if DCSUnit:isExist() and DCSUnit:isActive() then + local UnitVelocityVec3 = DCSUnit:getVelocity() + VelocityVec3.x = VelocityVec3.x + UnitVelocityVec3.x + VelocityVec3.y = VelocityVec3.y + UnitVelocityVec3.y + VelocityVec3.z = VelocityVec3.z + UnitVelocityVec3.z + GroupCount = GroupCount + 1 + end end VelocityVec3.x = VelocityVec3.x / GroupCount @@ -1754,11 +1757,13 @@ function GROUP:GetMaxVelocity() for Index, UnitData in pairs( DCSGroup:getUnits() ) do - local UnitVelocityVec3 = UnitData:getVelocity() - local UnitVelocity = math.abs( UnitVelocityVec3.x ) + math.abs( UnitVelocityVec3.y ) + math.abs( UnitVelocityVec3.z ) + if UnitData:isExist() and UnitData:isActive() then + local UnitVelocityVec3 = UnitData:getVelocity() + local UnitVelocity = math.abs( UnitVelocityVec3.x ) + math.abs( UnitVelocityVec3.y ) + math.abs( UnitVelocityVec3.z ) - if UnitVelocity > GroupVelocityMax then - GroupVelocityMax = UnitVelocity + if UnitVelocity > GroupVelocityMax then + GroupVelocityMax = UnitVelocity + end end end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 10d59ed81..1a5e51011 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -897,7 +897,7 @@ function UNIT:GetAmmunition() nAPshells = nAPshells + Nammo end - if ammotable[w].desc.typeName and string.find(ammotable[w].desc.typeName, "_HE", 1, true) then + if ammotable[w].desc.typeName and (string.find(ammotable[w].desc.typeName, "_HE", 1, true) or string.find(ammotable[w].desc.typeName, "HESH", 1, true)) then nHEshells = nHEshells + Nammo end @@ -1107,7 +1107,6 @@ function UNIT:GetUnits() if DCSUnit then Units[1] = UNIT:Find(DCSUnit) - - self:T3(Units) return Units end