Merge branch 'FF/Ops' into FF/OpsDev

This commit is contained in:
Frank
2024-01-01 21:55:52 +01:00
57 changed files with 3538 additions and 1657 deletions

View File

@@ -23,7 +23,7 @@
--
-- ## Missions:
--
-- [AID-A2A - AI A2A Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching)
-- [AID-A2A - AI A2A Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher)
--
-- ===
--
@@ -310,7 +310,7 @@ do -- AI_A2A_DISPATCHER
-- Use the method @{#AI_A2A_DISPATCHER.SetEngageRadius}() to set a specific Engage Radius.
-- **The Engage Radius is defined for ALL squadrons which are operational.**
--
-- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-019%20-%20Engage%20Range%20Test)
-- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-019%20-%20Engage%20Range%20Test)
--
-- In this example an Engage Radius is set to various values.
--
@@ -333,7 +333,7 @@ do -- AI_A2A_DISPATCHER
-- Use the method @{#AI_A2A_DISPATCHER.SetGciRadius}() to set a specific controlled ground intercept radius.
-- **The Ground Controlled Intercept radius is defined for ALL squadrons which are operational.**
--
-- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-013%20-%20Intercept%20Test)
-- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-013%20-%20Intercept%20Test)
--
-- In these examples, the Gci Radius is set to various values:
--
@@ -366,7 +366,7 @@ do -- AI_A2A_DISPATCHER
-- it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are.
-- In a hot war the borders are effectively defined by the ground based radar coverage of a coalition.
--
-- Demonstration Mission: [AID-009 - AI_A2A - Border Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-009%20-%20Border%20Test)
-- Demonstration Mission: [AID-009 - AI_A2A - Border Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-009%20-%20Border%20Test)
--
-- In this example a border is set for the CCCP A2A dispatcher:
--
@@ -1233,7 +1233,7 @@ do -- AI_A2A_DISPATCHER
--
-- **Use the method @{#AI_A2A_DISPATCHER.SetEngageRadius}() to modify the default Engage Radius for ALL squadrons.**
--
-- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-019%20-%20Engage%20Range%20Test)
-- Demonstration Mission: [AID-019 - AI_A2A - Engage Range Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-019%20-%20Engage%20Range%20Test)
--
-- @param #AI_A2A_DISPATCHER self
-- @param #number EngageRadius (Optional, Default = 100000) The radius to report friendlies near the target.
@@ -1283,7 +1283,7 @@ do -- AI_A2A_DISPATCHER
-- Use the method @{#AI_A2A_DISPATCHER.SetGciRadius}() to set a specific controlled ground intercept radius.
-- **The Ground Controlled Intercept radius is defined for ALL squadrons which are operational.**
--
-- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching/AID-A2A-013%20-%20Intercept%20Test)
-- Demonstration Mission: [AID-013 - AI_A2A - Intercept Test](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher/AID-A2A-013%20-%20Intercept%20Test)
--
-- @param #AI_A2A_DISPATCHER self
-- @param #number GciRadius (Optional, Default = 200000) The radius to ground control intercept detected targets from the nearest airbase.
@@ -3257,7 +3257,8 @@ do -- AI_A2A_DISPATCHER
end
end
--- @param #AI_A2A_DISPATCHER self
--- AI_A2A_Fsm:onafterHome
-- @param #AI_A2A_DISPATCHER self
function AI_A2A_Fsm:onafterHome( Defender, From, Event, To, Action )
if Defender and Defender:IsAlive() then
self:F( { "CAP Home", Defender:GetName() } )
@@ -3505,7 +3506,8 @@ do -- AI_A2A_DISPATCHER
Dispatcher:ClearDefenderTaskTarget( DefenderGroup )
end
--- @param #AI_A2A_DISPATCHER self
--- function Fsm:onafterLostControl
-- @param #AI_A2A_DISPATCHER self
function Fsm:onafterLostControl( Defender, From, Event, To )
self:F( { "GCI LostControl", Defender:GetName() } )
self:GetParent( self ).onafterHome( self, Defender, From, Event, To )
@@ -3518,7 +3520,8 @@ do -- AI_A2A_DISPATCHER
end
end
--- @param #AI_A2A_DISPATCHER self
--- function Fsm:onafterHome
-- @param #AI_A2A_DISPATCHER self
function Fsm:onafterHome( DefenderGroup, From, Event, To, Action )
self:F( { "GCI Home", DefenderGroup:GetName() } )
self:GetParent( self ).onafterHome( self, DefenderGroup, From, Event, To )
@@ -3959,7 +3962,7 @@ do
--
-- # Demo Missions
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching)
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher)
--
-- ===
--

View File

@@ -24,7 +24,7 @@
--
-- ## Missions:
--
-- [AID-A2G - AI A2G Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2G%20-%20AI%20A2G%20Dispatching)
-- [AID-A2G - AI A2G Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2G_Dispatcher)
--
-- ===
--

View File

@@ -24,7 +24,7 @@
--
-- ## Missions:
--
-- [AID-AIR - AI AIR Dispatching](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching)
-- [AI_A2A_Dispatcher](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_A2A_Dispatcher)
--
-- ===
--

View File

@@ -9,7 +9,7 @@
--
-- ===
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AIB%20-%20AI%20Balancing)
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Balancer)
--
-- ===
--
@@ -168,7 +168,8 @@ function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange )
self.ReturnThresholdRange = ReturnThresholdRange
end
--- @param #AI_BALANCER self
--- AI_BALANCER:onenterSpawning
-- @param #AI_BALANCER self
-- @param Core.Set#SET_GROUP SetGroup
-- @param #string ClientName
-- @param Wrapper.Group#GROUP AIGroup
@@ -190,7 +191,8 @@ function AI_BALANCER:onenterSpawning( SetGroup, From, Event, To, ClientName )
end
end
--- @param #AI_BALANCER self
--- AI_BALANCER:onenterDestroying
-- @param #AI_BALANCER self
-- @param Core.Set#SET_GROUP SetGroup
-- @param Wrapper.Group#GROUP AIGroup
function AI_BALANCER:onenterDestroying( SetGroup, From, Event, To, ClientName, AIGroup )
@@ -233,15 +235,16 @@ function AI_BALANCER:onenterReturning( SetGroup, From, Event, To, AIGroup )
end
--- @param #AI_BALANCER self
--- AI_BALANCER:onenterMonitoring
-- @param #AI_BALANCER self
function AI_BALANCER:onenterMonitoring( SetGroup )
self:T2( { self.SetClient:Count() } )
--self.SetClient:Flush()
self.SetClient:ForEachClient(
--- @param Wrapper.Client#CLIENT Client
--- SetClient:ForEachClient
-- @param Wrapper.Client#CLIENT Client
function( Client )
self:T3(Client.ClientName)
@@ -264,7 +267,8 @@ function AI_BALANCER:onenterMonitoring( SetGroup )
self:T2( RangeZone )
_DATABASE:ForEachPlayerUnit(
--- @param Wrapper.Unit#UNIT RangeTestUnit
--- Nameless function
-- @param Wrapper.Unit#UNIT RangeTestUnit
function( RangeTestUnit, RangeZone, AIGroup, PlayerInRange )
self:T2( { PlayerInRange, RangeTestUnit.UnitName, RangeZone.ZoneName } )
if RangeTestUnit:IsInZone( RangeZone ) == true then
@@ -276,7 +280,8 @@ function AI_BALANCER:onenterMonitoring( SetGroup )
end
end,
--- @param Core.Zone#ZONE_RADIUS RangeZone
--- Nameless function
-- @param Core.Zone#ZONE_RADIUS RangeZone
-- @param Wrapper.Group#GROUP AIGroup
function( RangeZone, AIGroup, PlayerInRange )
if PlayerInRange.Value == false then
@@ -307,6 +312,3 @@ function AI_BALANCER:onenterMonitoring( SetGroup )
self:__Monitor( 10 )
end

View File

@@ -18,7 +18,7 @@
--
-- Test missions can be located on the main GITHUB site.
--
-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching)
-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AI/AI_Cargo_Dispatcher)
--
-- ===
--
@@ -572,7 +572,7 @@
-- A home zone can be specified to where the Carriers will move when there isn't any cargo left for pickup.
-- Use @{#AI_CARGO_DISPATCHER.SetHomeZone}() to specify the home zone.
--
-- If no home zone is specified, the carriers will wait near the deploy zone for a new pickup command.
-- If no home zone is specified, the carriers will wait near the deploy zone for a new pickup command.
--
-- ===
--
@@ -583,10 +583,12 @@ AI_CARGO_DISPATCHER = {
PickupCargo = {}
}
--- @field #list
--- List of AI_Cargo
-- @field #list
AI_CARGO_DISPATCHER.AI_Cargo = {}
--- @field #list
--- List of PickupCargo
-- @field #list
AI_CARGO_DISPATCHER.PickupCargo = {}

View File

@@ -370,7 +370,7 @@ CARGOS = {}
do -- CARGO
--- @type CARGO
-- @type CARGO
-- @extends Core.Fsm#FSM_PROCESS
-- @field #string Type A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers.
-- @field #string Name A string defining the name of the cargo. The name is the unique identifier of the cargo.
@@ -433,7 +433,7 @@ do -- CARGO
Reported = {},
}
--- @type CARGO.CargoObjects
-- @type CARGO.CargoObjects
-- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo.
--- CARGO Constructor. This class is an abstract class and should not be instantiated.
@@ -447,7 +447,7 @@ do -- CARGO
function CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) --R2.1
local self = BASE:Inherit( self, FSM:New() ) -- #CARGO
self:F( { Type, Name, Weight, LoadRadius, NearRadius } )
self:T( { Type, Name, Weight, LoadRadius, NearRadius } )
self:SetStartState( "UnLoaded" )
self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" )
@@ -711,7 +711,7 @@ do -- CARGO
-- @param #CARGO self
-- @return #CARGO
function CARGO:Spawn( PointVec2 )
self:F()
self:T()
end
@@ -812,7 +812,7 @@ do -- CARGO
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the CargoGroup is within the loading radius.
function CARGO:IsInLoadRadius( Coordinate )
self:F( { Coordinate, LoadRadius = self.LoadRadius } )
self:T( { Coordinate, LoadRadius = self.LoadRadius } )
local Distance = 0
if self:IsUnLoaded() then
@@ -832,7 +832,7 @@ do -- CARGO
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo can report itself.
function CARGO:IsInReportRadius( Coordinate )
self:F( { Coordinate } )
self:T( { Coordinate } )
local Distance = 0
if self:IsUnLoaded() then
@@ -853,23 +853,23 @@ do -- CARGO
-- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision).
-- @return #boolean
function CARGO:IsNear( Coordinate, NearRadius )
--self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } )
--self:T( { PointVec2 = PointVec2, NearRadius = NearRadius } )
if self.CargoObject:IsAlive() then
--local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() )
--self:F( { CargoObjectName = self.CargoObject:GetName() } )
--self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } )
--self:F( { PointVec2 = PointVec2:GetVec2() } )
--self:T( { CargoObjectName = self.CargoObject:GetName() } )
--self:T( { CargoObjectVec2 = self.CargoObject:GetVec2() } )
--self:T( { PointVec2 = PointVec2:GetVec2() } )
local Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() )
--self:F( { Distance = Distance, NearRadius = NearRadius or "nil" } )
--self:T( { Distance = Distance, NearRadius = NearRadius or "nil" } )
if Distance <= NearRadius then
--self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } )
--self:T( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } )
return true
end
end
--self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = false } )
--self:T( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = false } )
return false
end
@@ -878,12 +878,12 @@ do -- CARGO
-- @param Core.Zone#ZONE_BASE Zone
-- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone.
function CARGO:IsInZone( Zone )
--self:F( { Zone } )
--self:T( { Zone } )
if self:IsLoaded() then
return Zone:IsPointVec2InZone( self.CargoCarrier:GetPointVec2() )
else
--self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } )
--self:T( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } )
if self.CargoObject:GetSize() ~= 0 then
return Zone:IsPointVec2InZone( self.CargoObject:GetPointVec2() )
else
@@ -1034,7 +1034,7 @@ end -- CARGO
do -- CARGO_REPRESENTABLE
--- @type CARGO_REPRESENTABLE
-- @type CARGO_REPRESENTABLE
-- @extends #CARGO
-- @field test
@@ -1056,7 +1056,7 @@ do -- CARGO_REPRESENTABLE
-- Inherit CARGO.
local self = BASE:Inherit( self, CARGO:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE
self:F( { Type, Name, LoadRadius, NearRadius } )
self:T( { Type, Name, LoadRadius, NearRadius } )
-- Descriptors.
local Desc=CargoObject:GetDesc()
@@ -1086,7 +1086,7 @@ do -- CARGO_REPRESENTABLE
function CARGO_REPRESENTABLE:Destroy()
-- Cargo objects are deleted from the _DATABASE and SET_CARGO objects.
self:F( { CargoName = self:GetName() } )
self:T( { CargoName = self:GetName() } )
--_EVENTDISPATCHER:CreateEventDeleteCargo( self )
return self
@@ -1123,12 +1123,12 @@ do -- CARGO_REPRESENTABLE
CoordinateZone:Scan( { Object.Category.UNIT } )
for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do
local NearUnit = UNIT:Find( DCSUnit )
self:F({NearUnit=NearUnit})
self:T({NearUnit=NearUnit})
local NearUnitCoalition = NearUnit:GetCoalition()
local CargoCoalition = self:GetCoalition()
if NearUnitCoalition == CargoCoalition then
local Attributes = NearUnit:GetDesc()
self:F({Desc=Attributes})
self:T({Desc=Attributes})
if NearUnit:HasAttribute( "Trucks" ) then
MESSAGE:New( Message, 20, NearUnit:GetCallsign() .. " reporting - Cargo " .. self:GetName() ):ToGroup( TaskGroup )
break
@@ -1142,7 +1142,7 @@ end -- CARGO_REPRESENTABLE
do -- CARGO_REPORTABLE
--- @type CARGO_REPORTABLE
-- @type CARGO_REPORTABLE
-- @extends #CARGO
CARGO_REPORTABLE = {
ClassName = "CARGO_REPORTABLE"
@@ -1158,7 +1158,7 @@ do -- CARGO_REPORTABLE
-- @return #CARGO_REPORTABLE
function CARGO_REPORTABLE:New( Type, Name, Weight, LoadRadius, NearRadius )
local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_REPORTABLE
self:F( { Type, Name, Weight, LoadRadius, NearRadius } )
self:T( { Type, Name, Weight, LoadRadius, NearRadius } )
return self
end
@@ -1178,7 +1178,7 @@ end
do -- CARGO_PACKAGE
--- @type CARGO_PACKAGE
-- @type CARGO_PACKAGE
-- @extends #CARGO_REPRESENTABLE
CARGO_PACKAGE = {
ClassName = "CARGO_PACKAGE"
@@ -1195,7 +1195,7 @@ do -- CARGO_PACKAGE
-- @return #CARGO_PACKAGE
function CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, LoadRadius, NearRadius )
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_PACKAGE
self:F( { Type, Name, Weight, LoadRadius, NearRadius } )
self:T( { Type, Name, Weight, LoadRadius, NearRadius } )
self:T( CargoCarrier )
self.CargoCarrier = CargoCarrier
@@ -1213,7 +1213,7 @@ end
-- @param #number BoardDistance
-- @param #number Angle
function CARGO_PACKAGE:onafterOnBoard( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle )
self:F()
self:T()
self.CargoInAir = self.CargoCarrier:InAir()
@@ -1246,7 +1246,7 @@ end
-- @param Wrapper.Unit#UNIT CargoCarrier
-- @return #boolean
function CARGO_PACKAGE:IsNear( CargoCarrier )
self:F()
self:T()
local CargoCarrierPoint = CargoCarrier:GetCoordinate()
@@ -1271,7 +1271,7 @@ end
-- @param #number LoadDistance
-- @param #number Angle
function CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle )
self:F()
self:T()
if self:IsNear( CargoCarrier ) then
self:__Load( 1, CargoCarrier, Speed, LoadDistance, Angle )
@@ -1292,7 +1292,7 @@ end
-- @param #number Radius
-- @param #number Angle
function CARGO_PACKAGE:onafterUnBoard( From, Event, To, CargoCarrier, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle )
self:F()
self:T()
self.CargoInAir = self.CargoCarrier:InAir()
@@ -1331,7 +1331,7 @@ end
-- @param Wrapper.Unit#UNIT CargoCarrier
-- @param #number Speed
function CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed )
self:F()
self:T()
if self:IsNear( CargoCarrier ) then
self:__UnLoad( 1, CargoCarrier, Speed )
@@ -1350,7 +1350,7 @@ end
-- @param #number LoadDistance
-- @param #number Angle
function CARGO_PACKAGE:onafterLoad( From, Event, To, CargoCarrier, Speed, LoadDistance, Angle )
self:F()
self:T()
self.CargoCarrier = CargoCarrier
@@ -1378,7 +1378,7 @@ end
-- @param #number Distance
-- @param #number Angle
function CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle )
self:F()
self:T()
local StartPointVec2 = self.CargoCarrier:GetPointVec2()
local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees.

View File

@@ -59,7 +59,7 @@ do -- CARGO_CRATE
-- @return #CARGO_CRATE
function CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_CRATE
self:F( { Type, Name, NearRadius } )
self:T( { Type, Name, NearRadius } )
self.CargoObject = CargoStatic -- Wrapper.Static#STATIC
@@ -116,7 +116,7 @@ do -- CARGO_CRATE
-- @param #string To
-- @param Core.Point#POINT_VEC2
function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 )
--self:F( { ToPointVec2, From, Event, To } )
--self:T( { ToPointVec2, From, Event, To } )
local Angle = 180
local Speed = 10
@@ -153,7 +153,7 @@ do -- CARGO_CRATE
-- @param #string To
-- @param Wrapper.Unit#UNIT CargoCarrier
function CARGO_CRATE:onenterLoaded( From, Event, To, CargoCarrier )
--self:F( { From, Event, To, CargoCarrier } )
--self:T( { From, Event, To, CargoCarrier } )
self.CargoCarrier = CargoCarrier
@@ -190,7 +190,7 @@ do -- CARGO_CRATE
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo Crate is within the report radius.
function CARGO_CRATE:IsInReportRadius( Coordinate )
--self:F( { Coordinate, LoadRadius = self.LoadRadius } )
--self:T( { Coordinate, LoadRadius = self.LoadRadius } )
local Distance = 0
if self:IsUnLoaded() then
@@ -210,7 +210,7 @@ do -- CARGO_CRATE
-- @param Core.Point#Coordinate Coordinate
-- @return #boolean true if the Cargo Crate is within the loading radius.
function CARGO_CRATE:IsInLoadRadius( Coordinate )
--self:F( { Coordinate, LoadRadius = self.NearRadius } )
--self:T( { Coordinate, LoadRadius = self.NearRadius } )
local Distance = 0
if self:IsUnLoaded() then
@@ -231,7 +231,7 @@ do -- CARGO_CRATE
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
-- @return #nil There is no valid Cargo in the CargoGroup.
function CARGO_CRATE:GetCoordinate()
--self:F()
--self:T()
return self.CargoObject:GetCoordinate()
end
@@ -261,7 +261,7 @@ do -- CARGO_CRATE
-- @param #CARGO_CRATE self
-- @param Core.Point#COORDINATE Coordinate
function CARGO_CRATE:RouteTo( Coordinate )
self:F( {Coordinate = Coordinate } )
self:T( {Coordinate = Coordinate } )
end
@@ -274,7 +274,7 @@ do -- CARGO_CRATE
-- @return #boolean The Cargo is near to the Carrier.
-- @return #nil The Cargo is not near to the Carrier.
function CARGO_CRATE:IsNear( CargoCarrier, NearRadius )
self:F( {NearRadius = NearRadius } )
self:T( {NearRadius = NearRadius } )
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
end
@@ -283,7 +283,7 @@ do -- CARGO_CRATE
-- @param #CARGO_CRATE self
function CARGO_CRATE:Respawn()
self:F( { "Respawning crate " .. self:GetName() } )
self:T( { "Respawning crate " .. self:GetName() } )
-- Respawn the group...
@@ -300,7 +300,7 @@ do -- CARGO_CRATE
-- @param #CARGO_CRATE self
function CARGO_CRATE:onafterReset()
self:F( { "Reset crate " .. self:GetName() } )
self:T( { "Reset crate " .. self:GetName() } )
-- Respawn the group...

View File

@@ -64,7 +64,7 @@ do -- CARGO_GROUP
-- Inherit CAROG_REPORTABLE
local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP
self:F( { Type, Name, LoadRadius } )
self:T( { Type, Name, LoadRadius } )
self.CargoSet = SET_CARGO:New()
self.CargoGroup = CargoGroup
@@ -146,7 +146,7 @@ do -- CARGO_GROUP
-- @param #CARGO_GROUP self
function CARGO_GROUP:Respawn()
self:F( { "Respawning" } )
self:T( { "Respawning" } )
for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do
local Cargo = CargoData -- Cargo.Cargo#CARGO
@@ -227,7 +227,7 @@ do -- CARGO_GROUP
-- @param #CARGO_GROUP self
function CARGO_GROUP:Regroup()
self:F("Regroup")
self:T("Regroup")
if self.Grouped == false then
@@ -241,7 +241,7 @@ do -- CARGO_GROUP
for CargoUnitName, CargoUnit in pairs( self.CargoSet:GetSet() ) do
local CargoUnit = CargoUnit -- Cargo.CargoUnit#CARGO_UNIT
self:F( { CargoUnit:GetName(), UnLoaded = CargoUnit:IsUnLoaded() } )
self:T( { CargoUnit:GetName(), UnLoaded = CargoUnit:IsUnLoaded() } )
if CargoUnit:IsUnLoaded() then
@@ -258,7 +258,7 @@ do -- CARGO_GROUP
-- Then we register the new group in the database
self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID )
self:F( { "Regroup", GroupTemplate } )
self:T( { "Regroup", GroupTemplate } )
-- Now we spawn the new group based on the template created.
self.CargoObject = _DATABASE:Spawn( GroupTemplate )
@@ -271,7 +271,7 @@ do -- CARGO_GROUP
-- @param Core.Event#EVENTDATA EventData
function CARGO_GROUP:OnEventCargoDead( EventData )
self:E(EventData)
self:T(EventData)
local Destroyed = false
@@ -296,7 +296,7 @@ do -- CARGO_GROUP
if Destroyed then
self:Destroyed()
self:E( { "Cargo group destroyed" } )
self:T( { "Cargo group destroyed" } )
end
end
@@ -309,14 +309,14 @@ do -- CARGO_GROUP
-- @param Wrapper.Unit#UNIT CargoCarrier
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
function CARGO_GROUP:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... )
self:F( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } )
self:T( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } )
NearRadius = NearRadius or self.NearRadius
-- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2
self.CargoSet:ForEach(
function( Cargo, ... )
self:F( { "Board Unit", Cargo:GetName( ), Cargo:IsDestroyed(), Cargo.CargoObject:IsAlive() } )
self:T( { "Board Unit", Cargo:GetName( ), Cargo:IsDestroyed(), Cargo.CargoObject:IsAlive() } )
local CargoGroup = Cargo.CargoObject --Wrapper.Group#GROUP
CargoGroup:OptionAlarmStateGreen()
Cargo:__Board( 1, CargoCarrier, NearRadius, ... )
@@ -334,7 +334,7 @@ do -- CARGO_GROUP
-- @param #string To
-- @param Wrapper.Unit#UNIT CargoCarrier
function CARGO_GROUP:onafterLoad( From, Event, To, CargoCarrier, ... )
--self:F( { From, Event, To, CargoCarrier, ...} )
--self:T( { From, Event, To, CargoCarrier, ...} )
if From == "UnLoaded" then
-- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier.
@@ -359,7 +359,7 @@ do -- CARGO_GROUP
-- @param Wrapper.Unit#UNIT CargoCarrier
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
--self:F( { CargoCarrier.UnitName, From, Event, To } )
--self:T( { CargoCarrier.UnitName, From, Event, To } )
local Boarded = true
local Cancelled = false
@@ -393,7 +393,7 @@ do -- CARGO_GROUP
if not Boarded then
self:__Boarding( -5, CargoCarrier, NearRadius, ... )
else
self:F("Group Cargo is loaded")
self:T("Group Cargo is loaded")
self:__Load( 1, CargoCarrier, ... )
end
else
@@ -413,7 +413,7 @@ do -- CARGO_GROUP
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
function CARGO_GROUP:onafterUnBoard( From, Event, To, ToPointVec2, NearRadius, ... )
self:F( {From, Event, To, ToPointVec2, NearRadius } )
self:T( {From, Event, To, ToPointVec2, NearRadius } )
NearRadius = NearRadius or 25
@@ -456,7 +456,7 @@ do -- CARGO_GROUP
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier.
function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... )
--self:F( { From, Event, To, ToPointVec2, NearRadius } )
--self:T( { From, Event, To, ToPointVec2, NearRadius } )
--local NearRadius = NearRadius or 25
@@ -493,7 +493,7 @@ do -- CARGO_GROUP
-- @param #string To
-- @param Core.Point#POINT_VEC2 ToPointVec2
function CARGO_GROUP:onafterUnLoad( From, Event, To, ToPointVec2, ... )
--self:F( { From, Event, To, ToPointVec2 } )
--self:T( { From, Event, To, ToPointVec2 } )
if From == "Loaded" then
@@ -611,7 +611,7 @@ do -- CARGO_GROUP
-- @param #CARGO_GROUP self
-- @param Core.Point#COORDINATE Coordinate
function CARGO_GROUP:RouteTo( Coordinate )
--self:F( {Coordinate = Coordinate } )
--self:T( {Coordinate = Coordinate } )
-- For each Cargo within the CargoSet, route each object to the Coordinate
self.CargoSet:ForEach(
@@ -629,13 +629,13 @@ do -- CARGO_GROUP
-- @param #number NearRadius
-- @return #boolean The Cargo is near to the Carrier or #nil if the Cargo is not near to the Carrier.
function CARGO_GROUP:IsNear( CargoCarrier, NearRadius )
self:F( {NearRadius = NearRadius } )
self:T( {NearRadius = NearRadius } )
for _, Cargo in pairs( self.CargoSet:GetSet() ) do
local Cargo = Cargo -- Cargo.Cargo#CARGO
if Cargo:IsAlive() then
if Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) then
self:F( "Near" )
self:T( "Near" )
return true
end
end
@@ -649,7 +649,7 @@ do -- CARGO_GROUP
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo Group is within the load radius.
function CARGO_GROUP:IsInLoadRadius( Coordinate )
--self:F( { Coordinate } )
--self:T( { Coordinate } )
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
@@ -669,7 +669,7 @@ do -- CARGO_GROUP
return false
end
self:F( { Distance = Distance, LoadRadius = self.LoadRadius } )
self:T( { Distance = Distance, LoadRadius = self.LoadRadius } )
if Distance <= self.LoadRadius then
return true
else
@@ -687,12 +687,12 @@ do -- CARGO_GROUP
-- @param Core.Point#Coordinate Coordinate
-- @return #boolean true if the Cargo Group is within the report radius.
function CARGO_GROUP:IsInReportRadius( Coordinate )
--self:F( { Coordinate } )
--self:T( { Coordinate } )
local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO
if Cargo then
self:F( { Cargo } )
self:T( { Cargo } )
local Distance = 0
if Cargo:IsUnLoaded() then
Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() )
@@ -738,7 +738,7 @@ do -- CARGO_GROUP
-- @return #boolean **true** if the first element of the CargoGroup is in the Zone
-- @return #boolean **false** if there is no element of the CargoGroup in the Zone.
function CARGO_GROUP:IsInZone( Zone )
--self:F( { Zone } )
--self:T( { Zone } )
local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO

View File

@@ -52,7 +52,7 @@ do -- CARGO_SLINGLOAD
-- @return #CARGO_SLINGLOAD
function CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius )
local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_SLINGLOAD
self:F( { Type, Name, NearRadius } )
self:T( { Type, Name, NearRadius } )
self.CargoObject = CargoStatic
@@ -130,7 +130,7 @@ do -- CARGO_SLINGLOAD
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo Crate is within the report radius.
function CARGO_SLINGLOAD:IsInReportRadius( Coordinate )
--self:F( { Coordinate, LoadRadius = self.LoadRadius } )
--self:T( { Coordinate, LoadRadius = self.LoadRadius } )
local Distance = 0
if self:IsUnLoaded() then
@@ -149,7 +149,7 @@ do -- CARGO_SLINGLOAD
-- @param Core.Point#COORDINATE Coordinate
-- @return #boolean true if the Cargo Slingload is within the loading radius.
function CARGO_SLINGLOAD:IsInLoadRadius( Coordinate )
--self:F( { Coordinate } )
--self:T( { Coordinate } )
local Distance = 0
if self:IsUnLoaded() then
@@ -169,7 +169,7 @@ do -- CARGO_SLINGLOAD
-- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup.
-- @return #nil There is no valid Cargo in the CargoGroup.
function CARGO_SLINGLOAD:GetCoordinate()
--self:F()
--self:T()
return self.CargoObject:GetCoordinate()
end
@@ -199,7 +199,7 @@ do -- CARGO_SLINGLOAD
-- @param #CARGO_SLINGLOAD self
-- @param Core.Point#COORDINATE Coordinate
function CARGO_SLINGLOAD:RouteTo( Coordinate )
--self:F( {Coordinate = Coordinate } )
--self:T( {Coordinate = Coordinate } )
end
@@ -212,7 +212,7 @@ do -- CARGO_SLINGLOAD
-- @return #boolean The Cargo is near to the Carrier.
-- @return #nil The Cargo is not near to the Carrier.
function CARGO_SLINGLOAD:IsNear( CargoCarrier, NearRadius )
--self:F( {NearRadius = NearRadius } )
--self:T( {NearRadius = NearRadius } )
return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius )
end
@@ -222,7 +222,7 @@ do -- CARGO_SLINGLOAD
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:Respawn()
--self:F( { "Respawning slingload " .. self:GetName() } )
--self:T( { "Respawning slingload " .. self:GetName() } )
-- Respawn the group...
@@ -239,7 +239,7 @@ do -- CARGO_SLINGLOAD
-- @param #CARGO_SLINGLOAD self
function CARGO_SLINGLOAD:onafterReset()
--self:F( { "Reset slingload " .. self:GetName() } )
--self:T( { "Reset slingload " .. self:GetName() } )
-- Respawn the group...

View File

@@ -75,7 +75,7 @@ do -- CARGO_UNIT
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius (optional) Defaut 25 m.
function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
self:F( { From, Event, To, ToPointVec2, NearRadius } )
self:T( { From, Event, To, ToPointVec2, NearRadius } )
local Angle = 180
local Speed = 60
@@ -114,7 +114,7 @@ do -- CARGO_UNIT
else
self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading )
end
self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } )
self:T( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } )
self.CargoCarrier = nil
local Points = {}
@@ -148,7 +148,7 @@ do -- CARGO_UNIT
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius (optional) Defaut 100 m.
function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius )
self:F( { From, Event, To, ToPointVec2, NearRadius } )
self:T( { From, Event, To, ToPointVec2, NearRadius } )
local Angle = 180
local Speed = 10
@@ -174,7 +174,7 @@ do -- CARGO_UNIT
-- @param Core.Point#POINT_VEC2 ToPointVec2
-- @param #number NearRadius (optional) Defaut 100 m.
function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius )
self:F( { From, Event, To, ToPointVec2, NearRadius } )
self:T( { From, Event, To, ToPointVec2, NearRadius } )
self.CargoInAir = self.CargoObject:InAir()
@@ -199,7 +199,7 @@ do -- CARGO_UNIT
-- @param #string To
-- @param Core.Point#POINT_VEC2
function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 )
self:F( { ToPointVec2, From, Event, To } )
self:T( { ToPointVec2, From, Event, To } )
local Angle = 180
local Speed = 10
@@ -236,7 +236,7 @@ do -- CARGO_UNIT
-- @param Wrapper.Group#GROUP CargoCarrier
-- @param #number NearRadius
function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... )
self:F( { From, Event, To, CargoCarrier, NearRadius = NearRadius } )
self:T( { From, Event, To, CargoCarrier, NearRadius = NearRadius } )
self.CargoInAir = self.CargoObject:InAir()
@@ -244,7 +244,7 @@ do -- CARGO_UNIT
local MaxSpeed = Desc.speedMaxOffRoad
local TypeName = Desc.typeName
--self:F({Unit=self.CargoObject:GetName()})
--self:T({Unit=self.CargoObject:GetName()})
-- A cargo unit can only be boarded if it is not dead
@@ -298,9 +298,9 @@ do -- CARGO_UNIT
-- @param Wrapper.Client#CLIENT CargoCarrier
-- @param #number NearRadius Default 25 m.
function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... )
self:F( { From, Event, To, CargoCarrier:GetName(), NearRadius = NearRadius } )
self:T( { From, Event, To, CargoCarrier:GetName(), NearRadius = NearRadius } )
self:F( { IsAlive=self.CargoObject:IsAlive() } )
self:T( { IsAlive=self.CargoObject:IsAlive() } )
if CargoCarrier and CargoCarrier:IsAlive() then -- and self.CargoObject and self.CargoObject:IsAlive() then
if (CargoCarrier:IsAir() and not CargoCarrier:InAir()) or true then
@@ -321,7 +321,7 @@ do -- CARGO_UNIT
local Angle = 180
local Distance = 0
--self:F({Unit=self.CargoObject:GetName()})
--self:T({Unit=self.CargoObject:GetName()})
local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2()
local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees.
@@ -348,7 +348,7 @@ do -- CARGO_UNIT
self.CargoObject:SetCommand( self.CargoObject:CommandStopRoute( true ) )
end
else
self:E("Something is wrong")
self:T("Something is wrong")
end
end
@@ -361,11 +361,11 @@ do -- CARGO_UNIT
-- @param #string To
-- @param Wrapper.Unit#UNIT CargoCarrier
function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier )
self:F( { From, Event, To, CargoCarrier } )
self:T( { From, Event, To, CargoCarrier } )
self.CargoCarrier = CargoCarrier
--self:F({Unit=self.CargoObject:GetName()})
--self:T({Unit=self.CargoObject:GetName()})
-- Only destroy the CargoObject if there is a CargoObject (packages don't have CargoObjects).
if self.CargoObject then

View File

@@ -449,10 +449,10 @@ do -- Zones and Pathlines
-- Loop over layers.
for layerID, layerData in pairs(env.mission.drawings.layers or {}) do
-- Loop over objects in layers.
for objectID, objectData in pairs(layerData.objects or {}) do
-- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice)
if objectData.polygonMode and (objectData.polygonMode=="free") and objectData.points and #objectData.points>=4 then
@@ -488,10 +488,32 @@ do -- Zones and Pathlines
-- Create new polygon zone.
local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points)
--Zone.DrawID = objectID
-- Set color.
Zone:SetColor({1, 0, 0}, 0.15)
Zone:SetFillColor({1, 0, 0}, 0.15)
if objectData.colorString then
-- eg colorString = 0xff0000ff
local color = string.gsub(objectData.colorString,"^0x","")
local r = tonumber(string.sub(color,1,2),16)/255
local g = tonumber(string.sub(color,3,4),16)/255
local b = tonumber(string.sub(color,5,6),16)/255
local a = tonumber(string.sub(color,7,8),16)/255
Zone:SetColor({r, g, b}, a)
end
if objectData.fillColorString then
-- eg fillColorString = 0xff00004b
local color = string.gsub(objectData.colorString,"^0x","")
local r = tonumber(string.sub(color,1,2),16)/255
local g = tonumber(string.sub(color,3,4),16)/255
local b = tonumber(string.sub(color,5,6),16)/255
local a = tonumber(string.sub(color,7,8),16)/255
Zone:SetFillColor({r, g, b}, a)
end
-- Store in DB.
self.ZONENAMES[ZoneName] = ZoneName
@@ -532,7 +554,26 @@ do -- Zones and Pathlines
-- Set color.
Zone:SetColor({1, 0, 0}, 0.15)
if objectData.colorString then
-- eg colorString = 0xff0000ff
local color = string.gsub(objectData.colorString,"^0x","")
local r = tonumber(string.sub(color,1,2),16)/255
local g = tonumber(string.sub(color,3,4),16)/255
local b = tonumber(string.sub(color,5,6),16)/255
local a = tonumber(string.sub(color,7,8),16)/255
Zone:SetColor({r, g, b}, a)
end
if objectData.fillColorString then
-- eg fillColorString = 0xff00004b
local color = string.gsub(objectData.colorString,"^0x","")
local r = tonumber(string.sub(color,1,2),16)/255
local g = tonumber(string.sub(color,3,4),16)/255
local b = tonumber(string.sub(color,5,6),16)/255
local a = tonumber(string.sub(color,7,8),16)/255
Zone:SetFillColor({r, g, b}, a)
end
-- Store in DB.
self.ZONENAMES[ZoneName] = ZoneName
@@ -756,7 +797,7 @@ end -- cargo
--- Finds a CLIENT based on the ClientName.
-- @param #DATABASE self
-- @param #string ClientName
-- @param #string ClientName - Note this is the UNIT name of the client!
-- @return Wrapper.Client#CLIENT The found CLIENT.
function DATABASE:FindClient( ClientName )

View File

@@ -368,7 +368,7 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings )
if CoalitionSide then
if self.MessageDuration ~= 0 then
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
end
end
@@ -498,7 +498,6 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G
_MESSAGESRS.Gender = Gender or "female"
_MESSAGESRS.MSRS:SetGoogle(PathToCredentials)
_MESSAGESRS.google = PathToCredentials
_MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE")
_MESSAGESRS.label = Label or "MESSAGE"
@@ -512,8 +511,6 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G
if Voice then _MESSAGESRS.MSRS:SetVoice(Voice) end
_MESSAGESRS.voice = Voice --or MSRS.Voices.Microsoft.Hedda
--if _MESSAGESRS.google and not Voice then _MESSAGESRS.Voice = MSRS.Voices.Google.Standard.en_GB_Standard_A end
--_MESSAGESRS.MSRS:SetVoice(Voice or _MESSAGESRS.voice)
_MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE")
end

View File

@@ -24,8 +24,9 @@
do -- COORDINATE
--- @type COORDINATE
---
-- @type COORDINATE
-- @field #string ClassName Name of the class
-- @field #number x Component of the 3D vector.
-- @field #number y Component of the 3D vector.
@@ -2472,15 +2473,18 @@ do -- COORDINATE
-- Write command as string and execute that. Idea by Grimes https://forum.dcs.world/topic/324201-mark-to-all-function/#comment-5273793
local s=string.format("trigger.action.markupToAll(7, %d, %d,", Coalition, MarkID)
for _,vec in pairs(vecs) do
s=s..string.format("%s,", UTILS._OneLineSerialize(vec))
--s=s..string.format("%s,", UTILS._OneLineSerialize(vec))
s=s..string.format("{x=%.1f, y=%.1f, z=%.1f},", vec.x, vec.y, vec.z)
end
s=s..string.format("%s, %s, %s, %s", UTILS._OneLineSerialize(Color), UTILS._OneLineSerialize(FillColor), tostring(LineType), tostring(ReadOnly))
if Text and Text~="" then
s=s..string.format(", \"%s\"", Text)
s=s..string.format("{%.3f, %.3f, %.3f, %.3f},", Color[1], Color[2], Color[3], Color[4])
s=s..string.format("{%.3f, %.3f, %.3f, %.3f},", FillColor[1], FillColor[2], FillColor[3], FillColor[4])
s=s..string.format("%d,", LineType or 1)
s=s..string.format("%s", tostring(ReadOnly))
if Text and type(Text)=="string" and string.len(Text)>0 then
s=s..string.format(", \"%s\"", tostring(Text))
end
s=s..")"
-- Execute string command
local success=UTILS.DoString(s)
@@ -2568,7 +2572,7 @@ do -- COORDINATE
Offset=Offset or 2
-- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each Coordinate.
-- Measurement of visibility should not be from the ground, so Adding a hypothetical 2 meters to each Coordinate.
local FromVec3 = self:GetVec3()
FromVec3.y = FromVec3.y + Offset
@@ -2969,10 +2973,10 @@ do -- COORDINATE
end
-- corrected Track to be direction of travel of bogey (self in this case)
local track = "Maneuver"
if self.Heading then
track = UTILS.BearingToCardinal(self.Heading) or "North"
local track = "Maneuver"
if self.Heading then
track = UTILS.BearingToCardinal(self.Heading) or "North"
end
if rangeNM > 3 then
@@ -3117,6 +3121,49 @@ do -- COORDINATE
local MGRS = coord.LLtoMGRS( lat, lon )
return "MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy )
end
--- Provides a COORDINATE from an MGRS String
-- @param #COORDINATE self
-- @param #string MGRSString MGRS String, e.g. "MGRS 37T DK 12345 12345"
-- @return #COORDINATE self
function COORDINATE:NewFromMGRSString( MGRSString )
local myparts = UTILS.Split(MGRSString," ")
local northing = tostring(myparts[5]) or ""
local easting = tostring(myparts[4]) or ""
if string.len(easting) < 5 then easting = easting..string.rep("0",5-string.len(easting)) end
if string.len(northing) < 5 then northing = northing..string.rep("0",5-string.len(northing)) end
local MGRS = {
UTMZone = myparts[2],
MGRSDigraph = myparts[3],
Easting = easting,
Northing = northing,
}
local lat, lon = coord.MGRStoLL(MGRS)
local point = coord.LLtoLO(lat, lon, 0)
local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z})
return coord
end
--- Provides a COORDINATE from an MGRS Coordinate
-- @param #COORDINATE self
-- @param #string UTMZone UTM Zone, e.g. "37T"
-- @param #string MGRSDigraph Digraph, e.g. "DK"
-- @param #string Easting Meters easting - string in order to allow for leading zeros, e.g. "01234". Should be 5 digits.
-- @param #string Northing Meters northing - string in order to allow for leading zeros, e.g. "12340". Should be 5 digits.
-- @return #COORDINATE self
function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing )
if string.len(Easting) < 5 then Easting = Easting..string.rep("0",5-string.len(Easting) )end
if string.len(Northing) < 5 then Northing = Northing..string.rep("0",5-string.len(Northing) )end
local MGRS = {
UTMZone = UTMZone,
MGRSDigraph = MGRSDigraph,
Easting = Easting,
Northing = Northing,
}
local lat, lon = coord.MGRStoLL(MGRS)
local point = coord.LLtoLO(lat, lon, 0)
local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z})
end
--- Provides a coordinate string of the point, based on a coordinate format system:
-- * Uses default settings in COORDINATE.
@@ -3630,7 +3677,7 @@ end
do -- POINT_VEC2
--- @type POINT_VEC2
-- @type POINT_VEC2
-- @field DCS#Distance x The x coordinate in meters.
-- @field DCS#Distance y the y coordinate in meters.
-- @extends Core.Point#COORDINATE

View File

@@ -146,7 +146,45 @@ do -- SET_BASE
return self
end
--- [Internal] Add a functional filter
-- @param #SET_BASE self
-- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a CONTROLLABLE object as first argument.
-- @param ... Condition function arguments, if any.
-- @return #boolean If true, at least one condition is true
function SET_BASE:FilterFunction(ConditionFunction, ...)
local condition={}
condition.func=ConditionFunction
condition.arg={}
if arg then
condition.arg=arg
end
if not self.Filter.Functions then self.Filter.Functions = {} end
table.insert(self.Filter.Functions, condition)
return self
end
--- [Internal] Check if the condition functions returns true.
-- @param #SET_BASE self
-- @param Wrapper.Controllable#CONTROLLABLE Object The object to filter for
-- @return #boolean If true, if **all** conditions are true
function SET_BASE:_EvalFilterFunctions(Object)
-- All conditions must be true.
for _,_condition in pairs(self.Filter.Functions or {}) do
local condition=_condition
-- Call function.
if condition.func(Object,unpack(condition.arg)) == false then
return false
end
end
-- No condition was true.
return true
end
--- Clear the Objects in the Set.
-- @param #SET_BASE self
-- @param #boolean TriggerEvent If `true`, an event remove is triggered for each group that is removed from the set.
@@ -419,7 +457,11 @@ do -- SET_BASE
-- @param #SET_BASE self
-- @return Core.Base#BASE
function SET_BASE:GetRandom()
local tablemax = table.maxn(self.Index)
local tablemax = 0
for _,_ind in pairs(self.Index) do
tablemax = tablemax + 1
end
--local tablemax = table.maxn(self.Index)
local RandomItem = self.Set[self.Index[math.random(1,tablemax)]]
self:T3( { RandomItem } )
return RandomItem
@@ -561,10 +603,12 @@ do -- SET_BASE
return self
end
--- Iterate the SET_BASE while identifying the nearest object from a @{Core.Point#POINT_VEC2}.
--- Iterate the SET_BASE while identifying the nearest object in the set from a @{Core.Point#POINT_VEC2}.
-- @param #SET_BASE self
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set.
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#COORDINATE} or @{Core.Point#POINT_VEC2} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set.
-- @return Core.Base#BASE The closest object.
-- @usage
-- myset:FindNearestObjectFromPointVec2( ZONE:New("Test Zone"):GetCoordinate() )
function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 )
self:F2( PointVec2 )
@@ -961,6 +1005,7 @@ do
-- * @{#SET_GROUP.FilterCategoryShip}: Builds the SET_GROUP from ships.
-- * @{#SET_GROUP.FilterCategoryStructure}: Builds the SET_GROUP from structures.
-- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Core.Zone#ZONE}.
-- * @{#SET_GROUP.FilterFunction}: Builds the SET_GROUP with a custom condition.
--
-- Once the filter criteria have been set for the SET_GROUP, you can start filtering using:
--
@@ -1034,6 +1079,7 @@ do
Countries = nil,
GroupPrefixes = nil,
Zones = nil,
Functions = nil,
},
FilterMeta = {
Coalitions = {
@@ -1247,7 +1293,26 @@ do
return self
end
--- [User] Add a custom condition function.
-- @function [parent=#SET_GROUP] FilterFunction
-- @param #SET_GROUP self
-- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a GROUP object as first argument.
-- @param ... Condition function arguments if any.
-- @return #SET_GROUP self
-- @usage
-- -- Image you want to exclude a specific GROUP from a SET:
-- local groundset = SET_GROUP:New():FilterCoalitions("blue"):FilterCategoryGround():FilterFunction(
-- -- The function needs to take a GROUP object as first - and in this case, only - argument.
-- function(grp)
-- local isinclude = true
-- if grp:GetName() == "Exclude Me" then isinclude = false end
-- return isinclude
-- end
-- ):FilterOnce()
-- BASE:I(groundset:Flush())
--- Builds a set of groups of coalitions.
-- Possible current coalitions are red, blue and neutral.
-- @param #SET_GROUP self
@@ -1921,7 +1986,7 @@ do
MGroupInclude = MGroupInclude and MGroupActive
end
if self.Filter.Coalitions then
if self.Filter.Coalitions and MGroupInclude then
local MGroupCoalition = false
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
@@ -1932,7 +1997,7 @@ do
MGroupInclude = MGroupInclude and MGroupCoalition
end
if self.Filter.Categories then
if self.Filter.Categories and MGroupInclude then
local MGroupCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
self:T3( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } )
@@ -1943,7 +2008,7 @@ do
MGroupInclude = MGroupInclude and MGroupCategory
end
if self.Filter.Countries then
if self.Filter.Countries and MGroupInclude then
local MGroupCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
self:T3( { "Country:", MGroup:GetCountry(), CountryName } )
@@ -1954,7 +2019,7 @@ do
MGroupInclude = MGroupInclude and MGroupCountry
end
if self.Filter.GroupPrefixes then
if self.Filter.GroupPrefixes and MGroupInclude then
local MGroupPrefix = false
for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do
self:T3( { "Prefix:", string.find( MGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } )
@@ -1965,7 +2030,7 @@ do
MGroupInclude = MGroupInclude and MGroupPrefix
end
if self.Filter.Zones then
if self.Filter.Zones and MGroupInclude then
local MGroupZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
--self:T( "Zone:", ZoneName )
@@ -1975,6 +2040,12 @@ do
end
MGroupInclude = MGroupInclude and MGroupZone
end
if self.Filter.Functions and MGroupInclude then
local MGroupFunc = false
MGroupFunc = self:_EvalFilterFunctions(MGroup)
MGroupInclude = MGroupInclude and MGroupFunc
end
self:T2( MGroupInclude )
return MGroupInclude
@@ -2074,6 +2145,7 @@ do -- SET_UNIT
-- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching)
-- * @{#SET_UNIT.FilterActive}: Builds the SET_UNIT with the units that are only active. Units that are inactive (late activation) won't be included in the set!
-- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Core.Zone#ZONE}.
-- * @{#SET_UNIT.FilterFunction}: Builds the SET_UNIT with a custom condition.
--
-- Once the filter criteria have been set for the SET_UNIT, you can start filtering using:
--
@@ -2152,6 +2224,7 @@ do -- SET_UNIT
Countries = nil,
UnitPrefixes = nil,
Zones = nil,
Functions = nil,
},
FilterMeta = {
Coalitions = {
@@ -2523,6 +2596,25 @@ do -- SET_UNIT
return self
end
--- [User] Add a custom condition function.
-- @function [parent=#SET_UNIT] FilterFunction
-- @param #SET_UNIT self
-- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a UNIT object as first argument.
-- @param ... Condition function arguments if any.
-- @return #SET_UNIT self
-- @usage
-- -- Image you want to exclude a specific UNIT from a SET:
-- local groundset = SET_UNIT:New():FilterCoalitions("blue"):FilterCategories("ground"):FilterFunction(
-- -- The function needs to take a UNIT object as first - and in this case, only - argument.
-- function(unit)
-- local isinclude = true
-- if unit:GetName() == "Exclude Me" then isinclude = false end
-- return isinclude
-- end
-- ):FilterOnce()
-- BASE:I(groundset:Flush())
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
-- @param #SET_UNIT self
@@ -2848,59 +2940,50 @@ do -- SET_UNIT
-- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units.
function SET_UNIT:GetCoordinate()
local Coordinate = nil
local unit = self:GetRandom()
if self:Count() == 1 and unit then
return unit:GetCoordinate()
end
if unit then
local Coordinate = unit:GetCoordinate()
--self:F({Coordinate:GetVec3()})
local x1 = Coordinate.x
local x2 = Coordinate.x
local y1 = Coordinate.y
local y2 = Coordinate.y
local z1 = Coordinate.z
local z2 = Coordinate.z
local MaxVelocity = 0
local AvgHeading = nil
local MovingCount = 0
for UnitName, UnitData in pairs( self:GetAliveSet() ) do
local Unit = UnitData -- Wrapper.Unit#UNIT
local Coordinate = Unit:GetCoordinate()
x1 = (Coordinate.x < x1) and Coordinate.x or x1
x2 = (Coordinate.x > x2) and Coordinate.x or x2
y1 = (Coordinate.y < y1) and Coordinate.y or y1
y2 = (Coordinate.y > y2) and Coordinate.y or y2
z1 = (Coordinate.y < z1) and Coordinate.z or z1
z2 = (Coordinate.y > z2) and Coordinate.z or z2
local Velocity = Coordinate:GetVelocity()
if Velocity ~= 0 then
MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity
local Heading = Coordinate:GetHeading()
AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading
MovingCount = MovingCount + 1
local function GetSetVec3(units)
-- Init.
local x=0
local y=0
local z=0
local n=0
-- Loop over all units.
for _,unit in pairs(units) do
local vec3=nil --DCS#Vec3
if unit and unit:IsAlive() then
vec3 = unit:GetVec3()
end
if vec3 then
-- Sum up posits.
x=x+vec3.x
y=y+vec3.y
z=z+vec3.z
-- Increase counter.
n=n+1
end
end
AvgHeading = AvgHeading and (AvgHeading / MovingCount)
Coordinate.x = (x2 - x1) / 2 + x1
Coordinate.y = (y2 - y1) / 2 + y1
Coordinate.z = (z2 - z1) / 2 + z1
Coordinate:SetHeading( AvgHeading )
Coordinate:SetVelocity( MaxVelocity )
self:F( { Coordinate = Coordinate } )
if n>0 then
-- Average.
local Vec3={x=x/n, y=y/n, z=z/n} --DCS#Vec3
return Vec3
end
return nil
end
local Coordinate = nil
local Vec3 = GetSetVec3(self.Set)
if Vec3 then
Coordinate = COORDINATE:NewFromVec3(Vec3)
end
return Coordinate
if Coordinate then
local heading = self:GetHeading() or 0
local velocity = self:GetVelocity() or 0
Coordinate:SetHeading( heading )
Coordinate:SetVelocity( velocity )
self:I(UTILS.PrintTableToLog(Coordinate))
end
return Coordinate
end
--- Get the maximum velocity of the SET_UNIT.
@@ -3109,7 +3192,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitActive
end
if self.Filter.Coalitions then
if self.Filter.Coalitions and MUnitInclude then
local MUnitCoalition = false
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
self:F( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
@@ -3120,7 +3203,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitCoalition
end
if self.Filter.Categories then
if self.Filter.Categories and MUnitInclude then
local MUnitCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } )
@@ -3131,7 +3214,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitCategory
end
if self.Filter.Types then
if self.Filter.Types and MUnitInclude then
local MUnitType = false
for TypeID, TypeName in pairs( self.Filter.Types ) do
self:T3( { "Type:", MUnit:GetTypeName(), TypeName } )
@@ -3142,7 +3225,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitType
end
if self.Filter.Countries then
if self.Filter.Countries and MUnitInclude then
local MUnitCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
self:T3( { "Country:", MUnit:GetCountry(), CountryName } )
@@ -3153,7 +3236,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitCountry
end
if self.Filter.UnitPrefixes then
if self.Filter.UnitPrefixes and MUnitInclude then
local MUnitPrefix = false
for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do
self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } )
@@ -3164,7 +3247,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitPrefix
end
if self.Filter.RadarTypes then
if self.Filter.RadarTypes and MUnitInclude then
local MUnitRadar = false
for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do
self:T3( { "Radar:", RadarType } )
@@ -3178,7 +3261,7 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MUnitRadar
end
if self.Filter.SEAD then
if self.Filter.SEAD and MUnitInclude then
local MUnitSEAD = false
if MUnit:HasSEAD() == true then
self:T3( "SEAD Found" )
@@ -3188,7 +3271,7 @@ do -- SET_UNIT
end
end
if self.Filter.Zones then
if self.Filter.Zones and MUnitInclude then
local MGroupZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
self:T3( "Zone:", ZoneName )
@@ -3199,6 +3282,11 @@ do -- SET_UNIT
MUnitInclude = MUnitInclude and MGroupZone
end
if self.Filter.Functions and MUnitInclude then
local MUnitFunc = self:_EvalFilterFunctions(MUnit)
MUnitInclude = MUnitInclude and MUnitFunc
end
self:T2( MUnitInclude )
return MUnitInclude
end
@@ -3280,6 +3368,7 @@ do -- SET_STATIC
-- * @{#SET_STATIC.FilterPrefixes}: Builds the SET_STATIC with the units containing the same string(s) in their name. **Attention!** LUA regular expression apply here, so special characters in names like minus, dot, hash (#) etc might lead to unexpected results.
-- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching)
-- * @{#SET_STATIC.FilterZones}: Builds the SET_STATIC with the units within a @{Core.Zone#ZONE}.
-- * @{#SET_STATIC.FilterFunction}: Builds the SET_STATIC with a custom condition.
--
-- Once the filter criteria have been set for the SET_STATIC, you can start filtering using:
--
@@ -3482,7 +3571,25 @@ do -- SET_STATIC
end
return self
end
--- [User] Add a custom condition function.
-- @function [parent=#SET_STATIC] FilterFunction
-- @param #SET_STATIC self
-- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a STATIC object as first argument.
-- @param ... Condition function arguments if any.
-- @return #SET_STATIC self
-- @usage
-- -- Image you want to exclude a specific CLIENT from a SET:
-- local groundset = SET_STATIC:New():FilterCoalitions("blue"):FilterActive(true):FilterFunction(
-- -- The function needs to take a STATIC object as first - and in this case, only - argument.
-- function(static)
-- local isinclude = true
-- if static:GetName() == "Exclude Me" then isinclude = false end
-- return isinclude
-- end
-- ):FilterOnce()
-- BASE:I(groundset:Flush())
--- Builds a set of units of defined countries.
-- Possible current countries are those known within DCS world.
-- @param #SET_STATIC self
@@ -4039,6 +4146,7 @@ do -- SET_CLIENT
-- Have a read through here to understand the application of regular expressions: [LUA regular expressions](https://riptutorial.com/lua/example/20315/lua-pattern-matching)
-- * @{#SET_CLIENT.FilterActive}: Builds the SET_CLIENT with the units that are only active. Units that are inactive (late activation) won't be included in the set!
-- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}.
-- * @{#SET_CLIENT.FilterFunction}: Builds the SET_CLIENT with a custom condition.
--
-- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using:
--
@@ -4541,6 +4649,25 @@ do -- SET_CLIENT
return AliveSet.Set or {}
end
--- [User] Add a custom condition function.
-- @function [parent=#SET_CLIENT] FilterFunction
-- @param #SET_CLIENT self
-- @param #function ConditionFunction If this function returns `true`, the object is added to the SET. The function needs to take a CLIENT object as first argument.
-- @param ... Condition function arguments if any.
-- @return #SET_CLIENT self
-- @usage
-- -- Image you want to exclude a specific CLIENT from a SET:
-- local groundset = SET_CLIENT:New():FilterCoalitions("blue"):FilterActive(true):FilterFunction(
-- -- The function needs to take a UNIT object as first - and in this case, only - argument.
-- function(client)
-- local isinclude = true
-- if client:GetPlayerName() == "Exclude Me" then isinclude = false end
-- return isinclude
-- end
-- ):FilterOnce()
-- BASE:I(groundset:Flush())
---
-- @param #SET_CLIENT self
-- @param Wrapper.Client#CLIENT MClient
@@ -4562,7 +4689,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientActive
end
if self.Filter.Coalitions then
if self.Filter.Coalitions and MClientInclude then
local MClientCoalition = false
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName )
@@ -4575,7 +4702,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientCoalition
end
if self.Filter.Categories then
if self.Filter.Categories and MClientInclude then
local MClientCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName )
@@ -4588,7 +4715,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientCategory
end
if self.Filter.Types then
if self.Filter.Types and MClientInclude then
local MClientType = false
for TypeID, TypeName in pairs( self.Filter.Types ) do
self:T3( { "Type:", MClient:GetTypeName(), TypeName } )
@@ -4600,7 +4727,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientType
end
if self.Filter.Countries then
if self.Filter.Countries and MClientInclude then
local MClientCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName )
@@ -4613,7 +4740,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientCountry
end
if self.Filter.ClientPrefixes then
if self.Filter.ClientPrefixes and MClientInclude then
local MClientPrefix = false
for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do
self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } )
@@ -4625,7 +4752,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientPrefix
end
if self.Filter.Zones then
if self.Filter.Zones and MClientInclude then
local MClientZone = false
for ZoneName, Zone in pairs( self.Filter.Zones ) do
self:T3( "Zone:", ZoneName )
@@ -4637,7 +4764,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientZone
end
if self.Filter.Playernames then
if self.Filter.Playernames and MClientInclude then
local MClientPlayername = false
local playername = MClient:GetPlayerName() or "Unknown"
--self:T(playername)
@@ -4650,7 +4777,7 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientPlayername
end
if self.Filter.Callsigns then
if self.Filter.Callsigns and MClientInclude then
local MClientCallsigns = false
local callsign = MClient:GetCallsign()
--self:I(callsign)
@@ -4663,6 +4790,11 @@ do -- SET_CLIENT
MClientInclude = MClientInclude and MClientCallsigns
end
if self.Filter.Functions and MClientInclude then
local MClientFunc = self:_EvalFilterFunctions(MClient)
MClientInclude = MClientInclude and MClientFunc
end
end
self:T2( MClientInclude )
return MClientInclude
@@ -5256,7 +5388,7 @@ do -- SET_AIRBASE
function SET_AIRBASE:GetRandomAirbase()
local RandomAirbase = self:GetRandom()
self:F( { RandomAirbase = RandomAirbase:GetName() } )
--self:F( { RandomAirbase = RandomAirbase:GetName() } )
return RandomAirbase
end
@@ -5422,7 +5554,7 @@ do -- SET_AIRBASE
MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition
end
if self.Filter.Categories then
if self.Filter.Categories and MAirbaseInclude then
local MAirbaseCategory = false
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName )
@@ -7768,7 +7900,7 @@ do -- SET_OPSGROUP
end
-- Filter coalitions.
if self.Filter.Coalitions then
if self.Filter.Coalitions and MGroupInclude then
local MGroupCoalition = false
@@ -7782,7 +7914,7 @@ do -- SET_OPSGROUP
end
-- Filter categories.
if self.Filter.Categories then
if self.Filter.Categories and MGroupInclude then
local MGroupCategory = false
@@ -7796,7 +7928,7 @@ do -- SET_OPSGROUP
end
-- Filter countries.
if self.Filter.Countries then
if self.Filter.Countries and MGroupInclude then
local MGroupCountry = false
for CountryID, CountryName in pairs( self.Filter.Countries ) do
if country.id[CountryName] == MGroup:GetCountry() then
@@ -7807,12 +7939,12 @@ do -- SET_OPSGROUP
end
-- Filter "prefixes".
if self.Filter.GroupPrefixes then
if self.Filter.GroupPrefixes and MGroupInclude then
local MGroupPrefix = false
for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do
if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then --Not sure why "-" is replaced by "%-" ?!
if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then --Not sure why "-" is replaced by "%-" ?! - So we can still match group names with a dash in them
MGroupPrefix = true
end
end

View File

@@ -1108,6 +1108,22 @@ function SPAWN:InitRandomizeCallsign()
return self
end
--- [BLUE AIR only!] This method sets a specific callsign for a spawned group. Use for a group with one unit only!
-- @param #SPAWN self
-- @param #number ID ID of the callsign enumerator, e.g. CALLSIGN.Tanker.Texaco - - resulting in e.g. Texaco-2-1
-- @param #string Name Name of this callsign as it cannot be determined from the ID because of the dependency on the task type of the plane, and the plane type. E.g. "Texaco"
-- @param #number Minor Minor number, i.e. the unit number within the group, e.g 2 - resulting in e.g. Texaco-2-1
-- @param #number Major Major number, i.e. the group number of this name, e.g. 1 - resulting in e.g. Texaco-2-1
-- @return #SPAWN self
function SPAWN:InitCallSign(ID,Name,Minor,Major)
self.SpawnInitCallSign = true
self.SpawnInitCallSignID = ID or 1
self.SpawnInitCallSignMinor = Minor or 1
self.SpawnInitCallSignMajor = Major or 1
self.SpawnInitCallSignName = string.lower(Name) or "enfield"
return self
end
--- This method sets a spawn position for the group that is different from the location of the template.
-- @param #SPAWN self
-- @param Core.Point#COORDINATE Coordinate The position to spawn from
@@ -3331,10 +3347,23 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
end
end
if self.SpawnInitCallSign then
for UnitID = 1, #SpawnTemplate.units do
local Callsign = SpawnTemplate.units[UnitID].callsign
if Callsign and type( Callsign ) ~= "number" then
SpawnTemplate.units[UnitID].callsign[1] = self.SpawnInitCallSignID
SpawnTemplate.units[UnitID].callsign[2] = self.SpawnInitCallSignMinor
SpawnTemplate.units[UnitID].callsign[3] = self.SpawnInitCallSignMajor
SpawnTemplate.units[UnitID].callsign["name"] = string.format("%s%d%d",self.SpawnInitCallSignName,self.SpawnInitCallSignMinor,self.SpawnInitCallSignMajor)
--UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1)
end
end
end
for UnitID = 1, #SpawnTemplate.units do
local Callsign = SpawnTemplate.units[UnitID].callsign
if Callsign then
if type( Callsign ) ~= "number" then -- blue callsign
if type( Callsign ) ~= "number" and not self.SpawnInitCallSign then -- blue callsign
-- UTILS.PrintTableToLog(Callsign,1)
Callsign[2] = ((SpawnIndex - 1) % 10) + 1
local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string
@@ -3342,7 +3371,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
local CallsignLen = CallsignName:len()
SpawnTemplate.units[UnitID].callsign[2] = UnitID
SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3]
else
elseif type( Callsign ) == "number" then
SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex
end
end
@@ -3377,11 +3406,11 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
end
end
-- VoiceCallsignNumber
if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber then
if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber and type( Callsign ) ~= "number" then
SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3]
end
-- VoiceCallsignLabel
if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel then
if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel and type( Callsign ) ~= "number" then
local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string
CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers
local label = "NY" -- Navy One exception

File diff suppressed because it is too large Load Diff

View File

@@ -606,8 +606,10 @@ function AICSAR:SetPilotTTSVoice(Voice,Culture,Gender)
self.SRSPilot:SetCulture(Culture or "en-US")
self.SRSPilot:SetGender(Gender or "male")
self.SRSPilot:SetLabel("PILOT")
if self.SRS.google then
self.SRSPilot:SetGoogle(self.SRS.google)
if self.SRSGoogle then
local poptions = self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -- Sound.SRS#MSRS.ProviderOptions
self.SRSPilot:SetGoogle(poptions.credentials)
self.SRSPilot:SetGoogleAPIKey(poptions.key)
end
return self
end
@@ -627,9 +629,11 @@ function AICSAR:SetOperatorTTSVoice(Voice,Culture,Gender)
self.SRSOperator:SetVoice(Voice)
self.SRSOperator:SetCulture(Culture or "en-GB")
self.SRSOperator:SetGender(Gender or "female")
self.SRSPilot:SetLabel("RESCUE")
if self.SRS.google then
self.SRSOperator:SetGoogle(self.SRS.google)
self.SRSOperator:SetLabel("RESCUE")
if self.SRSGoogle then
local poptions = self.SRS:GetProviderOptions(MSRS.Provider.GOOGLE) -- Sound.SRS#MSRS.ProviderOptions
self.SRSOperator:SetGoogle(poptions.credentials)
self.SRSOperator:SetGoogleAPIKey(poptions.key)
end
return self
end

View File

@@ -10,9 +10,7 @@
--
-- ===
--
-- ## Missions:
--
-- [ABP - Airbase Police](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/ABP%20-%20Airbase%20Police)
-- ## Missions: None
--
-- ===
--
@@ -699,7 +697,8 @@ end
function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
self:I("_AirbaseMonitor")
self.SetClient:ForEachClient(
--- @param Wrapper.Client#CLIENT Client
--- Nameless function
-- @param Wrapper.Client#CLIENT Client
function( Client )
if Client:IsAlive() then

View File

@@ -38,8 +38,9 @@
-- @image Detection.JPG
do -- DETECTION_BASE
--- @type DETECTION_BASE
---
-- @type DETECTION_BASE
-- @field Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
-- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected.
-- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects.
@@ -91,6 +92,11 @@ do -- DETECTION_BASE
--
-- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
--
--
-- ## Radar Blur - use to make the radar less exact, e.g. for WWII scenarios
--
-- * @{#DETECTION_BASE.SetRadarBlur}(): Set the radar blur to be used.
--
-- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list
--
-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later
@@ -268,11 +274,13 @@ do -- DETECTION_BASE
DetectedItems = {},
DetectedItemsByIndex = {},
}
--- @type DETECTION_BASE.DetectedObjects
---
-- @type DETECTION_BASE.DetectedObjects
-- @list <#DETECTION_BASE.DetectedObject>
--- @type DETECTION_BASE.DetectedObject
---
-- @type DETECTION_BASE.DetectedObject
-- @field #string Name
-- @field #boolean IsVisible
-- @field #boolean KnowType
@@ -283,8 +291,9 @@ do -- DETECTION_BASE
-- @field #number LastTime
-- @field #boolean LastPos
-- @field #number LastVelocity
--- @type DETECTION_BASE.DetectedItems
---
-- @type DETECTION_BASE.DetectedItems
-- @list <#DETECTION_BASE.DetectedItem>
--- Detected item data structure.
@@ -522,7 +531,7 @@ do -- DETECTION_BASE
do -- State Transition Handling
--- @param #DETECTION_BASE self
-- @param #DETECTION_BASE self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -530,7 +539,7 @@ do -- DETECTION_BASE
self:__Detect( 1 )
end
--- @param #DETECTION_BASE self
-- @param #DETECTION_BASE self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -570,7 +579,7 @@ do -- DETECTION_BASE
end
--- @param #DETECTION_BASE self
-- @param #DETECTION_BASE self
-- @param #number The amount of alive recce.
function DETECTION_BASE:CountAliveRecce()
@@ -578,7 +587,7 @@ do -- DETECTION_BASE
end
--- @param #DETECTION_BASE self
-- @param #DETECTION_BASE self
function DETECTION_BASE:ForEachAliveRecce( IteratorFunction, ... )
self:F2( arg )
@@ -587,7 +596,7 @@ do -- DETECTION_BASE
return self
end
--- @param #DETECTION_BASE self
-- @param #DETECTION_BASE self
-- @param #string From The From State string.
-- @param #string Event The Event string.
-- @param #string To The To State string.
@@ -712,6 +721,31 @@ do -- DETECTION_BASE
end
end
-- Calculate radar blur probability
if self.RadarBlur then
MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose)
local minheight = self.RadarBlurMinHeight or 250 -- meters
local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group
local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall
local dist = math.floor(Distance)
if dist <= self.RadarBlurClosing then
thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight)
thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur)
end
local fheight = math.floor(math.random(1,10000)/100)
local fblur = math.floor(math.random(1,10000)/100)
local unit = UNIT:FindByName(DetectedObjectName)
if unit and unit:IsAlive() then
local AGL = unit:GetAltitude(true)
MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m. Distance "..math.floor(Distance).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose)
MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose)
if fblur > thresblur then DetectionAccepted = false end
if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end
MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose)
end
end
-- Calculate additional probabilities
if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.DistanceProbability then
@@ -1011,7 +1045,24 @@ do -- DETECTION_BASE
return self
end
--- Method to make the radar detection less accurate, e.g. for WWII scenarios.
-- @param #DETECTION_BASE self
-- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground)
-- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance)
-- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found)
-- @param #number closing Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20.
-- @return #DETECTION_BASE self
function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur,closing)
self.RadarBlur = true
self.RadarBlurMinHeight = minheight or 250 -- meters
self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group
self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall
self.RadarBlurClosing = closing or 20 -- 20km
self.RadarBlurClosingSquare = self.RadarBlurClosing * self.RadarBlurClosing
return self
end
end
do
@@ -1354,7 +1405,7 @@ do -- DETECTION_BASE
}
}
--- @param DCS#Unit FoundDCSUnit
-- @param DCS#Unit FoundDCSUnit
-- @param Wrapper.Group#GROUP ReportGroup
-- @param Core.Set#SET_GROUP ReportSetGroup
local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData )
@@ -1419,7 +1470,7 @@ do -- DETECTION_BASE
DetectedItem.PlayersNearBy = nil
_DATABASE:ForEachPlayer(
--- @param Wrapper.Unit#UNIT PlayerUnit
-- @param Wrapper.Unit#UNIT PlayerUnit
function( PlayerUnitName )
local PlayerUnit = UNIT:FindByName( PlayerUnitName )
@@ -1975,8 +2026,9 @@ do -- DETECTION_BASE
end
do -- DETECTION_UNITS
--- @type DETECTION_UNITS
---
-- @type DETECTION_UNITS
-- @field DCS#Distance DetectionRange The range till which targets are detected.
-- @extends Functional.Detection#DETECTION_BASE
@@ -2231,8 +2283,9 @@ do -- DETECTION_UNITS
end
do -- DETECTION_TYPES
--- @type DETECTION_TYPES
---
-- @type DETECTION_TYPES
-- @extends Functional.Detection#DETECTION_BASE
--- Will detect units within the battle zone.
@@ -2434,8 +2487,9 @@ do -- DETECTION_TYPES
end
do -- DETECTION_AREAS
--- @type DETECTION_AREAS
---
-- @type DETECTION_AREAS
-- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target.
-- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange.
-- @extends Functional.Detection#DETECTION_BASE
@@ -2961,7 +3015,7 @@ do -- DETECTION_AREAS
-- DetectedSet:Flush( self )
DetectedSet:ForEachUnit( --- @param Wrapper.Unit#UNIT DetectedUnit
DetectedSet:ForEachUnit( -- @param Wrapper.Unit#UNIT DetectedUnit
function( DetectedUnit )
if DetectedUnit:IsAlive() then
-- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() )

View File

@@ -22,7 +22,7 @@
-- @module Functional.Mantis
-- @image Functional.Mantis.jpg
--
-- Last Update: Nov 2023
-- Last Update: Dec 2023
-------------------------------------------------------------------------
--- **MANTIS** class, extends Core.Base#BASE
@@ -94,7 +94,7 @@
-- Known SAM types at the time of writing are:
--
-- * Avenger
-- * Chaparrel
-- * Chaparral
-- * Hawk
-- * Linebacker
-- * NASAMS
@@ -365,7 +365,7 @@ MANTIS.SamData = {
["SA-15"] = { Range=11, Blindspot=0, Height=6, Type="Short", Radar="Tor 9A331" },
["SA-13"] = { Range=5, Blindspot=0, Height=3, Type="Short", Radar="Strela" },
["Avenger"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Avenger" },
["Chaparrel"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
["Chaparral"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Linebacker" },
["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" },
-- units from HDS Mod, multi launcher options is tricky
@@ -631,7 +631,7 @@ do
-- TODO Version
-- @field #string version
self.version="0.8.15"
self.version="0.8.16"
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
--- FSM Functions ---
@@ -1149,7 +1149,7 @@ do
--self:T(self.lid.." Relocating HQ")
local text = self.lid.." Relocating HQ"
--local m= MESSAGE:New(text,10,"MANTIS"):ToAll()
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true)
_hqgrp:RelocateGroundRandomInRadius(20,500,true,true,nil,true)
end
--relocate EWR
-- TODO: maybe dependent on AlarmState? Observed: SA11 SR only relocates if no objects in reach
@@ -1163,7 +1163,7 @@ do
local text = self.lid.." Relocating EWR ".._grp:GetName()
local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
if self.verbose then self:I(text) end
_grp:RelocateGroundRandomInRadius(20,500,true,true)
_grp:RelocateGroundRandomInRadius(20,500,true,true,nil,true)
end
end
end

View File

@@ -170,7 +170,7 @@
--
-- * A specific departure and/or destination airport can be chosen.
-- * Valid coalitions can be set, e.g. only red, blue or neutral, all three "colours".
-- * It is possible to start in air within a zone defined in the mission editor or within a zone above an airport of the map.
-- * It is possible to start in air within a zone or within a zone above an airport of the map.
--
-- ## Flight Plan
--
@@ -1179,13 +1179,13 @@ function RAT:SetTakeoffAir()
return self
end
--- Set possible departure ports. This can be an airport or a zone defined in the mission editor.
--- Set possible departure ports. This can be an airport or a zone.
-- @param #RAT self
-- @param #string departurenames Name or table of names of departure airports or zones.
-- @return #RAT RAT self object.
-- @usage RAT:SetDeparture("Sochi-Adler") will spawn RAT objects at Sochi-Adler airport.
-- @usage RAT:SetDeparture({"Sochi-Adler", "Gudauta"}) will spawn RAT aircraft radomly at Sochi-Adler or Gudauta airport.
-- @usage RAT:SetDeparture({"Zone A", "Gudauta"}) will spawn RAT aircraft in air randomly within Zone A, which has to be defined in the mission editor, or within a zone around Gudauta airport. Note that this also requires RAT:takeoff("air") to be set.
-- @usage RAT:SetDeparture({"Zone A", "Gudauta"}) will spawn RAT aircraft in air randomly within Zone A, or within a zone around Gudauta airport. Note that this also requires RAT:takeoff("air") to be set.
function RAT:SetDeparture(departurenames)
self:F2(departurenames)
@@ -2537,7 +2537,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint)
end
elseif self:_ZoneExists(_departure) then
-- If it's not an airport, check whether it's a zone.
departure=ZONE:New(_departure)
departure=ZONE:FindByName(_departure)
else
local text=string.format("ERROR! Specified departure airport %s does not exist for %s.", _departure, self.alias)
self:E(RAT.id..text)
@@ -2635,7 +2635,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint)
end
elseif self:_ZoneExists(_destination) then
destination=ZONE:New(_destination)
destination=ZONE:FindByName(_destination)
else
local text=string.format("ERROR: Specified destination airport/zone %s does not exist for %s!", _destination, self.alias)
self:E(RAT.id.."ERROR: "..text)
@@ -3142,7 +3142,7 @@ function RAT:_PickDeparture(takeoff)
end
elseif self:_ZoneExists(name) then
if takeoff==RAT.wp.air then
dep=ZONE:New(name)
dep=ZONE:FindByName(name)
else
self:E(RAT.id..string.format("ERROR! Takeoff is not in air. Cannot use %s as departure.", name))
end
@@ -3254,7 +3254,7 @@ function RAT:_PickDestination(departure, q, minrange, maxrange, random, landing)
end
elseif self:_ZoneExists(name) then
if landing==RAT.wp.air then
dest=ZONE:New(name)
dest=ZONE:FindByName(name)
else
self:E(RAT.id..string.format("ERROR! Landing is not in air. Cannot use zone %s as destination!", name))
end
@@ -4930,12 +4930,12 @@ function RAT:_AirportExists(name)
return false
end
--- Test if a trigger zone defined in the mission editor exists.
--- Test if a zone exists.
-- @param #RAT self
-- @param #string name
-- @return #boolean True if zone exsits, false otherwise.
function RAT:_ZoneExists(name)
local z=trigger.misc.getZone(name)
local z=ZONE:FindByName(name) --trigger.misc.getZone(name) as suggested by @Viking on MOOSE discord #rat
if z then
return true
end

View File

@@ -1207,18 +1207,18 @@ end
-- @return #RANGE self
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
if PathToSRS then
if PathToSRS or MSRS.path then
self.useSRS=true
self.controlmsrs=MSRS:New(PathToSRS, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0)
self.controlmsrs:SetPort(Port)
self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0)
self.controlmsrs:SetPort(Port or MSRS.port)
self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
self.controlmsrs:SetLabel("RANGEC")
self.controlsrsQ = MSRSQUEUE:New("CONTROL")
self.instructmsrs=MSRS:New(PathToSRS, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0)
self.instructmsrs:SetPort(Port)
self.instructmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0)
self.instructmsrs:SetPort(Port or MSRS.port)
self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE)
self.instructmsrs:SetLabel("RANGEI")
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
@@ -1737,7 +1737,9 @@ end
-- @param Core.Event#EVENTDATA EventData
function RANGE:OnEventBirth( EventData )
self:F( { eventbirth = EventData } )
if not EventData.IniPlayerName then return end
local _unitName = EventData.IniUnitName
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
@@ -2187,7 +2189,7 @@ function RANGE:onafterExitRange( From, Event, To, player )
local text = "You left the bombing range zone. "
local r=math.random(2)
local r=math.random(5)
if r==1 then
text=text.."Have a nice day!"

View File

@@ -17,9 +17,9 @@
--
-- ===
--
-- ### Authors: **FlightControl**, **applevangelist**
-- ### Authors: **applevangelist**, **FlightControl**
--
-- Last Update: Oct 2023
-- Last Update: Dec 2023
--
-- ===
--
@@ -144,7 +144,7 @@ function SEAD:New( SEADGroupPrefixes, Padding )
self:AddTransition("*", "ManageEvasion", "*")
self:AddTransition("*", "CalculateHitZone", "*")
self:I("*** SEAD - Started Version 0.4.5")
self:I("*** SEAD - Started Version 0.4.6")
return self
end
@@ -401,7 +401,7 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP
grp:EnableEmission(false)
end
grp:OptionAlarmStateGreen() -- needed else we cannot move around
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond")
grp:RelocateGroundRandomInRadius(20,300,false,false,"Diamond",true)
if self.UseCallBack then
local object = self.CallBack
object:SeadSuppressionStart(grp,name,attacker)

View File

@@ -0,0 +1,590 @@
--- **Functional** - TIRESIAS - manages AI behaviour.
--
-- ===
--
-- The @{#TIRESIAS} class is working in the back to keep your large-scale ground units in check.
--
-- ## Features:
--
-- * Designed to keep CPU and Network usage lower on missions with a lot of ground units.
-- * Does not affect ships to keep the Navy guys happy.
-- * Does not affect OpsGroup type groups.
-- * Distinguishes between SAM groups, AAA groups and other ground groups.
-- * Exceptions can be defined to keep certain actions going.
-- * Works coalition-independent in the back
-- * Easy setup.
--
-- ===
--
-- ## Missions:
--
-- ### [TIRESIAS](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master)
--
-- ===
--
-- ### Author : **applevangelist **
--
-- @module Functional.Tiresias
-- @image Functional.Tiresias.jpg
--
-- Last Update: Dec 2023
-------------------------------------------------------------------------
--- **TIRESIAS** class, extends Core.Base#BASE
-- @type TIRESIAS
-- @field #string ClassName
-- @field #booelan debug
-- @field #string version
-- @field #number Interval
-- @field Core.Set#SET_GROUP GroundSet
-- @field #number Coalition
-- @field Core.Set#SET_GROUP VehicleSet
-- @field Core.Set#SET_GROUP AAASet
-- @field Core.Set#SET_GROUP SAMSet
-- @field Core.Set#SET_GROUP ExceptionSet
-- @field Core.Set#SET_OPSGROUP OpsGroupSet
-- @field #number AAARange
-- @field #number HeloSwitchRange
-- @field #number PlaneSwitchRange
-- @field Core.Set#SET_GROUP FlightSet
-- @field #boolean SwitchAAA
-- @extends Core.Fsm#FSM
---
-- @type TIRESIAS.Data
-- @field #string type
-- @field #number range
-- @field #boolean invisible
-- @field #boolean AIOff
-- @field #boolean exception
--- *Tiresias, Greek demi-god and shapeshifter, blinded by the Gods, works as oracle for you.* (Wiki)
--
-- ===
--
-- ## TIRESIAS Concept
--
-- * Designed to keep CPU and Network usage lower on missions with a lot of ground units.
-- * Does not affect ships to keep the Navy guys happy.
-- * Does not affect OpsGroup type groups.
-- * Distinguishes between SAM groups, AAA groups and other ground groups.
-- * Exceptions can be defined in SET_GROUP objects to keep certain actions going.
-- * Works coalition-independent in the back
-- * Easy setup.
--
-- ## Setup
--
-- Setup is a one-liner:
--
-- local blinder = TIRESIAS:New()
--
-- Optionally you can set up exceptions, e.g. for convoys driving around
--
-- local exceptionset = SET_GROUP:New():FilterCoalitions("red"):FilterPrefixes("Convoy"):FilterStart()
-- local blinder = TIRESIAS:New()
-- blinder:AddExceptionSet(exceptionset)
--
-- Options
--
-- -- Setup different radius for activation around helo and airplane groups (applies to AI and humans)
-- blinder:SetActivationRanges(10,25) -- defaults are 10, and 25
--
-- -- Setup engagement ranges for AAA (non-advanced SAM units like Flaks etc) and if you want them to be AIOff
-- blinder:SetAAARanges(60,true) -- defaults are 60, and true
--
-- @field #TIRESIAS
TIRESIAS = {
ClassName = "TIRESIAS",
debug = false,
version = "0.0.4",
Interval = 20,
GroundSet = nil,
VehicleSet = nil,
AAASet = nil,
SAMSet = nil,
ExceptionSet = nil,
AAARange = 60, -- 60%
HeloSwitchRange = 10, -- NM
PlaneSwitchRange = 25, -- NM
SwitchAAA = true,
}
--- [USER] Create a new Tiresias object and start it up.
-- @param #TIRESIAS self
-- @return #TIRESIAS self
function TIRESIAS:New()
-- Inherit everything from FSM class.
local self = BASE:Inherit(self, FSM:New()) -- #TIRESIAS
--- FSM Functions ---
-- Start State.
self:SetStartState("Stopped")
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
self:AddTransition("*", "Status", "*") -- TIRESIAS status update.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
self.ExceptionSet = SET_GROUP:New():Clear(false)
self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
self.lid = string.format("TIRESIAS %s | ",self.version)
self:I(self.lid.."Managing ground groups!")
--- Triggers the FSM event "Stop". Stops TIRESIAS and all its event handlers.
-- @function [parent=#TIRESIAS] Stop
-- @param #TIRESIAS self
--- Triggers the FSM event "Stop" after a delay. Stops TIRESIAS and all its event handlers.
-- @function [parent=#TIRESIAS] __Stop
-- @param #TIRESIAS self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Start". Starts TIRESIAS and all its event handlers. Note - `:New()` already starts the instance.
-- @function [parent=#TIRESIAS] Start
-- @param #TIRESIAS self
--- Triggers the FSM event "Start" after a delay. Starts TIRESIAS and all its event handlers. Note - `:New()` already starts the instance.
-- @function [parent=#TIRESIAS] __Start
-- @param #TIRESIAS self
-- @param #number delay Delay in seconds.
self:__Start(1)
return self
end
-------------------------------------------------------------------------------------------------------------
--
-- Helper Functions
--
-------------------------------------------------------------------------------------------------------------
---[USER] Set activation radius for Helos and Planes in Nautical Miles.
-- @param #TIRESIAS self
-- @param #number HeloMiles Radius around a Helicopter in which AI ground units will be activated. Defaults to 10NM.
-- @param #number PlaneMiles Radius around an Airplane in which AI ground units will be activated. Defaults to 25NM.
-- @return #TIRESIAS self
function TIRESIAS:SetActivationRanges(HeloMiles,PlaneMiles)
self.HeloSwitchRange = HeloMiles or 10
self.PlaneSwitchRange = PlaneMiles or 25
return self
end
---[USER] Set AAA Ranges - AAA equals non-SAM systems which qualify as AAA in DCS world.
-- @param #TIRESIAS self
-- @param #number FiringRange The engagement range that AAA units will be set to. Can be 0 to 100 (percent). Defaults to 60.
-- @param #boolean SwitchAAA Decide if these system will have their AI switched off, too. Defaults to true.
-- @return #TIRESIAS self
function TIRESIAS:SetAAARanges(FiringRange,SwitchAAA)
self.AAARange = FiringRange or 60
self.SwitchAAA = (SwitchAAA == false) and false or true
return self
end
--- [USER] Add a SET_GROUP of GROUP objects as exceptions. Can be done multiple times.
-- @param #TIRESIAS self
-- @param Core.Set#SET_GROUP Set to add to the exception list.
-- @return #TIRESIAS self
function TIRESIAS:AddExceptionSet(Set)
self:T(self.lid.."AddExceptionSet")
local exceptions = self.ExceptionSet
Set:ForEachGroupAlive(
function(grp)
if not grp.Tiresias then
grp.Tiresias = { -- #TIRESIAS.Data
type = "Exception",
exception = true,
}
exceptions:AddGroup(grp,true)
end
BASE:I("TIRESIAS: Added exception group: "..grp:GetName())
end
)
return self
end
--- [INTERNAL] Filter Function
-- @param Wrapper.Group#GROUP Group
-- @return #boolean isin
function TIRESIAS._FilterNotAAA(Group)
local grp = Group -- Wrapper.Group#GROUP
local isaaa = grp:IsAAA()
if isaaa == true and grp:IsGround() and not grp:IsShip() then
return false -- remove from SET
else
return true -- keep in SET
end
end
--- [INTERNAL] Filter Function
-- @param Wrapper.Group#GROUP Group
-- @return #boolean isin
function TIRESIAS._FilterNotSAM(Group)
local grp = Group -- Wrapper.Group#GROUP
local issam = grp:IsSAM()
if issam == true and grp:IsGround() and not grp:IsShip() then
return false -- remove from SET
else
return true -- keep in SET
end
end
--- [INTERNAL] Filter Function
-- @param Wrapper.Group#GROUP Group
-- @return #boolean isin
function TIRESIAS._FilterAAA(Group)
local grp = Group -- Wrapper.Group#GROUP
local isaaa = grp:IsAAA()
if isaaa == true and grp:IsGround() and not grp:IsShip() then
return true -- remove from SET
else
return false -- keep in SET
end
end
--- [INTERNAL] Filter Function
-- @param Wrapper.Group#GROUP Group
-- @return #boolean isin
function TIRESIAS._FilterSAM(Group)
local grp = Group -- Wrapper.Group#GROUP
local issam = grp:IsSAM()
if issam == true and grp:IsGround() and not grp:IsShip() then
return true -- remove from SET
else
return false -- keep in SET
end
end
--- [INTERNAL] Init Groups
-- @param #TIRESIAS self
-- @return #TIRESIAS self
function TIRESIAS:_InitGroups()
self:T(self.lid.."_InitGroups")
-- Set all groups invisible/motionless
local EngageRange = self.AAARange
local SwitchAAA = self.SwitchAAA
--- AAA
self.AAASet:ForEachGroupAlive(
function(grp)
if not grp.Tiresias then
grp:OptionEngageRange(EngageRange)
grp:SetCommandInvisible(true)
if SwitchAAA then
grp:SetAIOff()
grp:EnableEmission(false)
end
grp.Tiresias = { -- #TIRESIAS.Data
type = "AAA",
invisible = true,
range = EngageRange,
exception = false,
AIOff = SwitchAAA,
}
end
if grp.Tiresias and (not grp.Tiresias.exception == true) then
if grp.Tiresias.invisible and grp.Tiresias.invisible == false then
grp:SetCommandInvisible(true)
grp.Tiresias.invisible = true
if SwitchAAA then
grp:SetAIOff()
grp:EnableEmission(false)
grp.Tiresias.AIOff = true
end
end
end
--BASE:I(string.format("Init/Switch off AAA %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
end
)
--- Vehicles
self.VehicleSet:ForEachGroupAlive(
function(grp)
if not grp.Tiresias then
grp:SetAIOff()
grp:SetCommandInvisible(true)
grp.Tiresias = { -- #TIRESIAS.Data
type = "Vehicle",
invisible = true,
AIOff = true,
exception = false,
}
end
if grp.Tiresias and (not grp.Tiresias.exception == true) then
if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then
grp:SetCommandInvisible(true)
grp:SetAIOff()
grp.Tiresias.invisible = true
end
end
--BASE:I(string.format("Init/Switch off Vehicle %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
end
)
--- SAM
self.SAMSet:ForEachGroupAlive(
function(grp)
if not grp.Tiresias then
grp:SetCommandInvisible(true)
grp.Tiresias = { -- #TIRESIAS.Data
type = "SAM",
invisible = true,
exception = false,
}
end
if grp.Tiresias and (not grp.Tiresias.exception == true) then
if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then
grp:SetCommandInvisible(true)
grp.Tiresias.invisible = true
end
end
--BASE:I(string.format("Init/Switch off SAM %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
end
)
return self
end
--- [INTERNAL] Event handler function
-- @param #TIRESIAS self
-- @param Core.Event#EVENTDATA EventData
-- @return #TIRESIAS self
function TIRESIAS:_EventHandler(EventData)
self:T(string.format("%s Event = %d",self.lid, EventData.id))
local event = EventData -- Core.Event#EVENTDATA
if event.id == EVENTS.PlayerEnterAircraft or event.id == EVENTS.PlayerEnterUnit then
--local _coalition = event.IniCoalition
--if _coalition ~= self.Coalition then
-- return --ignore!
--end
local unitname = event.IniUnitName or "none"
local _unit = event.IniUnit
local _group = event.IniGroup
if _group and _group:IsAlive() then
local radius = self.PlaneSwitchRange
if _group:IsHelicopter() then
radius = self.HeloSwitchRange
end
self:_SwitchOnGroups(_group,radius)
end
end
return self
end
--- [INTERNAL] Switch Groups Behaviour
-- @param #TIRESIAS self
-- @param Wrapper.Group#GROUP group
-- @param #number radius Radius in NM
-- @return #TIRESIAS self
function TIRESIAS:_SwitchOnGroups(group,radius)
self:T(self.lid.."_SwitchOnGroups "..group:GetName().." Radius "..radius.." NM")
local zone = ZONE_GROUP:New("Zone-"..group:GetName(),group,UTILS.NMToMeters(radius))
local ground = SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce()
local count = ground:CountAlive()
if self.debug then
local text = string.format("There are %d groups around this plane or helo!",count)
self:I(text)
end
local SwitchAAA = self.SwitchAAA
if ground:CountAlive() > 0 then
ground:ForEachGroupAlive(
function(grp)
if grp.Tiresias and grp.Tiresias.type and (not grp.Tiresias.exception == true ) then
if grp.Tiresias.invisible == true then
grp:SetCommandInvisible(false)
grp.Tiresias.invisible = false
end
if grp.Tiresias.type == "Vehicle" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then
grp:SetAIOn()
grp.Tiresias.AIOff = false
end
if SwitchAAA and grp.Tiresias.type == "AAA" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then
grp:SetAIOn()
grp:EnableEmission(true)
grp.Tiresias.AIOff = false
end
--BASE:I(string.format("TIRESIAS - Switch on %s %s (Exception %s)",tostring(grp.Tiresias.type),grp:GetName(),tostring(grp.Tiresias.exception)))
else
BASE:E("TIRESIAS - This group has not been initialized or is an exception!")
end
end
)
end
return self
end
-------------------------------------------------------------------------------------------------------------
--
-- FSM Functions
--
-------------------------------------------------------------------------------------------------------------
--- [INTERNAL] FSM Function
-- @param #TIRESIAS self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #TIRESIAS self
function TIRESIAS:onafterStart(From, Event, To)
self:T({From, Event, To})
local VehicleSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterNotAAA):FilterFunction(TIRESIAS._FilterNotSAM):FilterStart()
local AAASet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterAAA):FilterStart()
local SAMSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterSAM):FilterStart()
local OpsGroupSet = SET_OPSGROUP:New():FilterActive(true):FilterStart()
self.FlightSet = SET_GROUP:New():FilterCategories({"plane","helicopter"}):FilterStart()
local EngageRange = self.AAARange
local ExceptionSet = self.ExceptionSet
if self.ExceptionSet then
function ExceptionSet:OnAfterAdded(From,Event,To,ObjectName,Object)
BASE:I("TIRESIAS: EXCEPTION Object Added: "..Object:GetName())
if Object and Object:IsAlive() then
Object.Tiresias = { -- #TIRESIAS.Data
type = "Exception",
exception = true,
}
Object:SetAIOn()
Object:SetCommandInvisible(false)
Object:EnableEmission(true)
end
end
local OGS = OpsGroupSet:GetAliveSet()
for _,_OG in pairs(OGS or {}) do
local OG = _OG -- Ops.OpsGroup#OPSGROUP
local grp = OG:GetGroup()
ExceptionSet:AddGroup(grp,true)
end
function OpsGroupSet:OnAfterAdded(From,Event,To,ObjectName,Object)
local grp = Object:GetGroup()
ExceptionSet:AddGroup(grp,true)
end
end
function VehicleSet:OnAfterAdded(From,Event,To,ObjectName,Object)
BASE:I("TIRESIAS: VEHCILE Object Added: "..Object:GetName())
if Object and Object:IsAlive() then
Object:SetAIOff()
Object:SetCommandInvisible(true)
Object.Tiresias = { -- #TIRESIAS.Data
type = "Vehicle",
invisible = true,
AIOff = true,
exception = false,
}
end
end
local SwitchAAA = self.SwitchAAA
function AAASet:OnAfterAdded(From,Event,To,ObjectName,Object)
if Object and Object:IsAlive() then
BASE:I("TIRESIAS: AAA Object Added: "..Object:GetName())
Object:OptionEngageRange(EngageRange)
Object:SetCommandInvisible(true)
if SwitchAAA then
Object:SetAIOff()
Object:EnableEmission(false)
end
Object.Tiresias = { -- #TIRESIAS.Data
type = "AAA",
invisible = true,
range = EngageRange,
exception = false,
AIOff = SwitchAAA,
}
end
end
function SAMSet:OnAfterAdded(From,Event,To,ObjectName,Object)
if Object and Object:IsAlive() then
BASE:I("TIRESIAS: SAM Object Added: "..Object:GetName())
Object:SetCommandInvisible(true)
Object.Tiresias = { -- #TIRESIAS.Data
type = "SAM",
invisible = true,
exception = false,
}
end
end
self.VehicleSet = VehicleSet
self.AAASet = AAASet
self.SAMSet = SAMSet
self.OpsGroupSet = OpsGroupSet
self:_InitGroups()
self:__Status(1)
return self
end
--- [INTERNAL] FSM Function
-- @param #TIRESIAS self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #TIRESIAS self
function TIRESIAS:onbeforeStatus(From, Event, To)
self:T({From, Event, To})
if self:GetState() == "Stopped" then
return false
end
return self
end
--- [INTERNAL] FSM Function
-- @param #TIRESIAS self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #TIRESIAS self
function TIRESIAS:onafterStatus(From, Event, To)
self:T({From, Event, To})
if self.debug then
local count = self.VehicleSet:CountAlive()
local AAAcount = self.AAASet:CountAlive()
local SAMcount = self.SAMSet:CountAlive()
local text = string.format("Overall: %d | Vehicles: %d | AAA: %d | SAM: %d",count+AAAcount+SAMcount,count,AAAcount,SAMcount)
self:I(text)
end
self:_InitGroups()
if self.FlightSet:CountAlive() > 0 then
local Set = self.FlightSet:GetAliveSet()
for _,_plane in pairs(Set) do
local plane = _plane -- Wrapper.Group#GROUP
local radius = self.PlaneSwitchRange
if plane:IsHelicopter() then
radius = self.HeloSwitchRange
end
self:_SwitchOnGroups(_plane,radius)
end
end
if self:GetState() ~= "Stopped" then
self:__Status(self.Interval)
end
return self
end
--- [INTERNAL] FSM Function
-- @param #TIRESIAS self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #TIRESIAS self
function TIRESIAS:onafterStop(From, Event, To)
self:T({From, Event, To})
self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
return self
end
-------------------------------------------------------------------------------------------------------------
--
-- End
--
-------------------------------------------------------------------------------------------------------------

View File

@@ -84,6 +84,7 @@ __Moose.Include( 'Scripts/Moose/Functional/ZoneCaptureCoalition.lua' )
__Moose.Include( 'Scripts/Moose/Functional/ZoneGoal.lua' )
__Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCargo.lua' )
__Moose.Include( 'Scripts/Moose/Functional/ZoneGoalCoalition.lua' )
__Moose.Include( 'Scripts/Moose/Functional/Tiresias.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
__Moose.Include( 'Scripts/Moose/Ops/AirWing.lua' )

View File

@@ -890,7 +890,7 @@ _ATIS = {}
--- ATIS class version.
-- @field #string version
ATIS.version = "0.10.4"
ATIS.version = "1.0.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1528,12 +1528,12 @@ end
--- Use SRS Simple-Text-To-Speech for transmissions. No sound files necessary.
-- @param #ATIS self
-- @param #string PathToSRS Path to SRS directory.
-- @param #string PathToSRS Path to SRS directory (only necessary if SRS exe backend is used).
-- @param #string Gender Gender: "male" or "female" (default).
-- @param #string Culture Culture, e.g. "en-GB" (default).
-- @param #string Voice Specific voice. Overrides `Gender` and `Culture`.
-- @param #number Port SRS port. Default 5002.
-- @param #string GoogleKey Path to Google JSON-Key.
-- @param #string GoogleKey Path to Google JSON-Key (SRS exe backend) or Google API key (DCS-gRPC backend).
-- @return #ATIS self
function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey)
if PathToSRS or MSRS.path then

View File

@@ -8212,7 +8212,7 @@ function AIRBOSS:OnEventBirth( EventData )
self:E( EventData )
return
end
if EventData.IniUnit == nil then
if EventData.IniUnit == nil and (not EventData.IniObjectCategory == Object.Category.STATIC) then
self:E( self.lid .. "ERROR: EventData.IniUnit=nil in event BIRTH!" )
self:E( EventData )
return
@@ -11197,7 +11197,7 @@ function AIRBOSS:_AttitudeMonitor( playerData )
end
text = text .. string.format( "\nPitch=%.1f° | Roll=%.1f° | Yaw=%.1f°", pitch, roll, yaw )
text = text .. string.format( "\nClimb Angle=%.1f° | Rate=%d ft/min", unit:GetClimbAngle(), velo.y * 196.85 )
local dist = self:_GetOptLandingCoordinate():Get3DDistance( playerData.unit )
local dist = self:_GetOptLandingCoordinate():Get3DDistance( playerData.unit:GetVec3() )
-- Get player velocity in km/h.
local vplayer = playerData.unit:GetVelocityKMH()
-- Get carrier velocity in km/h.
@@ -14957,7 +14957,7 @@ function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture )
self.PilotRadio.gender = Gender or "male"
self.PilotRadio.culture = Culture or "en-US"
if (not Voice) and self.SRS and self.SRS.google then
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

View File

@@ -1861,6 +1861,7 @@ function ARMYGROUP:_UpdateEngageTarget()
else
-- Could not get position of target (not alive any more?) ==> Disengage.
self:T(self.lid.."Could not get position of target ==> Disengage!")
self:Disengage()
end
@@ -1868,6 +1869,7 @@ function ARMYGROUP:_UpdateEngageTarget()
else
-- Target not alive any more ==> Disengage.
self:T(self.lid.."Target not ALIVE ==> Disengage!")
self:Disengage()
end

View File

@@ -31,7 +31,7 @@
-- @image OPS_CSAR.jpg
-- Date: May 2023
-- Last: Update Oct 2024
-- Last: Update Dec 2024
-------------------------------------------------------------------------
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM

View File

@@ -24,7 +24,7 @@
-- @module Ops.CTLD
-- @image OPS_CTLD.jpg
-- Last Update November 2023
-- Last Update December 2023
do
@@ -1228,7 +1228,7 @@ CTLD.UnitTypeCapabilities = {
--- CTLD class version.
-- @field #string version
CTLD.version="1.0.43"
CTLD.version="1.0.45"
--- Instantiate a new CTLD.
-- @param #CTLD self
@@ -1443,6 +1443,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop". Stops the CTLD and all its event handlers.
-- @function [parent=#CTLD] Stop
-- @param #CTLD self
--- Triggers the FSM event "Stop" after a delay. Stops the CTLD and all its event handlers.
@@ -2454,11 +2455,13 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack)
realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,nil,subcat)
table.insert(droppedcargo,realcargo)
else
realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],false,cargotype.PerCrateMass,nil,subcat)
Cargo:RemoveStock()
realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],false,cargotype.PerCrateMass,nil,subcat)
end
table.insert(self.Spawned_Cargo, realcargo)
end
if not (drop or pack) then
Cargo:RemoveStock()
end
local text = string.format("Crates for %s have been positioned near you!",cratename)
if drop then
text = string.format("Crates for %s have been dropped!",cratename)
@@ -2561,6 +2564,40 @@ function CTLD:_ListCratesNearby( _group, _unit)
return self
end
-- (Internal) Function to find and Remove nearby crates.
-- @param #CTLD self
-- @param Wrapper.Group#GROUP Group
-- @param Wrapper.Unit#UNIT Unit
-- @return #CTLD self
function CTLD:_RemoveCratesNearby( _group, _unit)
self:T(self.lid .. " _RemoveCratesNearby")
local finddist = self.CrateDistance or 35
local crates,number = self:_FindCratesNearby(_group,_unit, finddist,true) -- #table
if number > 0 then
local text = REPORT:New("Removing Crates Found Nearby:")
text:Add("------------------------------------------------------------")
for _,_entry in pairs (crates) do
local entry = _entry -- #CTLD_CARGO
local name = entry:GetName() --#string
local dropped = entry:WasDropped()
if dropped then
text:Add(string.format("Crate for %s, %dkg removed",name, entry.PerCrateMass))
else
text:Add(string.format("Crate for %s, %dkg removed",name, entry.PerCrateMass))
end
entry:GetPositionable():Destroy(false)
end
if text:GetCount() == 1 then
text:Add(" N O N E")
end
text:Add("------------------------------------------------------------")
self:_SendMessage(text:Text(), 30, true, _group)
else
self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddist), 10, false, _group)
end
return self
end
--- (Internal) Return distance in meters between two coordinates.
-- @param #CTLD self
-- @param Core.Point#COORDINATE _point1 Coordinate one
@@ -2976,6 +3013,35 @@ function CTLD:IsHercules(Unit)
end
end
--- (Internal) Function to set troops positions of a template to a nice circle
-- @param #CTLD self
-- @param Core.Point#COORDINATE Coordinate Start coordinate to use
-- @param #number Radius Radius to be used
-- @param #number Heading Heading starting with
-- @param #string Template The group template name
-- @return #table Positions The positions table
function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template)
local Positions = {}
local template = _DATABASE:GetGroupTemplate(Template)
UTILS.PrintTableToLog(template)
local numbertroops = #template.units
local newcenter = Coordinate:Translate(Radius,((Heading+270)%360))
for i=1,360,math.floor(360/numbertroops) do
local phead = ((Heading+270+i)%360)
local post = newcenter:Translate(Radius,phead)
local pos1 = post:GetVec2()
local p1t = {
x = pos1.x,
y = pos1.y,
heading = phead,
}
table.insert(Positions,p1t)
end
UTILS.PrintTableToLog(Positions)
return Positions
end
--- (Internal) Function to unload troops from heli.
-- @param #CTLD self
-- @param Wrapper.Group#GROUP Group
@@ -3027,14 +3093,29 @@ function CTLD:_UnloadTroops(Group, Unit)
zoneradius = Unit:GetVelocityMPS() or 100
end
local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor)
local randomcoord = zone:GetRandomCoordinate(10,30*factor):GetVec2()
local randomcoord = zone:GetRandomCoordinate(10,30*factor) --:GetVec2()
local heading = Group:GetHeading() or 0
-- Spawn troops left from us, closer when hovering, further off when landed
if hoverunload or grounded then
randomcoord = Group:GetCoordinate()
-- slightly left from us
local Angle = (heading+270)%360
local offset = hoverunload and 1.5 or 5
randomcoord:Translate(offset,Angle,nil,true)
end
local tempcount = 0
for _,_template in pairs(temptable) do
self.TroopCounter = self.TroopCounter + 1
tempcount = tempcount+1
local alias = string.format("%s-%d", _template, math.random(1,100000))
local rad = 2.5+tempcount
local Positions = self:_GetUnitPositions(randomcoord,rad,heading,_template)
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
:InitRandomizeUnits(true,20,2)
--:InitRandomizeUnits(true,20,2)
--:InitHeading(heading)
:InitDelayOff()
:SpawnFromVec2(randomcoord)
:InitSetUnitAbsolutePositions(Positions)
:SpawnFromVec2(randomcoord:GetVec2())
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type)
end -- template loop
cargo:SetWasDropped(true)
@@ -3637,6 +3718,7 @@ function CTLD:_RefreshF10Menus()
local loadmenu = MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates, self._LoadCratesNearby, self, _group, _unit)
local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates)
local packmenu = MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit)
local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates)
if self.usesubcats then
local subcatmenus = {}
@@ -3672,6 +3754,7 @@ function CTLD:_RefreshF10Menus()
end
end
listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit)
removecrates = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu, self._RemoveCratesNearby, self, _group, _unit)
local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit)
if not self.nobuildmenu then
local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit)
@@ -3744,7 +3827,7 @@ end
-- @param #CTLD_CARGO.Enum Type Type of cargo. I.e. VEHICLE or FOB. VEHICLE will move to destination zones when dropped/build, FOB stays put.
-- @param #number NoCrates Number of crates needed to build this cargo.
-- @param #number PerCrateMass Mass in kg of each crate
-- @param #number Stock Number of groups in stock. Nil for unlimited.
-- @param #number Stock Number of buildable groups in stock. Nil for unlimited.
-- @param #string SubCategory Name of sub-category (optional).
function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory)
self:T(self.lid .. " AddCratesCargo")

View File

@@ -141,14 +141,14 @@
-- -- **Note** If you need different tanker types, i.e. Boom and Drogue, set them up at different AirWings!
-- -- Add a tanker point
-- mywing:AddPatrolPointTanker(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone Tanker"):GetCoordinate(),20000,280,270,50)
-- -- Add an AWACS squad - Radio 251 AM, TACAN 51Y
-- -- Add a tanker squad - Radio 251 AM, TACAN 51Y
-- mywing:AddTankerSquadron("Blue Tanker","Tanker Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.EXCELLENT,602,nil,251,radio.modulation.AM,51)
--
-- ### Add an AWACS (optional)
--
-- -- Add an AWACS point
-- mywing:AddPatrolPointAwacs(AIRBASE.Caucasus.Kutaisi,ZONE:FindByName("Blue Zone AWACS"):GetCoordinate(),25000,300,270,50)
-- -- Add a tanker squad - Radio 251 AM, TACAN 51Y
-- -- Add an AWACS squad - Radio 251 AM, TACAN 51Y
-- mywing:AddAWACSSquadron("Blue AWACS","AWACS Ops Kutaisi",AIRBASE.Caucasus.Kutaisi,20,AI.Skill.AVERAGE,702,nil,271,radio.modulation.AM)
--
-- # Fine-Tuning

View File

@@ -4957,7 +4957,7 @@ end
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add parking guard in front of a parking aircraft.
--- [INTERNAL] Add parking guard in front of a parking aircraft - delayed for MP.
-- @param #FLIGHTCONTROL self
-- @param Wrapper.Unit#UNIT unit The aircraft.
function FLIGHTCONTROL:SpawnParkingGuard(unit)

View File

@@ -26,6 +26,7 @@
-- @field #table filterCategoryGroup Filter for group categories.
-- @field Core.Set#SET_ZONE acceptzoneset Set of accept zones. If defined, only contacts in these zones are considered.
-- @field Core.Set#SET_ZONE rejectzoneset Set of reject zones. Contacts in these zones are not considered, even if they are in accept zones.
-- @field Core.Set#SET_ZONE conflictzoneset Set of conflict zones. Contacts in these zones are considered, even if they are not in accept zones or if they are in reject zones.
-- @field #table Contacts Table of detected items.
-- @field #table ContactsLost Table of lost detected items.
-- @field #table ContactsUnknown Table of new detected items.
@@ -159,13 +160,12 @@ INTEL.Ctype={
--- INTEL class version.
-- @field #string version
INTEL.version="0.3.5"
INTEL.version="0.3.6"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Make forget times user input. Currently these are hard coded.
-- TODO: Add min cluster size. Only create new clusters if they have a certain group size.
-- TODO: process detected set asynchroniously for better performance.
-- DONE: Add statics.
@@ -266,6 +266,7 @@ function INTEL:New(DetectionSet, Coalition, Alias)
self:SetForgetTime()
self:SetAcceptZones()
self:SetRejectZones()
self:SetConflictZones()
------------------------
--- Pseudo Functions ---
@@ -416,7 +417,7 @@ function INTEL:RemoveAcceptZone(AcceptZone)
end
--- Set reject zones. Contacts detected in this/these zone(s) are rejected and not reported by the detection.
-- Note that reject zones overrule accept zones, i.e. if a unit is inside and accept zone and inside a reject zone, it is rejected.
-- Note that reject zones overrule accept zones, i.e. if a unit is inside an accept zone and inside a reject zone, it is rejected.
-- @param #INTEL self
-- @param Core.Set#SET_ZONE RejectZoneSet Set of reject zone(s).
-- @return #INTEL self
@@ -426,7 +427,7 @@ function INTEL:SetRejectZones(RejectZoneSet)
end
--- Add a reject zone. Contacts detected in this zone are rejected and not reported by the detection.
-- Note that reject zones overrule accept zones, i.e. if a unit is inside and accept zone and inside a reject zone, it is rejected.
-- Note that reject zones overrule accept zones, i.e. if a unit is inside an accept zone and inside a reject zone, it is rejected.
-- @param #INTEL self
-- @param Core.Zone#ZONE RejectZone Add a zone to the reject zone set.
-- @return #INTEL self
@@ -444,6 +445,36 @@ function INTEL:RemoveRejectZone(RejectZone)
return self
end
--- Set conflict zones. Contacts detected in this/these zone(s) are reported by the detection.
-- Note that conflict zones overrule all other zones, i.e. if a unit is outside of an accept zone and inside a reject zone, it is still reported if inside a conflict zone.
-- @param #INTEL self
-- @param Core.Set#SET_ZONE ConflictZoneSet Set of conflict zone(s).
-- @return #INTEL self
function INTEL:SetConflictZones(ConflictZoneSet)
self.conflictzoneset=ConflictZoneSet or SET_ZONE:New()
return self
end
--- Add a conflict zone. Contacts detected in this zone are conflicted and not reported by the detection.
-- Note that conflict zones overrule all other zones, i.e. if a unit is outside of an accept zone and inside a reject zone, it is still reported if inside a conflict zone.
-- @param #INTEL self
-- @param Core.Zone#ZONE ConflictZone Add a zone to the conflict zone set.
-- @return #INTEL self
function INTEL:AddConflictZone(ConflictZone)
self.conflictzoneset:AddZone(ConflictZone)
return self
end
--- Remove a conflict zone from the conflict zone set.
-- Note that conflict zones overrule all other zones, i.e. if a unit is outside of an accept zone and inside a reject zone, it is still reported if inside a conflict zone.
-- @param #INTEL self
-- @param Core.Zone#ZONE ConflictZone Remove a zone from the conflict zone set.
-- @return #INTEL self
function INTEL:RemoveConflictZone(ConflictZone)
self.conflictzoneset:Remove(ConflictZone:GetName(), true)
return self
end
--- **OBSOLETE, will be removed in next version!** Set forget contacts time interval.
-- Previously known contacts that are not detected any more, are "lost" after this time.
-- This avoids fast oscillations between a contact being detected and undetected.
@@ -481,6 +512,33 @@ function INTEL:SetFilterCategory(Categories)
return self
end
--- Method to make the radar detection less accurate, e.g. for WWII scenarios.
-- @param #INTEL self
-- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground)
-- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance)
-- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found)
-- @param #number closing Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20.
-- @return #INTEL self
function INTEL:SetRadarBlur(minheight,thresheight,thresblur,closing)
self.RadarBlur = true
self.RadarBlurMinHeight = minheight or 250 -- meters
self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group
self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall
self.RadarBlurClosing = closing or 20 -- 20km
self.RadarBlurClosingSquare = self.RadarBlurClosing * self.RadarBlurClosing
return self
end
--- Set the accept range in kilometers from each of the recce. Only object closer than this range will be detected.
-- @param #INTEL self
-- @param #number Range Range in kilometers
-- @return #INTEL self
function INTEL:SetAcceptRange(Range)
self.RadarAcceptRange = true
self.RadarAcceptRangeKilometers = Range or 75
return self
end
--- Filter group categories. Valid categories are:
--
-- * Group.Category.AIRPLANE
@@ -780,7 +838,19 @@ function INTEL:UpdateIntel()
local remove={}
for unitname,_unit in pairs(DetectedUnits) do
local unit=_unit --Wrapper.Unit#UNIT
local inconflictzone=false
-- Check if unit is in any of the conflict zones.
if self.conflictzoneset:Count()>0 then
for _,_zone in pairs(self.conflictzoneset.Set) do
local zone=_zone --Core.Zone#ZONE
if unit:IsInZone(zone) then
inconflictzone=true
break
end
end
end
-- Check if unit is in any of the accept zones.
if self.acceptzoneset:Count()>0 then
local inzone=false
@@ -793,7 +863,7 @@ function INTEL:UpdateIntel()
end
-- Unit is not in accept zone ==> remove!
if not inzone then
if (not inzone) and (not inconflictzone) then
table.insert(remove, unitname)
end
end
@@ -810,7 +880,7 @@ function INTEL:UpdateIntel()
end
-- Unit is inside a reject zone ==> remove!
if inzone then
if inzone and (not inconflictzone) then
table.insert(remove, unitname)
end
end
@@ -1037,7 +1107,7 @@ function INTEL:CreateDetectedItems(DetectedGroups, DetectedStatics, RecceDetecti
end
--- (Internal) Return the detected target groups of the controllable as a @{SET_GROUP}.
-- The optional parametes specify the detection methods that can be applied.
-- The optional parameters specify the detection methods that can be applied.
-- If no detection method is given, the detection will use all the available methods by default.
-- @param #INTEL self
-- @param Wrapper.Unit#UNIT Unit The unit detecting.
@@ -1053,6 +1123,7 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua
-- Get detected DCS units.
local reccename = Unit:GetName()
local detectedtargets=Unit:GetDetectedTargets(DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK)
for DetectionObjectID, Detection in pairs(detectedtargets or {}) do
@@ -1071,11 +1142,47 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua
if status then
local unit=UNIT:FindByName(name)
if unit and unit:IsAlive() then
DetectedUnits[name]=unit
RecceDetecting[name]=reccename
self:T(string.format("Unit %s detect by %s", name, reccename))
local DetectionAccepted = true
if self.RadarAcceptRange then
local reccecoord = Unit:GetCoordinate()
local coord = unit:GetCoordinate()
local dist = math.floor(coord:Get2DDistance(reccecoord)/1000) -- km
if dist > self.RadarAcceptRangeKilometers then DetectionAccepted = false end
end
if self.RadarBlur then
local reccecoord = Unit:GetCoordinate()
local coord = unit:GetCoordinate()
local dist = math.floor(coord:Get2DDistance(reccecoord)/1000) -- km
local AGL = unit:GetAltitude(true)
local minheight = self.RadarBlurMinHeight or 250 -- meters
local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group
local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall
--local dist = math.floor(Distance)
if dist <= self.RadarBlurClosing then
thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight)
thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur)
end
local fheight = math.floor(math.random(1,10000)/100)
local fblur = math.floor(math.random(1,10000)/100)
if fblur > thresblur then DetectionAccepted = false end
if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end
if self.debug or self.verbose > 1 then
MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose>1)
MESSAGE:New("Unit "..name.." is at "..math.floor(AGL).."m. Distance "..math.floor(dist).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose>1)
MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose>1)
MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose>1)
end
end
if DetectionAccepted then
DetectedUnits[name]=unit
RecceDetecting[name]=reccename
self:T(string.format("Unit %s detect by %s", name, reccename))
end
else
if self.detectStatics then
local static=STATIC:FindByName(name, false)
@@ -1093,7 +1200,6 @@ function INTEL:GetDetectedUnits(Unit, DetectedUnits, RecceDetecting, DetectVisua
end
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -3480,10 +3480,9 @@ function OPSGROUP:RemoveWaypoint(wpindex)
-- Could be that the waypoint we are currently moving to was the LAST waypoint. Then we now passed the final waypoint.
if (self.adinfinitum or istemp) then
self:_PassedFinalWaypoint(false, "Removed PASSED temporary waypoint ")
end
self:_PassedFinalWaypoint(false, "Removed PASSED temporary waypoint")
end
end
end
@@ -5811,6 +5810,27 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
end
end
if self.legion and self.legionReturn==false and self.waypoints and #self.waypoints==1 then
---
-- This is the case where a group was send on a mission (which is over now), has no addional
-- waypoints or tasks and should NOT return to its legion.
-- We create a new waypoint at the current position and let it hold here.
---
local Coordinate=self:GetCoordinate()
if self.isArmygroup then
ARMYGROUP.AddWaypoint(self, Coordinate, 0, nil, nil, false)
elseif self.isNavygroup then
NAVYGROUP.AddWaypoint(self,Coordinate, 0, nil, nil, false)
end
-- Remove original waypoint.
self:RemoveWaypoint(1)
self:_PassedFinalWaypoint(true, "Passed final waypoint as group is done with mission but should NOT return to its legion")
end
-- Check if group is done.
self:_CheckGroupDone(delay)

View File

@@ -97,7 +97,7 @@ OPSZONE.ZoneType={
--- OPSZONE class version.
-- @field #string version
OPSZONE.version="0.6.0"
OPSZONE.version="0.6.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -1277,7 +1277,7 @@ function OPSZONE:EvaluateZone()
if Nblu>0 then
if not self:IsAttacked() then
if not self:IsAttacked() and self.Tnut>=self.threatlevelCapture then
self:Attacked(coalition.side.BLUE)
end
@@ -1329,7 +1329,7 @@ function OPSZONE:EvaluateZone()
if Nred>0 then
if not self:IsAttacked() then
if not self:IsAttacked() and self.Tnut>=self.threatlevelCapture then
-- Red is attacking blue zone.
self:Attacked(coalition.side.RED)
end

View File

@@ -79,6 +79,7 @@
-- @field Utilities.FiFo#FIFO TargetCache
-- @field #boolean smokeownposition
-- @field #table SmokeOwn
-- @field #boolean smokeaveragetargetpos
-- @extends Core.Fsm#FSM
---
@@ -104,7 +105,7 @@ PLAYERRECCE = {
ClassName = "PLAYERRECCE",
verbose = true,
lid = nil,
version = "0.0.22",
version = "0.1.23",
ViewZone = {},
ViewZoneVisual = {},
ViewZoneLaser = {},
@@ -130,8 +131,9 @@ PLAYERRECCE = {
ReferencePoint = nil,
TForget = 600,
TargetCache = nil,
smokeownposition = true,
smokeownposition = false,
SmokeOwn = {},
smokeaveragetargetpos = false,
}
---
@@ -1109,9 +1111,8 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername)
self:T(self.lid.."_SmokeTargets")
local cameraset = self:_GetTargetSet(client,true) -- Core.Set#SET_UNIT
local visualset = self:_GetTargetSet(client,false) -- Core.Set#SET_UNIT
cameraset:AddSet(visualset)
if cameraset:CountAlive() > 0 then
if cameraset:CountAlive() > 0 or visualset:CountAlive() > 0 then
self:__TargetsSmoked(-1,client,playername,cameraset)
else
return self
@@ -1126,29 +1127,31 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername)
-- laser targer gets extra smoke
if laser and laser.Target and laser.Target:IsAlive() then
laser.Target:GetCoordinate():Smoke(lasersmoke)
if cameraset:IsInSet(laser.Target) then
cameraset:Remove(laser.Target:GetName(),true)
end
local coord = visualset:GetCoordinate()
if coord and self.smokeaveragetargetpos then
coord:SetAtLandheight()
coord:Smoke(medsmoke)
else
-- smoke everything
for _,_unit in pairs(visualset.Set) do
local unit = _unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
local coord = unit:GetCoordinate()
local threat = unit:GetThreatLevel()
if coord then
local color = lowsmoke
if threat > 7 then
color = highsmoke
elseif threat > 2 then
color = medsmoke
end
coord:Smoke(color)
end
end
end
end
local coordinate = nil
local setthreat = 0
-- smoke everything else
if cameraset:CountAlive() > 1 then
local coordinate = cameraset:GetCoordinate()
local setthreat = cameraset:CalculateThreatLevelA2G()
end
if coordinate then
local color = lowsmoke
if setthreat > 7 then
color = medsmoke
elseif setthreat > 2 then
color = lowsmoke
end
coordinate:Smoke(color)
end
if self.SmokeOwn[playername] then
local cc = client:GetVec2()
-- don't smoke mid-air
@@ -1189,15 +1192,15 @@ function PLAYERRECCE:_FlareTargets(client,group,playername)
-- smoke everything else
for _,_unit in pairs(cameraset.Set) do
local unit = _unit --Wrapper.Unit#UNIT
if unit then
if unit and unit:IsAlive() then
local coord = unit:GetCoordinate()
local threat = unit:GetThreatLevel()
if coord then
local color = lowsmoke
if threat > 7 then
color = medsmoke
color = highsmoke
elseif threat > 2 then
color = lowsmoke
color = medsmoke
end
coord:Flare(color)
end
@@ -1546,7 +1549,7 @@ end
-- @param #PLAYERRECCE self
-- @return #PLAYERRECCE self
function PLAYERRECCE:EnableSmokeOwnPosition()
self:T(self.lid.."ENableSmokeOwnPosition")
self:T(self.lid.."EnableSmokeOwnPosition")
self.smokeownposition = true
return self
end
@@ -1560,6 +1563,24 @@ function PLAYERRECCE:DisableSmokeOwnPosition()
return self
end
--- [User] Enable smoking of average target positions, instead of all targets visible. Loses smoke per threatlevel -- each is med threat. Default is - smoke all positions.
-- @param #PLAYERRECCE self
-- @return #PLAYERRECCE self
function PLAYERRECCE:EnableSmokeAverageTargetPosition()
self:T(self.lid.."ENableSmokeOwnPosition")
self.smokeaveragetargetpos = true
return self
end
--- [User] Disable smoking of average target positions, instead of all targets visible. Default is - smoke all positions.
-- @param #PLAYERRECCE self
-- @return #PLAYERRECCE
function PLAYERRECCE:DisableSmokeAverageTargetPosition()
self:T(self.lid.."DisableSmokeAverageTargetPosition")
self.smokeaveragetargetpos = false
return self
end
--- [Internal] Get text for text-to-speech.
-- Numbers are spaced out, e.g. "Heading 180" becomes "Heading 1 8 0 ".
-- @param #PLAYERRECCE self

View File

@@ -526,11 +526,11 @@ function TARGET:IsAlive()
for _,_target in pairs(self.targets) do
local target=_target --Ops.Target#TARGET.Object
if target.Status==TARGET.ObjectStatus.ALIVE then
if target.Status~=TARGET.ObjectStatus.DEAD then
return true
end
end
return false
end

File diff suppressed because it is too large Load Diff

View File

@@ -160,52 +160,63 @@ do -- Sound File
-- @param #string FileName The name of the sound file, e.g. "Hello World.ogg".
-- @param #string Path The path of the directory, where the sound file is located. Default is "l10n/DEFAULT/" within the miz file.
-- @param #number Duration Duration in seconds, how long it takes to play the sound file. Default is 3 seconds.
-- @param #bolean UseSrs Set if SRS should be used to play this file. Default is false.
-- @return #SOUNDFILE self
function SOUNDFILE:New(FileName, Path, Duration)
function SOUNDFILE:New(FileName, Path, Duration, UseSrs)
-- Inherit BASE.
local self=BASE:Inherit(self, BASE:New()) -- #SOUNDFILE
-- Debug info:
self:F( {FileName, Path, Duration, UseSrs} )
-- Set file name.
self:SetFileName(FileName)
-- Set if SRS should be used to play this file
self:SetPlayWithSRS(UseSrs or false)
-- Set path.
self:SetPath(Path)
-- Set duration.
self:SetDuration(Duration)
-- Debug info:
self:T(string.format("New SOUNDFILE: file name=%s, path=%s", self.filename, self.path))
return self
end
--- Set path, where the sound file is located.
-- @param #SOUNDFILE self
-- @param #string Path Path to the directory, where the sound file is located. In case this is nil, it defaults to the DCS mission temp directory.
-- @return #SOUNDFILE self
function SOUNDFILE:SetPath(Path)
self:F( {Path} )
-- Init path.
self.path=Path or "l10n/DEFAULT/"
if not Path and self.useSRS then -- use path to mission temp dir
self.path = os.getenv('TMP') .. "\\DCS\\Mission\\l10n\\DEFAULT"
end
if not Path then
if self.useSRS then -- use path to mission temp dir
self.path = lfs.tempdir() .. "Mission\\l10n\\DEFAULT"
else -- use internal path in miz file
self.path="l10n/DEFAULT/"
end
else
self.path = Path
end
-- Remove (back)slashes.
local nmax=1000 ; local n=1
while (self.path:sub(-1)=="/" or self.path:sub(-1)==[[\]]) and n<=nmax do
self.path=self.path:sub(1,#self.path-1)
n=n+1
end
-- Append slash.
self.path=self.path.."/"
self:T("self.path=".. self.path)
return self
end
end
--- Get path of the directory, where the sound file is located.
-- @param #SOUNDFILE self
@@ -228,7 +239,7 @@ do -- Sound File
--- Get the sound file name.
-- @param #SOUNDFILE self
-- @return #string Name of the soud file. This does *not* include its path.
function SOUNDFILE:GetFileName()
function SOUNDFILE:GetFileName()
return self.filename
end
@@ -264,14 +275,16 @@ do -- Sound File
-- @param #boolean Switch If true or nil, use SRS. If false, use DCS transmission.
-- @return #SOUNDFILE self
function SOUNDFILE:SetPlayWithSRS(Switch)
self:F( {Switch} )
if Switch==true or Switch==nil then
self.useSRS=true
else
self.useSRS=false
end
self:T("self.useSRS=".. tostring(self.useSRS))
return self
end
end
end
do -- Text-To-Speech

View File

@@ -137,7 +137,7 @@ do -- UserSound
return self
end
--- Play the usersound to the given @{Wrapper.Unit}.
--- Play the usersound to the given @{Wrapper.Unit}.
-- @param #USERSOUND self
-- @param Wrapper.Unit#UNIT Unit The @{Wrapper.Unit} to play the usersound to.
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
@@ -159,4 +159,24 @@ do -- UserSound
return self
end
--- Play the usersound to the given @{Wrapper.Client}.
-- @param #USERSOUND self
-- @param Wrapper.Client#CLIENT The @{Wrapper.Client} to play the usersound to.
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
-- @return #USERSOUND The usersound instance.
-- @usage
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
-- local PlayerUnit = CLIENT:FindByPlayerName("Karl Heinz")-- Search for the active client with playername "Karl Heinz", a human player.
-- BlueVictory:ToClient( PlayerUnit ) -- Play the victory sound to the player unit.
--
function USERSOUND:ToClient( Client, Delay )
Delay=Delay or 0
if Delay>0 then
SCHEDULER:New(nil, USERSOUND.ToClient,{self, Client}, Delay)
else
trigger.action.outSoundForUnit( Client:GetID(), self.UserSoundFileName )
end
return self
end
end

View File

@@ -523,6 +523,7 @@ ENUMS.ReportingName =
Hawkeye = "E-2D",
Sentry = "E-3A",
Stratotanker = "KC-135",
Gasstation = "KC-135MPRS",
Extender = "KC-10",
Orion = "P-3C",
Viking = "S-3B",

View File

@@ -443,22 +443,35 @@ end
--- Print a table to log in a nice format
-- @param #table table The table to print
-- @param #number indent Number of idents
-- @param #number indent Number of indents
-- @return #string text Text created on the fly of the log output
function UTILS.PrintTableToLog(table, indent)
local text = "\n"
if not table then
env.warning("No table passed!")
return
return nil
end
if not indent then indent = 0 end
for k, v in pairs(table) do
if string.find(k," ") then k='"'..k..'"'end
if type(v) == "table" then
env.info(string.rep(" ", indent) .. tostring(k) .. " = {")
UTILS.PrintTableToLog(v, indent + 1)
env.info(string.rep(" ", indent) .. "}")
text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n"
text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n"
env.info(string.rep(" ", indent) .. "},")
text = text .. string.rep(" ", indent) .. "},\n"
else
env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v))
local value
if tostring(v) == "true" or tostring(v) == "false" or tonumber(v) ~= nil then
value=v
else
value = '"'..tostring(v)..'"'
end
env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n")
text = text .. string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n"
end
end
return text
end
--- Returns table in a easy readable string representation.
@@ -1078,9 +1091,9 @@ function UTILS.BeaufortScale(speed)
return bn,bd
end
--- Split string at seperators. C.f. [split-string-in-lua](http://stackoverflow.com/questions/1426954/split-string-in-lua).
--- Split string at separators. C.f. [split-string-in-lua](http://stackoverflow.com/questions/1426954/split-string-in-lua).
-- @param #string str Sting to split.
-- @param #string sep Speparator for split.
-- @param #string sep Separator for split.
-- @return #table Split text.
function UTILS.Split(str, sep)
local result = {}
@@ -2208,17 +2221,17 @@ function UTILS.IsLoadingDoorOpen( unit_name )
return true
end
if string.find(type_name, "Bell-47") then -- bell aint got no doors so always ready to load injured soldiers
if type_name == "Bell-47" then -- bell aint got no doors so always ready to load injured soldiers
BASE:T(unit_name .. " door is open")
return true
end
if string.find(type_name, "UH-60L") and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then
if type_name == "UH-60L" and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then
BASE:T(unit_name .. " cargo door is open")
return true
end
if string.find(type_name, "UH-60L" ) and (unit:getDrawArgumentValue(38) == 1 or unit:getDrawArgumentValue(400) == 1 ) then
if type_name == "UH-60L" and (unit:getDrawArgumentValue(38) > 0 or unit:getDrawArgumentValue(400) == 1 ) then
BASE:T(unit_name .. " front door(s) are open")
return true
end

View File

@@ -2937,7 +2937,7 @@ function CONTROLLABLE:CopyRoute( Begin, End, Randomize, Radius )
end
--- Return the detected targets of the controllable.
-- The optional parametes specify the detection methods that can be applied.
-- The optional parameters specify the detection methods that can be applied.
-- If no detection method is given, the detection will use all the available methods by default.
-- @param #CONTROLLABLE self
-- @param #boolean DetectVisual (optional)
@@ -3772,54 +3772,66 @@ function CONTROLLABLE:OptionProhibitAfterburner( Prohibit )
return self
end
--- Defines the usage of Electronic Counter Measures by airborne forces. Disables the ability for AI to use their ECM.
--- [Air] Defines the usage of Electronic Counter Measures by airborne forces.
-- @param #CONTROLLABLE self
-- @param #number ECMvalue Can be - 0=Never on, 1=if locked by radar, 2=if detected by radar, 3=always on, defaults to 1
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionECM_Never()
function CONTROLLABLE:OptionECM( ECMvalue )
self:F2( { self.ControllableName } )
if self:IsAir() then
self:SetOption( AI.Option.Air.id.ECM_USING, 0 )
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if self:IsAir() then
Controller:setOption( AI.Option.Air.id.ECM_USING, ECMvalue or 1 )
end
end
return self
end
--- Defines the usage of Electronic Counter Measures by airborne forces. If the AI is actively being locked by an enemy radar they will enable their ECM jammer.
--- [Air] Defines the usage of Electronic Counter Measures by airborne forces. Disables the ability for AI to use their ECM.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionECM_Never()
self:F2( { self.ControllableName } )
self:OptionECM(0)
return self
end
--- [Air] Defines the usage of Electronic Counter Measures by airborne forces. If the AI is actively being locked by an enemy radar they will enable their ECM jammer.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionECM_OnlyLockByRadar()
self:F2( { self.ControllableName } )
if self:IsAir() then
self:SetOption( AI.Option.Air.id.ECM_USING, 1 )
end
self:OptionECM(1)
return self
end
--- Defines the usage of Electronic Counter Measures by airborne forces. If the AI is being detected by a radar they will enable their ECM.
--- [Air] Defines the usage of Electronic Counter Measures by airborne forces. If the AI is being detected by a radar they will enable their ECM.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionECM_DetectedLockByRadar()
self:F2( { self.ControllableName } )
if self:IsAir() then
self:SetOption( AI.Option.Air.id.ECM_USING, 2 )
end
self:OptionECM(2)
return self
end
--- Defines the usage of Electronic Counter Measures by airborne forces. AI will leave their ECM on all the time.
--- [Air] Defines the usage of Electronic Counter Measures by airborne forces. AI will leave their ECM on all the time.
-- @param #CONTROLLABLE self
-- @return #CONTROLLABLE self
function CONTROLLABLE:OptionECM_AlwaysOn()
self:F2( { self.ControllableName } )
if self:IsAir() then
self:SetOption( AI.Option.Air.id.ECM_USING, 3 )
end
self:OptionECM(3)
return self
end
@@ -4013,14 +4025,22 @@ end
-- @param #boolean onroad If true, route on road (less problems with AI way finding), default true
-- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false
-- @param #string formation Formation string as in the mission editor, e.g. "Vee", "Diamond", "Line abreast", etc. Defaults to "Off Road"
-- @param #boolean onland (optional) If true, try up to 50 times to get a coordinate on land.SurfaceType.LAND. Note - this descriptor value is not reliably implemented on all maps.
-- @return #CONTROLLABLE self
function CONTROLLABLE:RelocateGroundRandomInRadius( speed, radius, onroad, shortcut, formation )
function CONTROLLABLE:RelocateGroundRandomInRadius( speed, radius, onroad, shortcut, formation, onland )
self:F2( { self.ControllableName } )
local _coord = self:GetCoordinate()
local _radius = radius or 500
local _speed = speed or 20
local _tocoord = _coord:GetRandomCoordinateInRadius( _radius, 100 )
if onland then
for i=1,50 do
local island = _tocoord:GetSurfaceType() == land.SurfaceType.LAND and true or false
if island then break end
_tocoord = _coord:GetRandomCoordinateInRadius( _radius, 100 )
end
end
local _onroad = onroad or true
local _grptsk = {}
local _candoroad = false

View File

@@ -302,7 +302,7 @@ end
--- Find the first(!) GROUP matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
-- @param #GROUP self
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA.
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA.
-- @return #GROUP The GROUP.
-- @usage
-- -- Find a group with a partial group name
@@ -327,7 +327,7 @@ end
--- Find all GROUP objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
-- @param #GROUP self
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA.
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA.
-- @return #table Groups Table of matching #GROUP objects found
-- @usage
-- -- Find all group with a partial group name
@@ -1177,6 +1177,7 @@ function GROUP:GetAverageCoordinate()
local coord = COORDINATE:NewFromVec3(vec3)
local Heading = self:GetHeading()
coord.Heading = Heading
return coord
else
BASE:E( { "Cannot GetAverageCoordinate", Group = self, Alive = self:IsAlive() } )
return nil
@@ -2782,7 +2783,7 @@ end
--- Switch on/off invisible flag for the group.
-- @param #GROUP self
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
-- @param #boolean switch If true, Invisible is enabled. If false, Invisible is disabled.
-- @return #GROUP self
function GROUP:SetCommandInvisible(switch)
self:F2( self.GroupName )
@@ -2796,7 +2797,7 @@ end
--- Switch on/off immortal flag for the group.
-- @param #GROUP self
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
-- @param #boolean switch If true, Immortal is enabled. If false, Immortal is disabled.
-- @return #GROUP self
function GROUP:SetCommandImmortal(switch)
self:F2( self.GroupName )
@@ -3002,3 +3003,36 @@ function GROUP:GetGroupSTN()
return tSTN,text
end
--- [GROUND] Determine if a GROUP is a SAM unit, i.e. has radar or optical tracker and is no mobile AAA.
-- @param #GROUP self
-- @return #boolean IsSAM True if SAM, else false
function GROUP:IsSAM()
local issam = false
local units = self:GetUnits()
for _,_unit in pairs(units or {}) do
local unit = _unit -- Wrapper.Unit#UNIT
if unit:HasSEAD() and unit:IsGround() and (not unit:HasAttribute("Mobile AAA")) then
issam = true
break
end
end
return issam
end
--- [GROUND] Determine if a GROUP has a AAA unit, i.e. has no radar or optical tracker but the AAA = true or the "Mobile AAA" = true attribute.
-- @param #GROUP self
-- @return #boolean IsSAM True if AAA, else false
function GROUP:IsAAA()
local issam = false
local units = self:GetUnits()
for _,_unit in pairs(units or {}) do
local unit = _unit -- Wrapper.Unit#UNIT
local desc = unit:GetDesc() or {}
local attr = desc.attributes or {}
if unit:HasSEAD() then return false end
if attr["AAA"] or attr["SAM related"] then
issam = true
end
end
return issam
end

View File

@@ -164,7 +164,7 @@ end
--- Find the first(!) UNIT matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
-- @param #UNIT self
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA.
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA.
-- @return #UNIT The UNIT.
-- @usage
-- -- Find a group with a partial group name
@@ -189,7 +189,7 @@ end
--- Find all UNIT objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
-- @param #UNIT self
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA.
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA.
-- @return #table Units Table of matching #UNIT objects found
-- @usage
-- -- Find all group with a partial group name