mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
113 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43ab4d5f38 | ||
|
|
0427c0d3a7 | ||
|
|
014750ea7f | ||
|
|
e4bbfce314 | ||
|
|
359429b17e | ||
|
|
6001f6abda | ||
|
|
fd9b5d8d16 | ||
|
|
b6f184388a | ||
|
|
d8471698ab | ||
|
|
6204cecbbd | ||
|
|
ddeca49916 | ||
|
|
d8dcf37886 | ||
|
|
5ae41a208b | ||
|
|
0462d900e6 | ||
|
|
0f962461e1 | ||
|
|
aadc03c38d | ||
|
|
683fa13bb2 | ||
|
|
e4408a964d | ||
|
|
f97f33ab59 | ||
|
|
f59102ee09 | ||
|
|
e42e4e1ddf | ||
|
|
a30079c45b | ||
|
|
50ffd9aba6 | ||
|
|
936fec1f49 | ||
|
|
9625d87dd5 | ||
|
|
802139205c | ||
|
|
fb918cb2a4 | ||
|
|
abb4de46d7 | ||
|
|
dce9631399 | ||
|
|
6c773786d2 | ||
|
|
8939963187 | ||
|
|
f9747d1c4c | ||
|
|
60a3d3409e | ||
|
|
b72124c0d9 | ||
|
|
d51e761b26 | ||
|
|
1445ef61a0 | ||
|
|
dfe2ed2a98 | ||
|
|
be87103b53 | ||
|
|
2fb460c4bb | ||
|
|
216ea230a8 | ||
|
|
90096163ee | ||
|
|
cd178d6a8c | ||
|
|
2aa0b5ddfb | ||
|
|
37f819458a | ||
|
|
168f4301d2 | ||
|
|
d5a406c60f | ||
|
|
cb16210577 | ||
|
|
8c0e0de45f | ||
|
|
3524cba4ef | ||
|
|
5f8d1cf5b0 | ||
|
|
846aa823d4 | ||
|
|
68298fc585 | ||
|
|
e9d75f6d94 | ||
|
|
8fa5277417 | ||
|
|
231f1f236d | ||
|
|
ece0a46f97 | ||
|
|
2c9b0b8376 | ||
|
|
a798f2d61c | ||
|
|
ae08c87822 | ||
|
|
ae880e9d1c | ||
|
|
101d2e1de5 | ||
|
|
f6b7708567 | ||
|
|
051286acd1 | ||
|
|
7f572a1a9b | ||
|
|
d62025dfe0 | ||
|
|
07009630c6 | ||
|
|
865042a843 | ||
|
|
f00d0dc871 | ||
|
|
d6adcdf8bd | ||
|
|
2c192fba30 | ||
|
|
97f11c93bb | ||
|
|
f531fdaa70 | ||
|
|
81f8e84ca4 | ||
|
|
d74de11b8b | ||
|
|
30f2097d7a | ||
|
|
f903844059 | ||
|
|
68b2b452cc | ||
|
|
668d120d60 | ||
|
|
7543f31c85 | ||
|
|
56d84e7b25 | ||
|
|
ed2d3d856b | ||
|
|
1e5c3a3c21 | ||
|
|
37d5b6a0fc | ||
|
|
c58a954d18 | ||
|
|
0d481afa16 | ||
|
|
016875d724 | ||
|
|
5df0d60135 | ||
|
|
e77d61c4cb | ||
|
|
504142c585 | ||
|
|
80df849d18 | ||
|
|
e74c79b5d6 | ||
|
|
f738ddfca8 | ||
|
|
af45b0d709 | ||
|
|
e746617139 | ||
|
|
3105f7407d | ||
|
|
2f568bca17 | ||
|
|
aeb1664134 | ||
|
|
b7702ab933 | ||
|
|
cc14f82e85 | ||
|
|
5aea17e20e | ||
|
|
ad9eaea010 | ||
|
|
284a770daa | ||
|
|
ccd190a8b1 | ||
|
|
19f6a8d8f6 | ||
|
|
24264bd885 | ||
|
|
872bb3d775 | ||
|
|
d36cd8e284 | ||
|
|
eab643268f | ||
|
|
9356794112 | ||
|
|
35c24810f9 | ||
|
|
a54944b021 | ||
|
|
bc9938d08a | ||
|
|
b77f179acc |
@@ -3895,10 +3895,14 @@ do -- AI_A2G_DISPATCHER
|
||||
|
||||
if Squadron then
|
||||
local FirstUnit = AttackSetUnit:GetRandomSurely()
|
||||
if FirstUnit then
|
||||
local Coordinate = FirstUnit:GetCoordinate() -- Core.Point#COORDINATE
|
||||
if self.SetSendPlayerMessages then
|
||||
Dispatcher:MessageToPlayers( Squadron, DefenderName .. ", on route to ground target at " .. Coordinate:ToStringA2G( DefenderGroup ), DefenderGroup )
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
self:GetParent(self).onafterEngageRoute( self, DefenderGroup, From, Event, To, AttackSetUnit )
|
||||
end
|
||||
@@ -4784,4 +4788,5 @@ end
|
||||
Squadron.ResourceCount = Squadron.ResourceCount - Amount
|
||||
end
|
||||
self:T({Squadron = Squadron.Name,SquadronResourceCount = Squadron.ResourceCount})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
-- @module AI.AI_Air
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
---
|
||||
-- @type AI_AIR
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
-- @module Core.Base
|
||||
-- @image Core_Base.JPG
|
||||
|
||||
local _TraceOnOff = true
|
||||
local _TraceOnOff = false -- default to no tracing
|
||||
local _TraceLevel = 1
|
||||
local _TraceAll = false
|
||||
local _TraceClass = {}
|
||||
@@ -34,11 +34,12 @@ local _TraceClassMethod = {}
|
||||
|
||||
local _ClassID = 0
|
||||
|
||||
---
|
||||
--- Base class of everything
|
||||
-- @type BASE
|
||||
-- @field ClassName The name of the class.
|
||||
-- @field ClassID The ID number of the class.
|
||||
-- @field ClassNameAndID The name of the class concatenated with the ID number of the class.
|
||||
-- @field #string ClassName The name of the class.
|
||||
-- @field #number ClassID The ID number of the class.
|
||||
-- @field #string ClassNameAndID The name of the class concatenated with the ID number of the class.
|
||||
-- @field Core.Scheduler#SCHEDULER Scheduler The scheduler object.
|
||||
|
||||
--- BASE class
|
||||
--
|
||||
@@ -210,14 +211,6 @@ BASE._ = {
|
||||
Schedules = {}, --- Contains the Schedulers Active
|
||||
}
|
||||
|
||||
--- The Formation Class
|
||||
-- @type FORMATION
|
||||
-- @field Cone A cone formation.
|
||||
FORMATION = {
|
||||
Cone = "Cone",
|
||||
Vee = "Vee",
|
||||
}
|
||||
|
||||
--- BASE constructor.
|
||||
--
|
||||
-- This is an example how to use the BASE:New() constructor in a new class definition when inheriting from BASE.
|
||||
@@ -741,7 +734,31 @@ do -- Event Handling
|
||||
-- @function [parent=#BASE] OnEventPlayerEnterAircraft
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
|
||||
--- Occurs when a player creates a dynamic cargo object from the F8 ground crew menu.
|
||||
-- *** NOTE *** this is a workarounf for DCS not creating these events as of Aug 2024.
|
||||
-- @function [parent=#BASE] OnEventNewDynamicCargo
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a player loads a dynamic cargo object with the F8 ground crew menu into a helo.
|
||||
-- *** NOTE *** this is a workarounf for DCS not creating these events as of Aug 2024.
|
||||
-- @function [parent=#BASE] OnEventDynamicCargoLoaded
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a player unloads a dynamic cargo object with the F8 ground crew menu from a helo.
|
||||
-- *** NOTE *** this is a workarounf for DCS not creating these events as of Aug 2024.
|
||||
-- @function [parent=#BASE] OnEventDynamicCargoUnloaded
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
--- Occurs when a dynamic cargo crate is removed.
|
||||
-- *** NOTE *** this is a workarounf for DCS not creating these events as of Aug 2024.
|
||||
-- @function [parent=#BASE] OnEventDynamicCargoRemoved
|
||||
-- @param #BASE self
|
||||
-- @param Core.Event#EVENTDATA EventData The EventData structure.
|
||||
|
||||
end
|
||||
|
||||
--- Creation of a Birth Event.
|
||||
@@ -862,6 +879,62 @@ end
|
||||
|
||||
world.onEvent(Event)
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_NEW_DYNAMIC_CARGO event.
|
||||
-- @param #BASE self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function BASE:CreateEventNewDynamicCargo(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.NewDynamicCargo,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_LOADED event.
|
||||
-- @param #BASE self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function BASE:CreateEventDynamicCargoLoaded(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoLoaded,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_UNLOADED event.
|
||||
-- @param #BASE self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function BASE:CreateEventDynamicCargoUnloaded(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoUnloaded,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_REMOVED event.
|
||||
-- @param #BASE self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function BASE:CreateEventDynamicCargoRemoved(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoRemoved,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- The main event handling function... This function captures all events generated for the class.
|
||||
-- @param #BASE self
|
||||
@@ -1157,6 +1230,15 @@ function BASE:_Serialize(Arguments)
|
||||
return text
|
||||
end
|
||||
|
||||
----- (Internal) Serialize arguments
|
||||
---- @param #BASE self
|
||||
---- @param #table Arguments
|
||||
---- @return #string Text
|
||||
--function BASE:_Serialize(Arguments)
|
||||
-- local text=UTILS.BasicSerialize(Arguments)
|
||||
-- return text
|
||||
--end
|
||||
|
||||
--- Trace a function call. This function is private.
|
||||
-- @param #BASE self
|
||||
-- @param Arguments A #table or any field.
|
||||
@@ -1191,7 +1273,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1206,7 +1288,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F2( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true and _TraceLevel >= 2 then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1221,7 +1303,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F3( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true and _TraceLevel >= 3 then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1265,7 +1347,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1280,7 +1362,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T2( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true and _TraceLevel >= 2 then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1295,7 +1377,7 @@ end
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T3( Arguments )
|
||||
|
||||
if BASE.Debug and _TraceOnOff then
|
||||
if BASE.Debug and _TraceOnOff == true and _TraceLevel >= 3 then
|
||||
local DebugInfoCurrent = BASE.Debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = BASE.Debug.getinfo( 3, "l" )
|
||||
|
||||
@@ -1327,7 +1409,7 @@ function BASE:E( Arguments )
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
|
||||
else
|
||||
env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, BASE:_Serialize(Arguments) ) )
|
||||
env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, UTILS.BasicSerialize(Arguments) ) )
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1354,7 +1436,7 @@ function BASE:I( Arguments )
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
|
||||
else
|
||||
env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, BASE:_Serialize(Arguments)) )
|
||||
env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.BasicSerialize(Arguments)) )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
-- * Manage database of hits to units and statics.
|
||||
-- * Manage database of destroys of units and statics.
|
||||
-- * Manage database of @{Core.Zone#ZONE_BASE} objects.
|
||||
-- * Manage database of @{Wrapper.DynamicCargo#DYNAMICCARGO} objects alive in the mission.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -39,6 +40,7 @@
|
||||
-- @field #table STORAGES DCS warehouse storages.
|
||||
-- @field #table STNS Used Link16 octal numbers for F16/15/18/AWACS planes.
|
||||
-- @field #table SADL Used Link16 octal numbers for A10/C-II planes.
|
||||
-- @field #table DYNAMICCARGO Dynamic Cargo objects.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator.
|
||||
@@ -54,6 +56,7 @@
|
||||
-- * PLAYERS
|
||||
-- * CARGOS
|
||||
-- * STORAGES (DCS warehouses)
|
||||
-- * DYNAMICCARGO
|
||||
--
|
||||
-- On top, for internal MOOSE administration purposes, the DATABASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
|
||||
--
|
||||
@@ -97,6 +100,7 @@ DATABASE = {
|
||||
STORAGES = {},
|
||||
STNS={},
|
||||
SADL={},
|
||||
DYNAMICCARGO={},
|
||||
}
|
||||
|
||||
local _DATABASECoalition =
|
||||
@@ -143,6 +147,8 @@ function DATABASE:New()
|
||||
self:HandleEvent( EVENTS.DeleteZone )
|
||||
--self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event.
|
||||
self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit )
|
||||
-- DCS 2.9.7 Moose own dynamic cargo events
|
||||
self:HandleEvent( EVENTS.DynamicCargoRemoved, self._EventOnDynamicCargoRemoved)
|
||||
|
||||
self:_RegisterTemplates()
|
||||
self:_RegisterGroupsAndUnits()
|
||||
@@ -173,16 +179,20 @@ end
|
||||
-- @param #boolean force
|
||||
-- @return Wrapper.Unit#UNIT The added unit.
|
||||
function DATABASE:AddUnit( DCSUnitName, force )
|
||||
|
||||
if not self.UNITS[DCSUnitName] or force == true then
|
||||
|
||||
local DCSunitName = DCSUnitName
|
||||
|
||||
if type(DCSunitName) == "number" then DCSunitName = string.format("%d",DCSUnitName) end
|
||||
|
||||
if not self.UNITS[DCSunitName] or force == true then
|
||||
-- Debug info.
|
||||
self:T( { "Add UNIT:", DCSUnitName } )
|
||||
self:T( { "Add UNIT:", DCSunitName } )
|
||||
|
||||
-- Register unit
|
||||
self.UNITS[DCSUnitName]=UNIT:Register(DCSUnitName)
|
||||
self.UNITS[DCSunitName]=UNIT:Register(DCSunitName)
|
||||
end
|
||||
|
||||
return self.UNITS[DCSUnitName]
|
||||
return self.UNITS[DCSunitName]
|
||||
end
|
||||
|
||||
|
||||
@@ -201,10 +211,9 @@ function DATABASE:AddStatic( DCSStaticName )
|
||||
|
||||
if not self.STATICS[DCSStaticName] then
|
||||
self.STATICS[DCSStaticName] = STATIC:Register( DCSStaticName )
|
||||
return self.STATICS[DCSStaticName]
|
||||
end
|
||||
|
||||
return nil
|
||||
return self.STATICS[DCSStaticName]
|
||||
end
|
||||
|
||||
|
||||
@@ -214,16 +223,42 @@ function DATABASE:DeleteStatic( DCSStaticName )
|
||||
self.STATICS[DCSStaticName] = nil
|
||||
end
|
||||
|
||||
--- Finds a STATIC based on the StaticName.
|
||||
--- Finds a STATIC based on the Static Name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string StaticName
|
||||
-- @param #string StaticName Name of the static object.
|
||||
-- @return Wrapper.Static#STATIC The found STATIC.
|
||||
function DATABASE:FindStatic( StaticName )
|
||||
|
||||
local StaticFound = self.STATICS[StaticName]
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
--- Add a DynamicCargo to the database.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string Name Name of the dynamic cargo.
|
||||
-- @return Wrapper.DynamicCargo#DYNAMICCARGO The dynamic cargo object.
|
||||
function DATABASE:AddDynamicCargo( Name )
|
||||
if not self.DYNAMICCARGO[Name] then
|
||||
self.DYNAMICCARGO[Name] = DYNAMICCARGO:Register(Name)
|
||||
end
|
||||
return self.DYNAMICCARGO[Name]
|
||||
end
|
||||
|
||||
--- Finds a DYNAMICCARGO based on the Dynamic Cargo Name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string DynamicCargoName
|
||||
-- @return Wrapper.DynamicCargo#DYNAMICCARGO The found DYNAMICCARGO.
|
||||
function DATABASE:FindDynamicCargo( DynamicCargoName )
|
||||
local StaticFound = self.DYNAMICCARGO[DynamicCargoName]
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
--- Deletes a DYNAMICCARGO from the DATABASE based on the Dynamic Cargo Name.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:DeleteDynamicCargo( DynamicCargoName )
|
||||
self.DYNAMICCARGO[DynamicCargoName] = nil
|
||||
return self
|
||||
end
|
||||
|
||||
--- Adds a Airbase based on the Airbase Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName The name of the airbase.
|
||||
@@ -818,12 +853,16 @@ end
|
||||
-- @param #boolean Force (optional) Force registration of client.
|
||||
-- @return Wrapper.Client#CLIENT The client object.
|
||||
function DATABASE:AddClient( ClientName, Force )
|
||||
|
||||
if not self.CLIENTS[ClientName] or Force == true then
|
||||
self.CLIENTS[ClientName] = CLIENT:Register( ClientName )
|
||||
|
||||
local DCSUnitName = ClientName
|
||||
|
||||
if type(DCSUnitName) == "number" then DCSUnitName = string.format("%d",ClientName) end
|
||||
|
||||
if not self.CLIENTS[DCSUnitName] or Force == true then
|
||||
self.CLIENTS[DCSUnitName] = CLIENT:Register( DCSUnitName )
|
||||
end
|
||||
|
||||
return self.CLIENTS[ClientName]
|
||||
return self.CLIENTS[DCSUnitName]
|
||||
end
|
||||
|
||||
|
||||
@@ -863,9 +902,11 @@ end
|
||||
--- Adds a player based on the Player Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:AddPlayer( UnitName, PlayerName )
|
||||
|
||||
|
||||
if type(UnitName) == "number" then UnitName = string.format("%d",UnitName) end
|
||||
|
||||
if PlayerName then
|
||||
self:T( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self:I( { "Add player for unit:", UnitName, PlayerName } )
|
||||
self.PLAYERS[PlayerName] = UnitName
|
||||
self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName )
|
||||
self.PLAYERSJOINED[PlayerName] = PlayerName
|
||||
@@ -873,6 +914,21 @@ function DATABASE:AddPlayer( UnitName, PlayerName )
|
||||
|
||||
end
|
||||
|
||||
--- Get a PlayerName by UnitName from PLAYERS in DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @return #string PlayerName
|
||||
-- @return Wrapper.Unit#UNIT PlayerUnit
|
||||
function DATABASE:_FindPlayerNameByUnitName(UnitName)
|
||||
if UnitName then
|
||||
for playername,unitname in pairs(self.PLAYERS) do
|
||||
if unitname == UnitName and self.PLAYERUNITS[playername] and self.PLAYERUNITS[playername]:IsAlive() then
|
||||
return playername, self.PLAYERUNITS[playername]
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Deletes a player from the DATABASE based on the Player Name.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:DeletePlayer( UnitName, PlayerName )
|
||||
@@ -1244,7 +1300,7 @@ function DATABASE:_GetGenericStaticCargoGroupTemplate(Name,Typename,Mass,Coaliti
|
||||
StaticTemplate.CategoryID = "static"
|
||||
StaticTemplate.CoalitionID = Coalition or coalition.side.BLUE
|
||||
StaticTemplate.CountryID = Country or country.id.GERMANY
|
||||
UTILS.PrintTableToLog(StaticTemplate)
|
||||
--UTILS.PrintTableToLog(StaticTemplate)
|
||||
return StaticTemplate
|
||||
end
|
||||
|
||||
@@ -1324,7 +1380,7 @@ function DATABASE:GetCoalitionFromClientTemplate( ClientName )
|
||||
if self.Templates.ClientsByName[ClientName] then
|
||||
return self.Templates.ClientsByName[ClientName].CoalitionID
|
||||
end
|
||||
self:E("ERROR: Template does not exist for client "..tostring(ClientName))
|
||||
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1336,7 +1392,7 @@ function DATABASE:GetCategoryFromClientTemplate( ClientName )
|
||||
if self.Templates.ClientsByName[ClientName] then
|
||||
return self.Templates.ClientsByName[ClientName].CategoryID
|
||||
end
|
||||
self:E("ERROR: Template does not exist for client "..tostring(ClientName))
|
||||
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1348,7 +1404,7 @@ function DATABASE:GetCountryFromClientTemplate( ClientName )
|
||||
if self.Templates.ClientsByName[ClientName] then
|
||||
return self.Templates.ClientsByName[ClientName].CountryID
|
||||
end
|
||||
self:E("ERROR: Template does not exist for client "..tostring(ClientName))
|
||||
self:E("WARNING: Template does not exist for client "..tostring(ClientName))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1417,7 +1473,7 @@ function DATABASE:_RegisterDynamicGroup(Groupname)
|
||||
|
||||
-- Add unit.
|
||||
self:I(string.format("Register Unit: %s", tostring(DCSUnitName)))
|
||||
self:AddUnit( DCSUnitName, true )
|
||||
self:AddUnit( tostring(DCSUnitName), true )
|
||||
|
||||
end
|
||||
else
|
||||
@@ -1523,12 +1579,29 @@ end
|
||||
-- @param DCS#Airbase airbase Airbase.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterAirbase(airbase)
|
||||
|
||||
|
||||
local IsSyria = UTILS.GetDCSMap() == "Syria" and true or false
|
||||
local countHSyria = 0
|
||||
|
||||
if airbase then
|
||||
|
||||
-- Get the airbase name.
|
||||
local DCSAirbaseName = airbase:getName()
|
||||
|
||||
|
||||
-- DCS 2.9.8.1107 added 143 helipads all named H with the same object ID ..
|
||||
if IsSyria and DCSAirbaseName == "H" and countHSyria > 0 then
|
||||
--[[
|
||||
local p = airbase:getPosition().p
|
||||
local mgrs = COORDINATE:New(p.x,p.z,p.y):ToStringMGRS()
|
||||
self:I("Airbase on Syria map named H @ "..mgrs)
|
||||
countHSyria = countHSyria + 1
|
||||
if countHSyria > 1 then return self end
|
||||
--]]
|
||||
return self
|
||||
elseif IsSyria and DCSAirbaseName == "H" and countHSyria == 0 then
|
||||
countHSyria = countHSyria + 1
|
||||
end
|
||||
|
||||
-- This gave the incorrect value to be inserted into the airdromeID for DCS 2.5.6. Is fixed now.
|
||||
local airbaseID=airbase:getID()
|
||||
|
||||
@@ -1568,7 +1641,7 @@ end
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnBirth( Event )
|
||||
self:F( { Event } )
|
||||
self:T( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
@@ -1576,7 +1649,17 @@ function DATABASE:_EventOnBirth( Event )
|
||||
|
||||
-- Add static object to DB.
|
||||
self:AddStatic( Event.IniDCSUnitName )
|
||||
|
||||
elseif Event.IniObjectCategory == Object.Category.CARGO and string.match(Event.IniUnitName,".+|%d%d:%d%d|PKG%d+") then
|
||||
|
||||
-- Add dynamic cargo object to DB
|
||||
|
||||
local cargo = self:AddDynamicCargo(Event.IniDCSUnitName)
|
||||
|
||||
self:I(string.format("Adding dynamic cargo %s", tostring(Event.IniDCSUnitName)))
|
||||
|
||||
self:CreateEventNewDynamicCargo( cargo )
|
||||
|
||||
else
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
@@ -1762,6 +1845,15 @@ function DATABASE:_EventOnPlayerEnterUnit( Event )
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnDynamicCargoRemoved event to clean the active dynamic cargo table.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnDynamicCargoRemoved( Event )
|
||||
self:T( { Event } )
|
||||
if Event.IniDynamicCargoName then
|
||||
self:DeleteDynamicCargo(Event.IniDynamicCargoName)
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnPlayerLeaveUnit event to clean the active players table.
|
||||
-- @param #DATABASE self
|
||||
|
||||
@@ -194,6 +194,11 @@ world.event.S_EVENT_NEW_ZONE_GOAL = world.event.S_EVENT_MAX + 1004
|
||||
world.event.S_EVENT_DELETE_ZONE_GOAL = world.event.S_EVENT_MAX + 1005
|
||||
world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1006
|
||||
world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT = world.event.S_EVENT_MAX + 1007
|
||||
-- dynamic cargo
|
||||
world.event.S_EVENT_NEW_DYNAMIC_CARGO = world.event.S_EVENT_MAX + 1008
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_LOADED = world.event.S_EVENT_MAX + 1009
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_UNLOADED = world.event.S_EVENT_MAX + 1010
|
||||
world.event.S_EVENT_DYNAMIC_CARGO_REMOVED = world.event.S_EVENT_MAX + 1011
|
||||
|
||||
|
||||
--- The different types of events supported by MOOSE.
|
||||
@@ -275,7 +280,12 @@ EVENTS = {
|
||||
SimulationFreeze = world.event.S_EVENT_SIMULATION_FREEZE or -1,
|
||||
SimulationUnfreeze = world.event.S_EVENT_SIMULATION_UNFREEZE or -1,
|
||||
HumanAircraftRepairStart = world.event.S_EVENT_HUMAN_AIRCRAFT_REPAIR_START or -1,
|
||||
HumanAircraftRepairFinish = world.event.S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH or -1,
|
||||
HumanAircraftRepairFinish = world.event.S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH or -1,
|
||||
-- dynamic cargo
|
||||
NewDynamicCargo = world.event.S_EVENT_NEW_DYNAMIC_CARGO or -1,
|
||||
DynamicCargoLoaded = world.event.S_EVENT_DYNAMIC_CARGO_LOADED or -1,
|
||||
DynamicCargoUnloaded = world.event.S_EVENT_DYNAMIC_CARGO_UNLOADED or -1,
|
||||
DynamicCargoRemoved = world.event.S_EVENT_DYNAMIC_CARGO_REMOVED or -1,
|
||||
}
|
||||
|
||||
|
||||
@@ -334,6 +344,9 @@ EVENTS = {
|
||||
--
|
||||
-- @field Core.Zone#ZONE Zone The zone object.
|
||||
-- @field #string ZoneName The name of the zone.
|
||||
--
|
||||
-- @field Wrapper.DynamicCargo#DYNAMICCARGO IniDynamicCargo The dynamic cargo object.
|
||||
-- @field #string IniDynamicCargoName The dynamic cargo unit name.
|
||||
|
||||
|
||||
|
||||
@@ -730,6 +743,31 @@ local _EVENTMETA = {
|
||||
Side = "I",
|
||||
Event = "OnEventHumanAircraftRepairFinish",
|
||||
Text = "S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH"
|
||||
},
|
||||
-- dynamic cargo
|
||||
[EVENTS.NewDynamicCargo] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventNewDynamicCargo",
|
||||
Text = "S_EVENT_NEW_DYNAMIC_CARGO"
|
||||
},
|
||||
[EVENTS.DynamicCargoLoaded] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventDynamicCargoLoaded",
|
||||
Text = "S_EVENT_DYNAMIC_CARGO_LOADED"
|
||||
},
|
||||
[EVENTS.DynamicCargoUnloaded] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventDynamicCargoUnloaded",
|
||||
Text = "S_EVENT_DYNAMIC_CARGO_UNLOADED"
|
||||
},
|
||||
[EVENTS.DynamicCargoRemoved] = {
|
||||
Order = 1,
|
||||
Side = "I",
|
||||
Event = "OnEventDynamicCargoRemoved",
|
||||
Text = "S_EVENT_DYNAMIC_CARGO_REMOVED"
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1146,7 +1184,63 @@ do -- Event Creation
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Creation of a S_EVENT_NEW_DYNAMIC_CARGO event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function EVENT:CreateEventNewDynamicCargo(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.NewDynamicCargo,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_LOADED event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function EVENT:CreateEventDynamicCargoLoaded(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoLoaded,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_UNLOADED event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function EVENT:CreateEventDynamicCargoUnloaded(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoUnloaded,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a S_EVENT_DYNAMIC_CARGO_REMOVED event.
|
||||
-- @param #EVENT self
|
||||
-- @param Wrapper.DynamicCargo#DYNAMICCARGO DynamicCargo the dynamic cargo object
|
||||
function EVENT:CreateEventDynamicCargoRemoved(DynamicCargo)
|
||||
self:F({DynamicCargo})
|
||||
local Event = {
|
||||
id = EVENTS.DynamicCargoRemoved,
|
||||
time = timer.getTime(),
|
||||
dynamiccargo = DynamicCargo,
|
||||
initiator = DynamicCargo:GetDCSObject(),
|
||||
}
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Main event function.
|
||||
@@ -1235,6 +1329,7 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
|
||||
Event.IniDCSGroupName = Event.IniUnit and Event.IniUnit.GroupName or ""
|
||||
Event.IniGroupName=Event.IniDCSGroupName --At least set the group name because group might not exist any more
|
||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
@@ -1261,7 +1356,13 @@ function EVENT:onEvent( Event )
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
|
||||
if string.match(Event.IniUnitName,".+|%d%d:%d%d|PKG%d+") then
|
||||
Event.IniDynamicCargo = DYNAMICCARGO:FindByName(Event.IniUnitName)
|
||||
Event.IniDynamicCargoName = Event.IniUnitName
|
||||
Event.IniPlayerName = string.match(Event.IniUnitName,"^(.+)|%d%d:%d%d|PKG%d+")
|
||||
else
|
||||
Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName )
|
||||
end
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
@@ -1271,7 +1372,7 @@ function EVENT:onEvent( Event )
|
||||
-- Scenery
|
||||
---
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit.getName and Event.IniDCSUnit:getName() or "Scenery no name "..math.random(1,20000)
|
||||
Event.IniUnitName = Event.IniDCSUnitName
|
||||
Event.IniUnit = SCENERY:Register( Event.IniDCSUnitName, Event.initiator )
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
@@ -1373,16 +1474,18 @@ function EVENT:onEvent( Event )
|
||||
-- SCENERY
|
||||
---
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit.getName and Event.TgtDCSUnit.getName() or nil
|
||||
if Event.TgtDCSUnitName~=nil then
|
||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||
Event.TgtUnit = SCENERY:Register( Event.TgtDCSUnitName, Event.target )
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Weapon.
|
||||
if Event.weapon and type(Event.weapon) == "table" then
|
||||
if Event.weapon and type(Event.weapon) == "table" and Event.weapon.isExist and Event.weapon:isExist() then
|
||||
Event.Weapon = Event.weapon
|
||||
Event.WeaponName = Event.weapon:isExist() and Event.weapon:getTypeName() or "Unknown Weapon"
|
||||
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
||||
@@ -1425,6 +1528,15 @@ function EVENT:onEvent( Event )
|
||||
Event.Cargo = Event.cargo
|
||||
Event.CargoName = Event.cargo.Name
|
||||
end
|
||||
|
||||
-- Dynamic cargo Object
|
||||
if Event.dynamiccargo then
|
||||
Event.IniDynamicCargo = Event.dynamiccargo
|
||||
Event.IniDynamicCargoName = Event.IniDynamicCargo.StaticName
|
||||
if Event.IniDynamicCargo.Owner or Event.IniUnitName then
|
||||
Event.IniPlayerName = Event.IniDynamicCargo.Owner or string.match(Event.IniUnitName or "None|00:00|PKG00","^(.+)|%d%d:%d%d|PKG%d+")
|
||||
end
|
||||
end
|
||||
|
||||
-- Zone object.
|
||||
if Event.zone then
|
||||
|
||||
@@ -108,26 +108,30 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
|
||||
--- On after "MarkAdded" event. Triggered when a Marker is added to the F10 map.
|
||||
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkAdded
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @param #string From The From state
|
||||
-- @param #string Event The Event called
|
||||
-- @param #string To The To state
|
||||
-- @param #string Text The text on the marker
|
||||
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||
-- @param #string From The From state.
|
||||
-- @param #string Event The Event called.
|
||||
-- @param #string To The To state.
|
||||
-- @param #string Text The text on the marker.
|
||||
-- @param #table Keywords Table of matching keywords found in the Event text.
|
||||
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||
-- @param #number MarkerID Id of this marker
|
||||
-- @param #number CoalitionNumber Coalition of the marker creator
|
||||
-- @param #number MarkerID Id of this marker.
|
||||
-- @param #number CoalitionNumber Coalition of the marker creator.
|
||||
-- @param #string PlayerName Name of the player creating/changing the mark. nil if it cannot be obtained.
|
||||
-- @param Core.Event#EVENTDATA EventData the event data table.
|
||||
|
||||
--- On after "MarkChanged" event. Triggered when a Marker is changed on the F10 map.
|
||||
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkChanged
|
||||
-- @param #MARKEROPS_BASE self
|
||||
-- @param #string From The From state
|
||||
-- @param #string Event The Event called
|
||||
-- @param #string To The To state
|
||||
-- @param #string Text The text on the marker
|
||||
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||
-- @param #string From The From state.
|
||||
-- @param #string Event The Event called.
|
||||
-- @param #string To The To state.
|
||||
-- @param #string Text The text on the marker.
|
||||
-- @param #table Keywords Table of matching keywords found in the Event text.
|
||||
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||
-- @param #number MarkerID Id of this marker
|
||||
-- @param #number CoalitionNumber Coalition of the marker creator
|
||||
-- @param #number MarkerID Id of this marker.
|
||||
-- @param #number CoalitionNumber Coalition of the marker creator.
|
||||
-- @param #string PlayerName Name of the player creating/changing the mark. nil if it cannot be obtained.
|
||||
-- @param Core.Event#EVENTDATA EventData the event data table
|
||||
|
||||
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
|
||||
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
|
||||
@@ -167,7 +171,7 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
||||
if Eventtext~=nil then
|
||||
if self:_MatchTag(Eventtext) then
|
||||
local matchtable = self:_MatchKeywords(Eventtext)
|
||||
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition)
|
||||
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
|
||||
end
|
||||
end
|
||||
elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
|
||||
@@ -177,7 +181,7 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
||||
if Eventtext~=nil then
|
||||
if self:_MatchTag(Eventtext) then
|
||||
local matchtable = self:_MatchKeywords(Eventtext)
|
||||
self:MarkChanged(Eventtext,matchtable,coord,Event.idx,coalition)
|
||||
self:MarkChanged(Eventtext,matchtable,coord,Event.idx,coalition,Event.PlayerName,Event)
|
||||
end
|
||||
end
|
||||
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
|
||||
|
||||
@@ -75,35 +75,37 @@ MESSAGE.Type = {
|
||||
|
||||
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{#MESSAGE.ToClient} or @{#MESSAGE.ToCoalition} or @{#MESSAGE.ToAll} to send these Messages to the respective recipients.
|
||||
-- @param self
|
||||
-- @param #string MessageText is the text of the Message.
|
||||
-- @param #number MessageDuration is a number in seconds of how long the MESSAGE should be shown on the display panel.
|
||||
-- @param #string MessageCategory (optional) is a string expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ".
|
||||
-- @param #string Text is the text of the Message.
|
||||
-- @param #number Duration Duration in seconds how long the message text is shown.
|
||||
-- @param #string Category (Optional) String expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ".
|
||||
-- @param #boolean ClearScreen (optional) Clear all previous messages if true.
|
||||
-- @return #MESSAGE
|
||||
-- @return #MESSAGE self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Create a series of new Messages.
|
||||
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
|
||||
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- -- Create a series of new Messages.
|
||||
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
|
||||
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission" )
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" )
|
||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" )
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score")
|
||||
--
|
||||
function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen )
|
||||
function MESSAGE:New( Text, Duration, Category, ClearScreen )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText, MessageDuration, MessageCategory } )
|
||||
|
||||
self:F( { Text, Duration, Category } )
|
||||
|
||||
self.MessageType = nil
|
||||
|
||||
-- When no MessageCategory is given, we don't show it as a title...
|
||||
if MessageCategory and MessageCategory ~= "" then
|
||||
if MessageCategory:sub( -1 ) ~= "\n" then
|
||||
self.MessageCategory = MessageCategory .. ": "
|
||||
if Category and Category ~= "" then
|
||||
if Category:sub( -1 ) ~= "\n" then
|
||||
self.MessageCategory = Category .. ": "
|
||||
else
|
||||
self.MessageCategory = MessageCategory:sub( 1, -2 ) .. ":\n"
|
||||
self.MessageCategory = Category:sub( 1, -2 ) .. ":\n"
|
||||
end
|
||||
else
|
||||
self.MessageCategory = ""
|
||||
@@ -114,9 +116,9 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen
|
||||
self.ClearScreen = ClearScreen
|
||||
end
|
||||
|
||||
self.MessageDuration = MessageDuration or 5
|
||||
self.MessageDuration = Duration or 5
|
||||
self.MessageTime = timer.getTime()
|
||||
self.MessageText = MessageText:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||
self.MessageText = Text:gsub( "^\n", "", 1 ):gsub( "\n$", "", 1 )
|
||||
|
||||
self.MessageSent = false
|
||||
self.MessageGroup = false
|
||||
@@ -177,7 +179,7 @@ end
|
||||
--
|
||||
-- -- Send the 2 messages created with the @{New} method to the Client Group.
|
||||
-- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1.
|
||||
-- Client = CLIENT:FindByName("UnitNameOfMyClient")
|
||||
-- Client = CLIENT:FindByName("NameOfClientUnit")
|
||||
--
|
||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" ):ToClient( Client )
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score" ):ToClient( Client )
|
||||
@@ -192,7 +194,7 @@ end
|
||||
--
|
||||
function MESSAGE:ToClient( Client, Settings )
|
||||
self:F( Client )
|
||||
self:ToUnit(Client, Settings)
|
||||
self:ToUnit(Client,Settings)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -239,6 +241,7 @@ function MESSAGE:ToUnit( Unit, Settings )
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
local ID = Unit:GetID()
|
||||
trigger.action.outTextForUnit( Unit:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen )
|
||||
end
|
||||
end
|
||||
@@ -377,7 +380,8 @@ end
|
||||
--- Sends a MESSAGE to all players.
|
||||
-- @param #MESSAGE self
|
||||
-- @param Core.Settings#Settings Settings (Optional) Settings for message display.
|
||||
-- @return #MESSAGE
|
||||
-- @param #number Delay (Optional) Delay in seconds before the message is send. Default instantly (`nil`).
|
||||
-- @return #MESSAGE self
|
||||
-- @usage
|
||||
--
|
||||
-- -- Send a message created to all players.
|
||||
@@ -388,18 +392,24 @@ end
|
||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", 25, "End of Mission")
|
||||
-- MessageAll:ToAll()
|
||||
--
|
||||
function MESSAGE:ToAll( Settings )
|
||||
function MESSAGE:ToAll( Settings, Delay )
|
||||
self:F()
|
||||
|
||||
if self.MessageType then
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MESSAGE.ToAll, self, Settings, 0)
|
||||
else
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||
if self.MessageType then
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
@@ -2669,9 +2669,9 @@ do -- COORDINATE
|
||||
local date=UTILS.GetDCSMissionDate()
|
||||
|
||||
-- Debug output.
|
||||
--self:I(string.format("Sun rise at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%d sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), sunrise, Tdiff))
|
||||
--self:I(string.format("Sun rise at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%s sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), tonumber(sunrise) or "0", Tdiff))
|
||||
|
||||
if InSeconds then
|
||||
if InSeconds or type(sunrise) == "string" then
|
||||
return sunrise
|
||||
else
|
||||
return UTILS.SecondsToClock(sunrise, true)
|
||||
@@ -2837,9 +2837,9 @@ do -- COORDINATE
|
||||
local date=UTILS.GetDCSMissionDate()
|
||||
|
||||
-- Debug output.
|
||||
--self:I(string.format("Sun set at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%d sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), sunrise, Tdiff))
|
||||
--self:I(string.format("Sun set at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%s sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), tostring(sunrise) or "0", Tdiff))
|
||||
|
||||
if InSeconds then
|
||||
if InSeconds or type(sunrise) == "string" then
|
||||
return sunrise
|
||||
else
|
||||
return UTILS.SecondsToClock(sunrise, true)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -29,7 +29,9 @@
|
||||
-- @module Core.Settings
|
||||
-- @image Core_Settings.JPG
|
||||
|
||||
--- @type SETTINGS
|
||||
|
||||
---
|
||||
-- @type SETTINGS
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Takes care of various settings that influence the behavior of certain functionalities and classes within the MOOSE framework.
|
||||
@@ -218,7 +220,8 @@ SETTINGS = {
|
||||
|
||||
SETTINGS.__Enum = {}
|
||||
|
||||
--- @type SETTINGS.__Enum.Era
|
||||
---
|
||||
-- @type SETTINGS.__Enum.Era
|
||||
-- @field #number WWII
|
||||
-- @field #number Korea
|
||||
-- @field #number Cold
|
||||
@@ -491,7 +494,7 @@ do -- SETTINGS
|
||||
return (self.A2ASystem and self.A2ASystem == "MGRS") or (not self.A2ASystem and _SETTINGS:IsA2A_MGRS())
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
-- @param Wrapper.Group#GROUP MenuGroup Group for which to add menus.
|
||||
-- @param #table RootMenu Root menu table
|
||||
-- @return #SETTINGS
|
||||
@@ -945,49 +948,49 @@ do -- SETTINGS
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
|
||||
self.A2GSystem = A2GSystem
|
||||
MESSAGE:New( string.format( "Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
|
||||
self.A2ASystem = A2ASystem
|
||||
MESSAGE:New( string.format( "Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
|
||||
self.LL_Accuracy = LL_Accuracy
|
||||
MESSAGE:New( string.format( "Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
|
||||
self.MGRS_Accuracy = MGRS_Accuracy
|
||||
MESSAGE:New( string.format( "Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
|
||||
self.Metric = MW
|
||||
MESSAGE:New( string.format( "Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuMessageTimingsSystem( MenuGroup, RootMenu, MessageType, MessageTime )
|
||||
self:SetMessageTime( MessageType, MessageTime )
|
||||
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToAll()
|
||||
end
|
||||
|
||||
do
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
|
||||
--BASE:E( {PlayerUnit:GetName(), A2GSystem } )
|
||||
self.A2GSystem = A2GSystem
|
||||
@@ -998,7 +1001,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
|
||||
self.A2ASystem = A2ASystem
|
||||
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
@@ -1008,7 +1011,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
|
||||
self.LL_Accuracy = LL_Accuracy
|
||||
MESSAGE:New( string.format( "Settings: LL format accuracy set to %d decimal places for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
@@ -1018,7 +1021,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
|
||||
self.MGRS_Accuracy = MGRS_Accuracy
|
||||
MESSAGE:New( string.format( "Settings: MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
@@ -1028,7 +1031,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
|
||||
self.Metric = MW
|
||||
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
@@ -1038,7 +1041,7 @@ do -- SETTINGS
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMessageTimingsSystem( PlayerUnit, PlayerGroup, PlayerName, MessageType, MessageTime )
|
||||
self:SetMessageTime( MessageType, MessageTime )
|
||||
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToGroup( PlayerGroup )
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -535,12 +535,6 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
-- Name of the spawned static.
|
||||
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
|
||||
|
||||
-- Add and register the new static.
|
||||
local mystatic=_DATABASE:AddStatic(Template.name)
|
||||
|
||||
-- Debug output.
|
||||
self:T(Template)
|
||||
|
||||
-- Add static to the game.
|
||||
local Static=nil --DCS#StaticObject
|
||||
|
||||
@@ -577,12 +571,28 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
|
||||
self:T("Spawning Static")
|
||||
self:T2({Template=Template})
|
||||
Static=coalition.addStaticObject(CountryID, Template)
|
||||
|
||||
if Static then
|
||||
self:T(string.format("Succesfully spawned static object \"%s\" ID=%d", Static:getName(), Static:getID()))
|
||||
--[[
|
||||
local static=StaticObject.getByName(Static:getName())
|
||||
if static then
|
||||
env.info(string.format("FF got static from StaticObject.getByName"))
|
||||
else
|
||||
env.error(string.format("FF error did NOT get static from StaticObject.getByName"))
|
||||
end ]]
|
||||
else
|
||||
self:E(string.format("ERROR: DCS static object \"%s\" is nil!", tostring(Template.name)))
|
||||
end
|
||||
end
|
||||
|
||||
-- Add and register the new static.
|
||||
local mystatic=_DATABASE:AddStatic(Template.name)
|
||||
|
||||
-- If there is a SpawnFunction hook defined, call it.
|
||||
if self.SpawnFunctionHook then
|
||||
-- delay calling this for .3 seconds so that it hopefully comes after the BIRTH event of the group.
|
||||
self:ScheduleOnce(0.3,self.SpawnFunctionHook,mystatic, unpack(self.SpawnFunctionArguments))
|
||||
self:ScheduleOnce(0.3, self.SpawnFunctionHook, mystatic, unpack(self.SpawnFunctionArguments))
|
||||
end
|
||||
|
||||
return mystatic
|
||||
|
||||
@@ -3641,7 +3641,7 @@ do -- ZONE_ELASTIC
|
||||
end
|
||||
|
||||
|
||||
--- Create a convec hull.
|
||||
--- Create a convex hull.
|
||||
-- @param #ZONE_ELASTIC self
|
||||
-- @param #table pl Points
|
||||
-- @return #table Points
|
||||
|
||||
@@ -14,6 +14,7 @@ do -- world
|
||||
-- @field #world.event event [https://wiki.hoggitworld.com/view/DCS_enum_world](https://wiki.hoggitworld.com/view/DCS_enum_world)
|
||||
-- @field #world.BirthPlace BirthPlace The birthplace enumerator is used to define where an aircraft or helicopter has spawned in association with birth events.
|
||||
-- @field #world.VolumeType VolumeType The volumeType enumerator defines the types of 3d geometery used within the [world.searchObjects](https://wiki.hoggitworld.com/view/DCS_func_searchObjects) function.
|
||||
-- @field #world.weather weather Weather functions for fog etc.
|
||||
|
||||
--- The world singleton contains functions centered around two different but extremely useful functions.
|
||||
-- * Events and event handlers are all governed within world.
|
||||
@@ -132,6 +133,36 @@ do -- world
|
||||
-- @function [parent=#world] getAirbases
|
||||
-- @param #number coalitionId The coalition side number ID. Default is all airbases are returned.
|
||||
-- @return #table Table of DCS airbase objects.
|
||||
|
||||
|
||||
--- Weather functions.
|
||||
-- @type world.weather
|
||||
|
||||
--- Fog animation data structure.
|
||||
-- @type world.FogAnimation
|
||||
-- @field #number time
|
||||
-- @field #number visibility
|
||||
-- @field #number thickness
|
||||
|
||||
--- Returns the current fog thickness.
|
||||
-- @function [parent=#world.weather] getFogThickness Returns the fog thickness.
|
||||
-- @return #number Fog thickness in meters. If there is no fog, zero is returned.
|
||||
|
||||
--- Sets the fog thickness instantly. Any current fog animation is discarded.
|
||||
-- @function [parent=#world.weather] setFogThickness
|
||||
-- @param #number thickness Fog thickness in meters. Set to zero to disable fog.
|
||||
|
||||
--- Returns the current fog visibility distance.
|
||||
-- @function [parent=#world.weather] getFogVisibilityDistance Returns the current maximum visibility distance in meters. Returns zero if fog is not present.
|
||||
|
||||
--- Instantly sets the maximum visibility distance of fog at sea level when looking at the horizon. Any current fog animation is discarded. Set zero to disable the fog.
|
||||
-- @function [parent=#world.weather] setFogVisibilityDistance
|
||||
-- @param #number visibility Max fog visibility in meters. Set to zero to disable fog.
|
||||
|
||||
--- Sets fog animation keys. Time is set in seconds and relative to the current simulation time, where time=0 is the current moment.
|
||||
-- Time must be increasing. Previous animation is always discarded despite the data being correct.
|
||||
-- @function [parent=#world.weather] setFogAnimation
|
||||
-- @param #world.FogAnimation animation List of fog animations
|
||||
|
||||
end -- world
|
||||
|
||||
@@ -407,7 +438,7 @@ do -- coalition
|
||||
-- @param #table groupData Group data table.
|
||||
-- @return DCS#Group The spawned Group object.
|
||||
|
||||
--- Dynamically spawns a static object. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_addGroup)
|
||||
--- Dynamically spawns a static object. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_addStaticObject)
|
||||
-- @function [parent=#coalition] addStaticObject
|
||||
-- @param #number countryId Id of the country.
|
||||
-- @param #table groupData Group data table.
|
||||
@@ -420,6 +451,7 @@ end -- coalition
|
||||
|
||||
do -- Types
|
||||
|
||||
--- Descriptors.
|
||||
-- @type Desc
|
||||
-- @field #number speedMax0 Max speed in meters/second at zero altitude.
|
||||
-- @field #number massEmpty Empty mass in kg.
|
||||
@@ -1013,14 +1045,16 @@ do -- Spot
|
||||
end -- Spot
|
||||
|
||||
do -- Controller
|
||||
|
||||
--- Controller is an object that performs A.I.-tasks. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C.
|
||||
--
|
||||
-- This class has 2 types of functions:
|
||||
--
|
||||
-- * Tasks
|
||||
-- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics.
|
||||
-- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics.
|
||||
--
|
||||
-- @type Controller
|
||||
-- @field #Controller.Detection Detection Enum contains identifiers of surface types.
|
||||
-- @field #Controller.Detection Detection Enum contains identifiers of surface types.
|
||||
|
||||
--- Enables and disables the controller.
|
||||
-- Note: Now it works only for ground / naval groups!
|
||||
@@ -1079,18 +1113,18 @@ do -- Controller
|
||||
|
||||
-- Detection
|
||||
|
||||
--- Enum contains identifiers of surface types.
|
||||
--- Enum containing detection types.
|
||||
-- @type Controller.Detection
|
||||
-- @field VISUAL
|
||||
-- @field OPTIC
|
||||
-- @field RADAR
|
||||
-- @field IRST
|
||||
-- @field RWR
|
||||
-- @field DLINK
|
||||
-- @field #number VISUAL Visual detection. Numeric value 1.
|
||||
-- @field #number OPTIC Optical detection. Numeric value 2.
|
||||
-- @field #number RADAR Radar detection. Numeric value 4.
|
||||
-- @field #number IRST Infra-red search and track detection. Numeric value 8.
|
||||
-- @field #number RWR Radar Warning Receiver detection. Numeric value 16.
|
||||
-- @field #number DLINK Data link detection. Numeric value 32.
|
||||
|
||||
--- Detected target.
|
||||
-- @type DetectedTarget
|
||||
-- @field Wrapper.Object#Object object The target
|
||||
-- @type Controller.DetectedTarget
|
||||
-- @field DCS#Object object The target
|
||||
-- @field #boolean visible The target is visible
|
||||
-- @field #boolean type The target type is known
|
||||
-- @field #boolean distance Distance to the target is known
|
||||
@@ -1103,9 +1137,9 @@ do -- Controller
|
||||
-- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN
|
||||
-- @return #boolean detected True if the target is detected.
|
||||
-- @return #boolean visible Has effect only if detected is true. True if the target is visible now.
|
||||
-- @return #boolean type Has effect only if detected is true. True if the target type is known.
|
||||
-- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known.
|
||||
-- @return #ModelTime lastTime Has effect only if visible is false. Last time when target was seen.
|
||||
-- @return #boolean type Has effect only if detected is true. True if the target type is known.
|
||||
-- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known.
|
||||
-- @return #Vec3 lastPos Has effect only if visible is false. Last position of the target when it was seen.
|
||||
-- @return #Vec3 lastVel Has effect only if visible is false. Last velocity of the target when it was seen.
|
||||
|
||||
@@ -1131,6 +1165,7 @@ end -- Controller
|
||||
|
||||
do -- Unit
|
||||
|
||||
--- Unit.
|
||||
-- @type Unit
|
||||
-- @extends #CoalitionObject
|
||||
-- @field ID Identifier of an unit. It assigned to an unit by the Mission Editor automatically.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
-- ### Author: FlightControl - Framework Design & Programming
|
||||
-- ### Refactoring to use the Runway auto-detection: Applevangelist
|
||||
-- @date August 2022
|
||||
-- Last Update Nov 2023
|
||||
-- Last Update Oct 2024
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -721,14 +721,18 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
|
||||
|
||||
if NotInRunwayZone then
|
||||
|
||||
local Taxi = Client:GetState( self, "Taxi" )
|
||||
|
||||
if IsOnGround then
|
||||
local Taxi = Client:GetState( self, "Taxi" )
|
||||
|
||||
self:T( Taxi )
|
||||
if Taxi == false then
|
||||
local Velocity = VELOCITY:New( AirbaseMeta.KickSpeed or self.KickSpeed )
|
||||
Client:Message( "Welcome to " .. AirbaseID .. ". The maximum taxiing speed is " ..
|
||||
Velocity:ToString() , 20, "ATC" )
|
||||
Client:SetState( self, "Taxi", true )
|
||||
Client:SetState( self, "Speeding", false )
|
||||
Client:SetState( self, "Warnings", 0 )
|
||||
end
|
||||
|
||||
-- TODO: GetVelocityKMH function usage
|
||||
@@ -737,7 +741,7 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
|
||||
local IsAboveRunway = Client:IsAboveRunway()
|
||||
self:T( {IsAboveRunway, IsOnGround, Velocity:Get() })
|
||||
|
||||
if IsOnGround then
|
||||
if IsOnGround and not Taxi then
|
||||
local Speeding = false
|
||||
if AirbaseMeta.MaximumKickSpeed then
|
||||
if Velocity:Get() > AirbaseMeta.MaximumKickSpeed then
|
||||
@@ -749,15 +753,17 @@ function ATC_GROUND_UNIVERSAL:_AirbaseMonitor()
|
||||
end
|
||||
end
|
||||
if Speeding == true then
|
||||
MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() ..
|
||||
" has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
|
||||
Client:Destroy()
|
||||
Client:SetState( self, "Speeding", false )
|
||||
Client:SetState( self, "Warnings", 0 )
|
||||
--MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() ..
|
||||
-- " has been kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll()
|
||||
--Client:Destroy()
|
||||
Client:SetState( self, "Speeding", true )
|
||||
local SpeedingWarnings = Client:GetState( self, "Warnings" )
|
||||
Client:SetState( self, "Warnings", SpeedingWarnings + 1 )
|
||||
Client:Message( "Warning " .. SpeedingWarnings .. "/3! Airbase traffic rule violation! Slow down now! Your speed is " ..
|
||||
Velocity:ToString(), 5, "ATC" )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
if IsOnGround then
|
||||
|
||||
local Speeding = false
|
||||
@@ -1035,23 +1041,23 @@ end
|
||||
-- The following airbases are monitored at the Nevada region.
|
||||
-- Use the @{Wrapper.Airbase#AIRBASE.Nevada} enumeration to select the airbases to be monitored.
|
||||
--
|
||||
-- * `AIRBASE.Nevada.Beatty_Airport`
|
||||
-- * `AIRBASE.Nevada.Boulder_City_Airport`
|
||||
-- * `AIRBASE.Nevada.Creech_AFB`
|
||||
-- * `AIRBASE.Nevada.Beatty`
|
||||
-- * `AIRBASE.Nevada.Boulder_City`
|
||||
-- * `AIRBASE.Nevada.Creech`
|
||||
-- * `AIRBASE.Nevada.Echo_Bay`
|
||||
-- * `AIRBASE.Nevada.Groom_Lake_AFB`
|
||||
-- * `AIRBASE.Nevada.Henderson_Executive_Airport`
|
||||
-- * `AIRBASE.Nevada.Jean_Airport`
|
||||
-- * `AIRBASE.Nevada.Laughlin_Airport`
|
||||
-- * `AIRBASE.Nevada.Groom_Lake`
|
||||
-- * `AIRBASE.Nevada.Henderson_Executive`
|
||||
-- * `AIRBASE.Nevada.Jean`
|
||||
-- * `AIRBASE.Nevada.Laughlin`
|
||||
-- * `AIRBASE.Nevada.Lincoln_County`
|
||||
-- * `AIRBASE.Nevada.McCarran_International_Airport`
|
||||
-- * `AIRBASE.Nevada.McCarran_International`
|
||||
-- * `AIRBASE.Nevada.Mesquite`
|
||||
-- * `AIRBASE.Nevada.Mina_Airport`
|
||||
-- * `AIRBASE.Nevada.Nellis_AFB`
|
||||
-- * `AIRBASE.Nevada.Mina`
|
||||
-- * `AIRBASE.Nevada.Nellis`
|
||||
-- * `AIRBASE.Nevada.North_Las_Vegas`
|
||||
-- * `AIRBASE.Nevada.Pahute_Mesa_Airstrip`
|
||||
-- * `AIRBASE.Nevada.Tonopah_Airport`
|
||||
-- * `AIRBASE.Nevada.Tonopah_Test_Range_Airfield`
|
||||
-- * `AIRBASE.Nevada.Pahute_Mesa`
|
||||
-- * `AIRBASE.Nevada.Tonopah`
|
||||
-- * `AIRBASE.Nevada.Tonopah_Test_Range`
|
||||
--
|
||||
-- # Installation
|
||||
--
|
||||
@@ -1088,10 +1094,10 @@ end
|
||||
--
|
||||
-- -- Monitor specific airbases.
|
||||
-- ATC_Ground = ATC_GROUND_NEVADA:New(
|
||||
-- { AIRBASE.Nevada.Laughlin_Airport,
|
||||
-- { AIRBASE.Nevada.Laughlin,
|
||||
-- AIRBASE.Nevada.Lincoln_County,
|
||||
-- AIRBASE.Nevada.North_Las_Vegas,
|
||||
-- AIRBASE.Nevada.McCarran_International_Airport
|
||||
-- AIRBASE.Nevada.McCarran_International
|
||||
-- }
|
||||
-- )
|
||||
--
|
||||
@@ -1330,33 +1336,33 @@ end
|
||||
-- The following airbases are monitored at the PersianGulf region.
|
||||
-- Use the @{Wrapper.Airbase#AIRBASE.PersianGulf} enumeration to select the airbases to be monitored.
|
||||
--
|
||||
-- * `AIRBASE.PersianGulf.Abu_Musa_Island_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Al_Dhafra_AB`
|
||||
-- * `AIRBASE.PersianGulf.Abu_Musa_Island`
|
||||
-- * `AIRBASE.PersianGulf.Al_Dhafra_AFB`
|
||||
-- * `AIRBASE.PersianGulf.Al_Maktoum_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Al_Minhad_AB`
|
||||
-- * `AIRBASE.PersianGulf.Al_Minhad_AFB`
|
||||
-- * `AIRBASE.PersianGulf.Bandar_Abbas_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Bandar_Lengeh`
|
||||
-- * `AIRBASE.PersianGulf.Dubai_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Fujairah_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Havadarya`
|
||||
-- * `AIRBASE.PersianGulf.Kerman_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Kerman`
|
||||
-- * `AIRBASE.PersianGulf.Khasab`
|
||||
-- * `AIRBASE.PersianGulf.Lar_Airbase`
|
||||
-- * `AIRBASE.PersianGulf.Lar`
|
||||
-- * `AIRBASE.PersianGulf.Qeshm_Island`
|
||||
-- * `AIRBASE.PersianGulf.Sharjah_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Shiraz_International_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Shiraz_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Sir_Abu_Nuayr`
|
||||
-- * `AIRBASE.PersianGulf.Sirri_Island`
|
||||
-- * `AIRBASE.PersianGulf.Tunb_Island_AFB`
|
||||
-- * `AIRBASE.PersianGulf.Tunb_Kochak`
|
||||
-- * `AIRBASE.PersianGulf.Sas_Al_Nakheel_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Bandar_e_Jask_airfield`
|
||||
-- * `AIRBASE.PersianGulf.Abu_Dhabi_International_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Al_Bateen_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Kish_International_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Al_Ain_International_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Lavan_Island_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Jiroft_Airport`
|
||||
-- * `AIRBASE.PersianGulf.Sas_Al_Nakheel`
|
||||
-- * `AIRBASE.PersianGulf.Bandar_e_Jask`
|
||||
-- * `AIRBASE.PersianGulf.Abu_Dhabi_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Al_Bateen`
|
||||
-- * `AIRBASE.PersianGulf.Kish_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Al_Ain_Intl`
|
||||
-- * `AIRBASE.PersianGulf.Lavan_Island`
|
||||
-- * `AIRBASE.PersianGulf.Jiroft`
|
||||
--
|
||||
-- # Installation
|
||||
--
|
||||
@@ -1391,8 +1397,8 @@ end
|
||||
-- AirbasePoliceCaucasus = ATC_GROUND_PERSIANGULF:New()
|
||||
--
|
||||
-- ATC_Ground = ATC_GROUND_PERSIANGULF:New(
|
||||
-- { AIRBASE.PersianGulf.Kerman_Airport,
|
||||
-- AIRBASE.PersianGulf.Al_Minhad_AB
|
||||
-- { AIRBASE.PersianGulf.Kerman,
|
||||
-- AIRBASE.PersianGulf.Al_Minhad_AFB
|
||||
-- }
|
||||
-- )
|
||||
--
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #boolean Debug Write Debug messages to DCS log file and send Debug messages to all players.
|
||||
-- @field #string lid String for DCS log file.
|
||||
-- @field #number FilterCoalition If not nil, will only activate for aircraft of the given coalition value.
|
||||
-- @field #number FilterCategory If not nil, will only activate for aircraft of the given category value.
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
--- Manage and track client slots easily to add your own client-based menus and modules to.
|
||||
@@ -66,7 +68,7 @@
|
||||
--
|
||||
-- -- Create an instance with a client unit prefix and send them a message when they spawn
|
||||
-- local clientInstance = CLIENTWATCH:New("Rotary")
|
||||
-- function clientInstance:OnAfterSpawn(From,Event,To,ClientObject)
|
||||
-- function clientInstance:OnAfterSpawn(From,Event,To,ClientObject,EventData)
|
||||
-- MESSAGE:New("Welcome to your aircraft!",10):ToUnit(ClientObject.Unit)
|
||||
-- end
|
||||
--
|
||||
@@ -108,7 +110,7 @@
|
||||
--
|
||||
-- -- Show a message to player when they take damage from a weapon
|
||||
-- local clientInstance = CLIENTWATCH:New("Rotary")
|
||||
-- function clientInstance:OnAfterSpawn(From,Event,To,ClientObject)
|
||||
-- function clientInstance:OnAfterSpawn(From,Event,To,ClientObject,EventData)
|
||||
-- function ClientObject:OnAfterHit(From,Event,To,EventData)
|
||||
-- local typeShooter = EventData.IniTypeName
|
||||
-- local nameWeapon = EventData.weapon_name
|
||||
@@ -120,6 +122,7 @@
|
||||
CLIENTWATCH = {}
|
||||
CLIENTWATCH.ClassName = "CLIENTWATCH"
|
||||
CLIENTWATCH.Debug = false
|
||||
CLIENTWATCH.DebugEventData = false
|
||||
CLIENTWATCH.lid = nil
|
||||
|
||||
-- @type CLIENTWATCHTools
|
||||
@@ -139,7 +142,10 @@ CLIENTWATCH.version="1.0.1"
|
||||
|
||||
--- Creates a new instance of CLIENTWATCH to add scripts to. Can be used multiple times with the same client/prefixes if you need it for multiple scripts.
|
||||
-- @param #CLIENTWATCH self
|
||||
-- @param #string, #table, or Wrapper.Client#CLIENT client Takes multiple inputs. If provided a #string, it will watch for clients whos UNIT NAME or GROUP NAME matches part of the #string as a prefix. You can also provide it with a #table containing multiple #string prefixes. Lastly, you can provide it with a Wrapper.Client#CLIENT of the specific client you want to apply this to.
|
||||
-- @param #string Will watch for clients whos UNIT NAME or GROUP NAME matches part of the #string as a prefix.
|
||||
-- @param #table Put strings in a table to use multiple prefixes for the above method.
|
||||
-- @param Wrapper.Client#CLIENT Provide a Moose CLIENT object to apply to that specific aircraft slot (static slots only!)
|
||||
-- @param #nil Leave blank to activate for ALL CLIENTS
|
||||
-- @return #CLIENTWATCH self
|
||||
function CLIENTWATCH:New(client)
|
||||
--Init FSM
|
||||
@@ -147,6 +153,9 @@ function CLIENTWATCH:New(client)
|
||||
self:SetStartState( "Idle" )
|
||||
self:AddTransition( "*", "Spawn", "*" )
|
||||
|
||||
self.FilterCoalition = nil
|
||||
self.FilterCategory = nil
|
||||
|
||||
--- User function for OnAfter "Spawn" event.
|
||||
-- @function [parent=#CLIENTWATCH] OnAfterSpawn
|
||||
-- @param #CLIENTWATCH self
|
||||
@@ -155,27 +164,50 @@ function CLIENTWATCH:New(client)
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
-- @param #table clientObject Custom object that handles events and stores Moose object data. See top documentation for more details.
|
||||
-- @param #table eventdata Data from EVENTS.Birth.
|
||||
|
||||
--Set up spawn tracking
|
||||
if type(client) == "table" or type(client) == "string" then
|
||||
if not client then
|
||||
if self.Debug then self:I({"New client instance created. ClientType = All clients"}) end
|
||||
self:HandleEvent(EVENTS.Birth)
|
||||
function self:OnEventBirth(eventdata)
|
||||
if (eventdata.IniCategory == 0 or eventdata.IniCategory == 1) and eventdata.IniPlayerName
|
||||
and (not self.FilterCoalition or self.FilterCoalition == eventdata.IniCoalition)
|
||||
and (not self.FilterCategory or self.FilterCategory == eventdata.IniCategory) then
|
||||
if self.Debug then
|
||||
self:I({"Client spawned in.",IniCategory = eventdata.IniCategory})
|
||||
end
|
||||
local clientWatchDebug = self.Debug
|
||||
local clientObject = CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
self:Spawn(clientObject,eventdata)
|
||||
end
|
||||
end
|
||||
elseif type(client) == "table" or type(client) == "string" then
|
||||
if type(client) == "table" then
|
||||
|
||||
--CLIENT TABLE
|
||||
if client.ClassName == "CLIENT" then
|
||||
if self.Debug then self:I({"New client instance created. ClientType = Wrapper.CLIENT",client}) end
|
||||
self.ClientName = client:GetName()
|
||||
self:HandleEvent(EVENTS.Birth)
|
||||
function self:OnEventBirth(eventdata)
|
||||
if self.Debug then UTILS.PrintTableToLog(eventdata) end
|
||||
if eventdata.IniCategory and eventdata.IniCategory <= 1 then
|
||||
if (eventdata.IniCategory == 0 or eventdata.IniCategory == 1) and eventdata.IniPlayerName
|
||||
and (not self.FilterCoalition or self.FilterCoalition == eventdata.IniCoalition)
|
||||
and (not self.FilterCategory or self.FilterCategory == eventdata.IniCategory) then
|
||||
if self.ClientName == eventdata.IniUnitName then
|
||||
local clientObject = CLIENTWATCHTools:_newClient(eventdata)
|
||||
self:Spawn(clientObject)
|
||||
if self.Debug then
|
||||
self:I({"Client spawned in.",IniCategory = eventdata.IniCategory})
|
||||
end
|
||||
local clientWatchDebug = self.Debug
|
||||
local clientObject = CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
self:Spawn(clientObject,eventdata)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--STRING TABLE
|
||||
else
|
||||
if self.Debug then self:I({"New client instance created. ClientType = Multiple Prefixes",client}) end
|
||||
local tableValid = true
|
||||
for _,entry in pairs(client) do
|
||||
if type(entry) ~= "string" then
|
||||
@@ -187,12 +219,17 @@ function CLIENTWATCH:New(client)
|
||||
if tableValid then
|
||||
self:HandleEvent(EVENTS.Birth)
|
||||
function self:OnEventBirth(eventdata)
|
||||
if self.Debug then UTILS.PrintTableToLog(eventdata) end
|
||||
for _,entry in pairs(client) do
|
||||
if eventdata.IniCategory and eventdata.IniCategory <= 1 then
|
||||
if (eventdata.IniCategory == 0 or eventdata.IniCategory == 1) and eventdata.IniPlayerName
|
||||
and (not self.FilterCoalition or self.FilterCoalition == eventdata.IniCoalition)
|
||||
and (not self.FilterCategory or self.FilterCategory == eventdata.IniCategory) then
|
||||
if string.match(eventdata.IniUnitName,entry) or string.match(eventdata.IniGroupName,entry) then
|
||||
local clientObject = CLIENTWATCHTools:_newClient(eventdata)
|
||||
self:Spawn(clientObject)
|
||||
if self.Debug then
|
||||
self:I({"Client spawned in.",IniCategory = eventdata.IniCategory})
|
||||
end
|
||||
local clientWatchDebug = self.Debug
|
||||
local clientObject = CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
self:Spawn(clientObject,eventdata)
|
||||
break
|
||||
end
|
||||
end
|
||||
@@ -201,15 +238,21 @@ function CLIENTWATCH:New(client)
|
||||
end
|
||||
end
|
||||
else
|
||||
if self.Debug then self:I({"New client instance created. ClientType = Single Prefix",client}) end
|
||||
|
||||
--SOLO STRING
|
||||
self:HandleEvent(EVENTS.Birth)
|
||||
function self:OnEventBirth(eventdata)
|
||||
if self.Debug then UTILS.PrintTableToLog(eventdata) end
|
||||
if eventdata.IniCategory and eventdata.IniCategory <= 1 then
|
||||
if (eventdata.IniCategory == 0 or eventdata.IniCategory == 1) and eventdata.IniPlayerName
|
||||
and (not self.FilterCoalition or self.FilterCoalition == eventdata.IniCoalition)
|
||||
and (not self.FilterCategory or self.FilterCategory == eventdata.IniCategory) then
|
||||
if string.match(eventdata.IniUnitName,client) or string.match(eventdata.IniGroupName,client) then
|
||||
local clientObject = CLIENTWATCHTools:_newClient(eventdata)
|
||||
self:Spawn(clientObject)
|
||||
if self.Debug then
|
||||
self:I({"Client spawned in.",IniCategory = eventdata.IniCategory})
|
||||
end
|
||||
local clientWatchDebug = self.Debug
|
||||
local clientObject = CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
self:Spawn(clientObject,eventdata)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -222,13 +265,41 @@ function CLIENTWATCH:New(client)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Filter out all clients not belonging to the provided coalition
|
||||
-- @param #CLIENTWATCH self
|
||||
-- @param #number Coalition number (1 = red, 2 = blue)
|
||||
-- @param #string Coalition string ('red' or 'blue')
|
||||
function CLIENTWATCH:FilterByCoalition(value)
|
||||
if value == 1 or value == "red" then
|
||||
self.FilterCoalition = 1
|
||||
else
|
||||
self.FilterCoalition = 2
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Filter out all clients that are not of the given category
|
||||
-- @param #CLIENTWATCH self
|
||||
-- @param #number Category number (0 = airplane, 1 = helicopter)
|
||||
-- @param #string Category string ('airplane' or 'helicopter')
|
||||
function CLIENTWATCH:FilterByCategory(value)
|
||||
if value == 1 or value == "helicopter" then
|
||||
self.FilterCategory = 1
|
||||
else
|
||||
self.FilterCategory = 0
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Internal function for creating a new client on birth. Do not use!!!.
|
||||
-- @param #CLIENTWATCHTools self
|
||||
-- @param #EVENTS.Birth EventData
|
||||
-- @return #CLIENTWATCHTools self
|
||||
function CLIENTWATCHTools:_newClient(eventdata)
|
||||
function CLIENTWATCHTools:_newClient(clientWatchDebug,eventdata)
|
||||
--Init FSM
|
||||
local self=BASE:Inherit(self, FSM:New())
|
||||
self:SetStartState( "Alive" )
|
||||
@@ -299,78 +370,130 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventHit(EventData)
|
||||
if EventData.TgtUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered hit event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Hit(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventKill(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered kill event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Kill(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventScore(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered score event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Score(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventShot(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered shot event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Shot(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventShootingStart(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered shooting start event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:ShootingStart(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventShootingEnd(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered shooting end event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:ShootingEnd(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventLand(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered land event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Land(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventTakeoff(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered takeoff event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Takeoff(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventRunwayTakeoff(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered runway takeoff event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:RunwayTakeoff(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventRunwayTouch(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered runway touch event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:RunwayTouch(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventRefueling(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered refueling event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Refueling(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventRefuelingStop(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered refueling event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:RefuelingStop(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventPlayerLeaveUnit(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered leave unit event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:PlayerLeaveUnit(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -378,6 +501,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventCrash(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered crash event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Crash(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -385,6 +512,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventDead(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered dead event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Dead(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -392,6 +523,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventPilotDead(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered pilot dead event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:PilotDead(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -399,6 +534,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventUnitLost(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered unit lost event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:UnitLost(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -406,6 +545,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventEjection(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered ejection event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:Ejection(EventData)
|
||||
self._deadRoutine()
|
||||
end
|
||||
@@ -413,6 +556,10 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventHumanFailure(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered human failure event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:HumanFailure(EventData)
|
||||
if not self.Unit:IsAlive() then
|
||||
self._deadRoutine()
|
||||
@@ -422,42 +569,70 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
function self:OnEventHumanAircraftRepairFinish(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered repair finished event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:HumanAircraftRepairFinish(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventHumanAircraftRepairStart(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered repair start event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:HumanAircraftRepairStart(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventEngineShutdown(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered engine shutdown event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:EngineShutdown(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventEngineStartup(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered engine startup event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:EngineStartup(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventWeaponAdd(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered weapon add event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:WeaponAdd(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventWeaponDrop(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered weapon drop event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:WeaponDrop(EventData)
|
||||
end
|
||||
end
|
||||
|
||||
function self:OnEventWeaponRearm(EventData)
|
||||
if EventData.IniUnitName == self.UnitName then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client triggered weapon rearm event.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
|
||||
end
|
||||
self:WeaponRearm(EventData)
|
||||
end
|
||||
end
|
||||
@@ -466,6 +641,9 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
--Fallback timer
|
||||
self.FallbackTimer = TIMER:New(function()
|
||||
if not self.Unit:IsAlive() then
|
||||
if clientWatchDebug then
|
||||
self:I({"Client is registered as dead without an event trigger. Running fallback dead routine.",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName})
|
||||
end
|
||||
self._deadRoutine()
|
||||
end
|
||||
end)
|
||||
@@ -473,6 +651,7 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
|
||||
--Stop event handlers and trigger Despawn
|
||||
function self._deadRoutine()
|
||||
if clientWatchDebug then self:I({"Client dead routine triggered. Shutting down tracking...",Player = self.PlayerName,Group = self.GroupName,Unit = self.UnitName}) end
|
||||
self:UnHandleEvent( EVENTS.Hit )
|
||||
self:UnHandleEvent( EVENTS.Kill )
|
||||
self:UnHandleEvent( EVENTS.Score )
|
||||
@@ -503,6 +682,6 @@ function CLIENTWATCHTools:_newClient(eventdata)
|
||||
self:Despawn()
|
||||
end
|
||||
|
||||
self:I({"CLIENT SPAWN EVENT", PlayerName = self.PlayerName, UnitName = self.UnitName, GroupName = self.GroupName})
|
||||
self:I({"Detected client spawn and applied internal functions and events.", PlayerName = self.PlayerName, UnitName = self.UnitName, GroupName = self.GroupName})
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -595,7 +595,8 @@ do -- DETECTION_BASE
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #DETECTION_BASE self
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
@@ -615,7 +616,7 @@ do -- DETECTION_BASE
|
||||
self:T( { "DetectionGroup is Alive", Detection:GetName() } )
|
||||
|
||||
local DetectionGroupName = Detection:GetName()
|
||||
local DetectionUnit = Detection:GetUnit( 1 )
|
||||
local DetectionUnit = Detection:GetFirstUnitAlive()
|
||||
|
||||
local DetectedUnits = {}
|
||||
|
||||
@@ -632,26 +633,26 @@ do -- DETECTION_BASE
|
||||
--self:T(UTILS.PrintTableToLog(DetectedTargets))
|
||||
|
||||
|
||||
for DetectionObjectID, Detection in pairs( DetectedTargets ) do
|
||||
for DetectionObjectID, Detection in pairs( DetectedTargets or {}) do
|
||||
local DetectedObject = Detection.object -- DCS#Object
|
||||
|
||||
if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then -- and ( DetectedObject:getCategory() == Object.Category.UNIT or DetectedObject:getCategory() == Object.Category.STATIC ) then
|
||||
local DetectedObjectName = DetectedObject:getName()
|
||||
if not self.DetectedObjects[DetectedObjectName] then
|
||||
self.DetectedObjects[DetectedObjectName] = self.DetectedObjects[DetectedObjectName] or {}
|
||||
self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName
|
||||
self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName
|
||||
self.DetectedObjects[DetectedObjectName].Object = DetectedObject
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for DetectionObjectName, DetectedObjectData in pairs( self.DetectedObjects ) do
|
||||
for DetectionObjectName, DetectedObjectData in pairs( self.DetectedObjects or {}) do
|
||||
|
||||
local DetectedObject = DetectedObjectData.Object
|
||||
|
||||
if DetectedObject:isExist() then
|
||||
|
||||
local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected(
|
||||
local TargetIsDetected, TargetIsVisible, TargetKnowType, TargetKnowDistance, TargetLastTime, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected(
|
||||
DetectedObject,
|
||||
self.DetectVisual,
|
||||
self.DetectOptical,
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
-- @module Functional.Mantis
|
||||
-- @image Functional.Mantis.jpg
|
||||
--
|
||||
-- Last Update: July 2024
|
||||
-- Last Update: Sep 2024
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **MANTIS** class, extends Core.Base#BASE
|
||||
@@ -439,27 +439,50 @@ MANTIS.SamDataSMA = {
|
||||
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
|
||||
-- @field #string Radar Radar typename on unit level (used as key)
|
||||
MANTIS.SamDataCH = {
|
||||
-- units from CH (Military Assets by Currenthill)
|
||||
-- https://www.currenthill.com/
|
||||
-- group name MUST contain CHM to ID launcher type correctly!
|
||||
["2S38 CH"] = { Range=8, Blindspot=0.5, Height=6, Type="Short", Radar="2S38" },
|
||||
["PantsirS1 CH"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" },
|
||||
["PantsirS2 CH"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
|
||||
["PGL-625 CH"] = { Range=10, Blindspot=0.5, Height=5, Type="Short", Radar="PGL_625" },
|
||||
["HQ-17A CH"] = { Range=20, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
|
||||
["M903PAC2 CH"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
|
||||
["M903PAC3 CH"] = { Range=120, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
|
||||
["TorM2 CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" },
|
||||
["TorM2K CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" },
|
||||
["TorM2M CH"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" },
|
||||
["NASAMS3-AMRAAMER CH"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
|
||||
["NASAMS3-AIM9X2 CH"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
|
||||
["C-RAM CH"] = { Range=2, Blindspot=0, Height=2, Type="Short", Radar="CH_Centurion_C_RAM" },
|
||||
["PGZ-09 CH"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="CH_PGZ09" },
|
||||
["S350-9M100 CH"] = { Range=15, Blindspot=1.5, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" },
|
||||
["S350-9M96D CH"] = { Range=150, Blindspot=2.5, Height=30, Type="Long", Radar="CH_S350_50P6_9M96D" },
|
||||
["LAV-AD CH"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_LAVAD" },
|
||||
["HQ-22 CH"] = { Range=170, Blindspot=5, Height=27, Type="Long", Radar="CH_HQ22_LN" },
|
||||
-- units from CH (Military Assets by Currenthill)
|
||||
-- https://www.currenthill.com/
|
||||
-- group name MUST contain CHM to ID launcher type correctly!
|
||||
["2S38 CHM"] = { Range=8, Blindspot=0.5, Height=6, Type="Short", Radar="2S38" },
|
||||
["PantsirS1 CHM"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" },
|
||||
["PantsirS2 CHM"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
|
||||
["PGL-625 CHM"] = { Range=10, Blindspot=0.5, Height=5, Type="Short", Radar="PGL_625" },
|
||||
["HQ-17A CHM"] = { Range=20, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
|
||||
["M903PAC2 CHM"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
|
||||
["M903PAC3 CHM"] = { Range=120, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
|
||||
["TorM2 CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" },
|
||||
["TorM2K CHM"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" },
|
||||
["TorM2M CHM"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" },
|
||||
["NASAMS3-AMRAAMER CHM"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
|
||||
["NASAMS3-AIM9X2 CHM"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
|
||||
["C-RAM CHM"] = { Range=2, Blindspot=0, Height=2, Type="Short", Radar="CH_Centurion_C_RAM" },
|
||||
["PGZ-09 CHM"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="CH_PGZ09" },
|
||||
["S350-9M100 CHM"] = { Range=15, Blindspot=1.5, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" },
|
||||
["S350-9M96D CHM"] = { Range=150, Blindspot=2.5, Height=30, Type="Long", Radar="CH_S350_50P6_9M96D" },
|
||||
["LAV-AD CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_LAVAD" },
|
||||
["HQ-22 CHM"] = { Range=170, Blindspot=5, Height=27, Type="Long", Radar="CH_HQ22_LN" },
|
||||
["PGZ-95 CHM"] = { Range=2, Blindspot=0, Height=2, Type="Short", Radar="CH_PGZ95" },
|
||||
["LD-3000 CHM"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="CH_LD3000_stationary" },
|
||||
["LD-3000M CHM"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="CH_LD3000" },
|
||||
["FlaRakRad CHM"] = { Range=8, Blindspot=1.5, Height=6, Type="Short", Radar="HQ17A" },
|
||||
["IRIS-T SLM CHM"] = { Range=40, Blindspot=0.5, Height=20, Type="Medium", Radar="CH_IRIST_SLM" },
|
||||
["M903PAC2KAT1 CHM"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="CH_MIM104_M903_PAC2_KAT1" },
|
||||
["Skynex CHM"] = { Range=3.5, Blindspot=0, Height=3.5, Type="Short", Radar="CH_SkynexHX" },
|
||||
["Skyshield CHM"] = { Range=3.5, Blindspot=0, Height=3.5, Type="Short", Radar="CH_Skyshield_Gun" },
|
||||
["WieselOzelot CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_Wiesel2Ozelot" },
|
||||
["BukM3-9M317M CHM"] = { Range=70, Blindspot=0.25, Height=35, Type="Medium", Radar="CH_BukM3_9A317M" },
|
||||
["BukM3-9M317MA CHM"] = { Range=70, Blindspot=0.25, Height=35, Type="Medium", Radar="CH_BukM3_9A317MA" },
|
||||
["SkySabre CHM"] = { Range=30, Blindspot=0.5, Height=10, Type="Medium", Radar="CH_SkySabreLN" },
|
||||
["Stormer CHM"] = { Range=7.5, Blindspot=0.3, Height=7, Type="Short", Radar="CH_StormerHVM" },
|
||||
["THAAD CHM"] = { Range=200, Blindspot=40, Height=150, Type="Long", Radar="CH_THAAD_M1120" },
|
||||
["USInfantryFIM92K CHM"] = { Range=8, Blindspot=0.2, Height=4.8, Type="Short", Radar="CH_USInfantry_FIM92" },
|
||||
["RBS98M CHM"] = { Range=20, Blindspot=0, Height=8, Type="Short", Radar="RBS-98" },
|
||||
["RBS70 CHM"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-70" },
|
||||
["RBS90 CHM"] = { Range=8, Blindspot=0, Height=5.5, Type="Short", Radar="RBS-90" },
|
||||
["RBS103A CHM"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_Rb103A" },
|
||||
["RBS103B CHM"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_Rb103B" },
|
||||
["RBS103AM CHM"] = { Range=150, Blindspot=3, Height=24.5, Type="Long", Radar="LvS-103_Lavett103_HX_Rb103A" },
|
||||
["RBS103BM CHM"] = { Range=35, Blindspot=0, Height=36, Type="Medium", Radar="LvS-103_Lavett103_HX_Rb103B" },
|
||||
["Lvkv9040M CHM"] = { Range=4, Blindspot=0, Height=2.5, Type="Short", Radar="LvKv9040" },
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
@@ -640,7 +663,7 @@ do
|
||||
|
||||
-- TODO Version
|
||||
-- @field #string version
|
||||
self.version="0.8.18"
|
||||
self.version="0.8.20"
|
||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||
|
||||
--- FSM Functions ---
|
||||
@@ -1396,7 +1419,7 @@ do
|
||||
-- @return #string type Long, medium or short range
|
||||
-- @return #number blind "blind" spot
|
||||
function MANTIS:_GetSAMDataFromUnits(grpname,mod,sma,chm)
|
||||
self:T(self.lid.."_GetSAMRangeFromUnits")
|
||||
self:T(self.lid.."_GetSAMDataFromUnits")
|
||||
local found = false
|
||||
local range = self.checkradius
|
||||
local height = 3000
|
||||
@@ -1449,7 +1472,7 @@ do
|
||||
-- @return #string type Long, medium or short range
|
||||
-- @return #number blind "blind" spot
|
||||
function MANTIS:_GetSAMRange(grpname)
|
||||
self:T(self.lid.."_GetSAMRange")
|
||||
self:T(self.lid.."_GetSAMRange for "..tostring(grpname))
|
||||
local range = self.checkradius
|
||||
local height = 3000
|
||||
local type = MANTIS.SamType.MEDIUM
|
||||
@@ -1466,9 +1489,9 @@ do
|
||||
elseif string.find(grpname,"CHM",1,true) then
|
||||
CHMod = true
|
||||
end
|
||||
if self.automode then
|
||||
--if self.automode then
|
||||
for idx,entry in pairs(self.SamData) do
|
||||
--self:I("ID = " .. idx)
|
||||
self:T("ID = " .. idx)
|
||||
if string.find(grpname,idx,1,true) then
|
||||
local _entry = entry -- #MANTIS.SamData
|
||||
type = _entry.Type
|
||||
@@ -1476,18 +1499,21 @@ do
|
||||
range = _entry.Range * 1000 * radiusscale -- max firing range
|
||||
height = _entry.Height * 1000 -- max firing height
|
||||
blind = _entry.Blindspot
|
||||
--self:I("Matching Groupname = " .. grpname .. " Range= " .. range)
|
||||
self:T("Matching Groupname = " .. grpname .. " Range= " .. range)
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
--end
|
||||
-- secondary filter if not found
|
||||
if (not found and self.automode) or HDSmod or SMAMod or CHMod then
|
||||
if (not found) or HDSmod or SMAMod or CHMod then
|
||||
range, height, type = self:_GetSAMDataFromUnits(grpname,HDSmod,SMAMod,CHMod)
|
||||
elseif not found then
|
||||
self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname))
|
||||
end
|
||||
if string.find(grpname,"SHORAD",1,true) then
|
||||
type = MANTIS.SamType.SHORT -- force short on match
|
||||
end
|
||||
return range, height, type, blind
|
||||
end
|
||||
|
||||
@@ -1651,6 +1677,10 @@ do
|
||||
function MANTIS:_CheckLoop(samset,detset,dlink,limit)
|
||||
self:T(self.lid .. "CheckLoop " .. #detset .. " Coordinates")
|
||||
local switchedon = 0
|
||||
local statusreport = REPORT:New("\nMANTIS Status")
|
||||
local instatusred = 0
|
||||
local instatusgreen = 0
|
||||
local SEADactive = 0
|
||||
for _,_data in pairs (samset) do
|
||||
local samcoordinate = _data[2]
|
||||
local name = _data[1]
|
||||
@@ -1673,7 +1703,7 @@ do
|
||||
elseif (not self.UseEmOnOff) and switchedon < limit then
|
||||
samgroup:OptionAlarmStateRed()
|
||||
switchedon = switchedon + 1
|
||||
switch = true
|
||||
switch = true
|
||||
end
|
||||
if self.SamStateTracker[name] ~= "RED" and switch then
|
||||
self:__RedState(1,samgroup)
|
||||
@@ -1691,7 +1721,7 @@ do
|
||||
-- debug output
|
||||
if (self.debug or self.verbose) and switch then
|
||||
local text = string.format("SAM %s in alarm state RED!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
--local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
end
|
||||
end --end alive
|
||||
@@ -1709,12 +1739,26 @@ do
|
||||
end
|
||||
if self.debug or self.verbose then
|
||||
local text = string.format("SAM %s in alarm state GREEN!", name)
|
||||
local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
--local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug)
|
||||
if self.verbose then self:I(self.lid..text) end
|
||||
end
|
||||
end --end alive
|
||||
end --end check
|
||||
end --for for loop
|
||||
end --end check
|
||||
end --for loop
|
||||
if self.debug then
|
||||
for _,_status in pairs(self.SamStateTracker) do
|
||||
if _status == "GREEN" then
|
||||
instatusgreen=instatusgreen+1
|
||||
elseif _status == "RED" then
|
||||
instatusred=instatusred+1
|
||||
end
|
||||
end
|
||||
statusreport:Add("+-----------------------------+")
|
||||
statusreport:Add(string.format("+ SAM in RED State: %2d",instatusred))
|
||||
statusreport:Add(string.format("+ SAM in GREEN State: %2d",instatusgreen))
|
||||
statusreport:Add("+-----------------------------+")
|
||||
MESSAGE:New(statusreport:Text(),10,nil,true):ToAll():ToLog()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1828,7 +1872,7 @@ do
|
||||
end
|
||||
--]]
|
||||
if self.autoshorad then
|
||||
self.Shorad = SHORAD:New(self.name.."-SHORAD",self.name.."-SHORAD",self.SAM_Group,self.ShoradActDistance,self.ShoradTime,self.coalition,self.UseEmOnOff)
|
||||
self.Shorad = SHORAD:New(self.name.."-SHORAD","SHORAD",self.SAM_Group,self.ShoradActDistance,self.ShoradTime,self.coalition,self.UseEmOnOff)
|
||||
self.Shorad:SetDefenseLimits(80,95)
|
||||
self.ShoradLink = true
|
||||
self.Shorad.Groupset=self.ShoradGroupSet
|
||||
|
||||
@@ -1340,7 +1340,7 @@ end
|
||||
-- @return #RANGE self
|
||||
function RANGE:SetSoundfilesPath( path )
|
||||
self.soundpath = tostring( path or "Range Soundfiles/" )
|
||||
self:I( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) )
|
||||
self:T2( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) )
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1636,9 +1636,9 @@ function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
|
||||
|
||||
-- Debug or error output.
|
||||
if _isstatic == true then
|
||||
self:I( self.lid .. string.format( "Adding STATIC bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||
self:T( self.lid .. string.format( "Adding STATIC bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||
elseif _isstatic == false then
|
||||
self:I( self.lid .. string.format( "Adding UNIT bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||
self:T( self.lid .. string.format( "Adding UNIT bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||
else
|
||||
self:E( self.lid .. string.format( "ERROR! No bombing target with name %s could be found. Carefully check all UNIT and STATIC names defined in the mission editor!", name ) )
|
||||
end
|
||||
@@ -1706,7 +1706,7 @@ function RANGE:AddBombingTargetScenery( scenery, goodhitrange)
|
||||
|
||||
-- Debug or error output.
|
||||
if name then
|
||||
self:I( self.lid .. string.format( "Adding SCENERY bombing target %s with good hit range %d", name, goodhitrange) )
|
||||
self:T( self.lid .. string.format( "Adding SCENERY bombing target %s with good hit range %d", name, goodhitrange) )
|
||||
else
|
||||
self:E( self.lid .. string.format( "ERROR! No bombing target with name %s could be found!", name ) )
|
||||
end
|
||||
@@ -1811,7 +1811,7 @@ function RANGE:OnEventBirth( EventData )
|
||||
if not EventData.IniPlayerName then return end
|
||||
|
||||
local _unitName = EventData.IniUnitName
|
||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName, EventData.IniPlayerName )
|
||||
|
||||
self:T3( self.lid .. "BIRTH: unit = " .. tostring( EventData.IniUnitName ) )
|
||||
self:T3( self.lid .. "BIRTH: group = " .. tostring( EventData.IniGroupName ) )
|
||||
@@ -1967,7 +1967,9 @@ end
|
||||
-- @param #number attackAlt Attack altitude.
|
||||
-- @param #number attackVel Attack velocity.
|
||||
function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackVel)
|
||||
|
||||
|
||||
if not playerData then return end
|
||||
|
||||
-- Get closet target to last position.
|
||||
local _closetTarget = nil -- #RANGE.BombTarget
|
||||
local _distance = nil
|
||||
@@ -1984,13 +1986,13 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
|
||||
-- Coordinate of impact point.
|
||||
local impactcoord = weapon:GetImpactCoordinate()
|
||||
|
||||
-- Check if impact happened in range zone.
|
||||
-- Check if impact happened in range zone.+
|
||||
local insidezone = self.rangezone:IsCoordinateInZone( impactcoord )
|
||||
|
||||
|
||||
-- Smoke impact point of bomb.
|
||||
if playerData.smokebombimpact and insidezone then
|
||||
if playerData.delaysmoke then
|
||||
if playerData and playerData.smokebombimpact and insidezone then
|
||||
if playerData and playerData.delaysmoke then
|
||||
timer.scheduleFunction( self._DelayedSmoke, { coord = impactcoord, color = playerData.smokecolor }, timer.getTime() + self.TdelaySmoke )
|
||||
else
|
||||
impactcoord:Smoke( playerData.smokecolor )
|
||||
@@ -2115,7 +2117,7 @@ function RANGE:OnEventShot( EventData )
|
||||
local _unitName = EventData.IniUnitName
|
||||
|
||||
-- Get player unit and name.
|
||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName, EventData.IniPlayerName )
|
||||
|
||||
-- Distance Player-to-Range. Set this to larger value than the threshold.
|
||||
local dPR = self.BombtrackThreshold * 2
|
||||
@@ -2127,11 +2129,13 @@ function RANGE:OnEventShot( EventData )
|
||||
end
|
||||
|
||||
-- Only track if distance player to range is < 25 km. Also check that a player shot. No need to track AI weapons.
|
||||
if _track and dPR <= self.BombtrackThreshold and _unit and _playername then
|
||||
if _track and dPR <= self.BombtrackThreshold and _unit and _playername and self.PlayerSettings[_playername] then
|
||||
|
||||
-- Player data.
|
||||
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
|
||||
|
||||
|
||||
if not playerData then return end
|
||||
|
||||
-- Attack parameters.
|
||||
local attackHdg=_unit:GetHeading()
|
||||
local attackAlt=_unit:GetHeight()
|
||||
@@ -2192,7 +2196,7 @@ function RANGE:onafterStatus( From, Event, To )
|
||||
end
|
||||
|
||||
-- Check range status.
|
||||
self:I( self.lid .. text )
|
||||
self:T( self.lid .. text )
|
||||
|
||||
end
|
||||
|
||||
@@ -2393,7 +2397,7 @@ function RANGE:onafterSave( From, Event, To )
|
||||
if f then
|
||||
f:write( data )
|
||||
f:close()
|
||||
self:I( self.lid .. string.format( "Saving player results to file %s", tostring( filename ) ) )
|
||||
self:T( self.lid .. string.format( "Saving player results to file %s", tostring( filename ) ) )
|
||||
else
|
||||
self:E( self.lid .. string.format( "ERROR: Could not save results to file %s", tostring( filename ) ) )
|
||||
end
|
||||
@@ -2472,7 +2476,7 @@ function RANGE:onafterLoad( From, Event, To )
|
||||
|
||||
-- Info message.
|
||||
local text = string.format( "Loading player bomb results from file %s", filename )
|
||||
self:I( self.lid .. text )
|
||||
self:T( self.lid .. text )
|
||||
|
||||
-- Load asset data from file.
|
||||
local data = _loadfile( filename )
|
||||
@@ -2845,7 +2849,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
|
||||
|
||||
-- Check if we have a player.
|
||||
if unit and playername then
|
||||
self:I(playername)
|
||||
--self:I(playername)
|
||||
-- Message text.
|
||||
local text = ""
|
||||
|
||||
@@ -2983,7 +2987,7 @@ function RANGE:_DisplayBombTargets( _unitname )
|
||||
end
|
||||
end
|
||||
|
||||
self:_DisplayMessageToGroup( _unit, _text, 120, true, true, _multiplayer )
|
||||
self:_DisplayMessageToGroup( _unit, _text, 150, true, true, _multiplayer )
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3449,10 +3453,10 @@ function RANGE:_AddF10Commands( _unitName )
|
||||
-- Range menu
|
||||
local _rangePath = MENU_GROUP:New( group, self.rangename, _rootMenu )
|
||||
|
||||
local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
|
||||
local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
|
||||
local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
|
||||
local _infoPath = MENU_GROUP:New( group, "Range Info", _rangePath )
|
||||
local _markPath = MENU_GROUP:New( group, "Mark Targets", _rangePath )
|
||||
local _statsPath = MENU_GROUP:New( group, "Statistics", _rangePath )
|
||||
local _settingsPath = MENU_GROUP:New( group, "My Settings", _rangePath )
|
||||
|
||||
-- F10/On the Range/<Range Name>/My Settings/
|
||||
local _mysmokePath = MENU_GROUP:New( group, "Smoke Color", _settingsPath )
|
||||
@@ -4099,8 +4103,8 @@ end
|
||||
-- @return Wrapper.Unit#UNIT Unit of player.
|
||||
-- @return #string Name of the player.
|
||||
-- @return #boolean If true, group has > 1 player in it
|
||||
function RANGE:_GetPlayerUnitAndName( _unitName )
|
||||
self:F2( _unitName )
|
||||
function RANGE:_GetPlayerUnitAndName( _unitName, PlayerName )
|
||||
--self:I( _unitName )
|
||||
|
||||
if _unitName ~= nil then
|
||||
|
||||
@@ -4109,9 +4113,9 @@ function RANGE:_GetPlayerUnitAndName( _unitName )
|
||||
-- Get DCS unit from its name.
|
||||
local DCSunit = Unit.getByName( _unitName )
|
||||
|
||||
if DCSunit then
|
||||
if DCSunit and DCSunit.getPlayerName then
|
||||
|
||||
local playername = DCSunit:getPlayerName()
|
||||
local playername = DCSunit:getPlayerName() or PlayerName or "None"
|
||||
local unit = UNIT:Find( DCSunit )
|
||||
|
||||
self:T2( { DCSunit = DCSunit, unit = unit, playername = playername } )
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
--
|
||||
-- ### Authors: **applevangelist**, **FlightControl**
|
||||
--
|
||||
-- Last Update: Dec 2023
|
||||
-- Last Update: Oct 2024
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -28,6 +28,16 @@
|
||||
|
||||
---
|
||||
-- @type SEAD
|
||||
-- @field #string ClassName The Class Name.
|
||||
-- @field #table TargetSkill Table of target skills.
|
||||
-- @field #table SEADGroupPrefixes Table of SEAD prefixes.
|
||||
-- @field #table SuppressedGroups Table of currently suppressed groups.
|
||||
-- @field #number EngagementRange Engagement Range.
|
||||
-- @field #number Padding Padding in seconds.
|
||||
-- @field #function CallBack Callback function for suppression plans.
|
||||
-- @field #boolean UseCallBack Switch for callback function to be used.
|
||||
-- @field #boolean debug Debug switch.
|
||||
-- @field #boolen WeaponTrack Track switch, if true track weapon speed for 30 secs.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- Make SAM sites execute evasive and defensive behaviour when being fired upon.
|
||||
@@ -56,10 +66,11 @@ SEAD = {
|
||||
SEADGroupPrefixes = {},
|
||||
SuppressedGroups = {},
|
||||
EngagementRange = 75, -- default 75% engagement range Feature Request #1355
|
||||
Padding = 10,
|
||||
Padding = 15,
|
||||
CallBack = nil,
|
||||
UseCallBack = false,
|
||||
debug = false,
|
||||
WeaponTrack = false,
|
||||
}
|
||||
|
||||
--- Missile enumerators
|
||||
@@ -144,7 +155,7 @@ function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
self:AddTransition("*", "ManageEvasion", "*")
|
||||
self:AddTransition("*", "CalculateHitZone", "*")
|
||||
|
||||
self:I("*** SEAD - Started Version 0.4.6")
|
||||
self:I("*** SEAD - Started Version 0.4.8")
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -371,7 +382,7 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP
|
||||
reach = wpndata[1] * 1.1
|
||||
local mach = wpndata[2]
|
||||
wpnspeed = math.floor(mach * 340.29)
|
||||
if Weapon then
|
||||
if Weapon and Weapon:GetSpeed() > 0 then
|
||||
wpnspeed = Weapon:GetSpeed()
|
||||
self:T(string.format("*** SEAD - Weapon Speed from WEAPON: %f m/s",wpnspeed))
|
||||
end
|
||||
@@ -452,22 +463,30 @@ end
|
||||
-- @return #SEAD self
|
||||
function SEAD:HandleEventShot( EventData )
|
||||
self:T( { EventData.id } )
|
||||
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
||||
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
|
||||
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
||||
local SEADUnit = EventData.IniDCSUnit
|
||||
local SEADUnitName = EventData.IniDCSUnitName
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||
|
||||
local WeaponWrapper = WEAPON:New(EventData.Weapon)
|
||||
--local SEADWeaponSpeed = WeaponWrapper:GetSpeed() -- mps
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
--self:T({ SEADWeapon })
|
||||
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = EventData.WeaponName or "None" -- return weapon type
|
||||
|
||||
if self:_CheckHarms(SEADWeaponName) then
|
||||
local SEADPlane = EventData.IniUnit -- Wrapper.Unit#UNIT
|
||||
|
||||
if not SEADPlane then return self end -- case IniUnit is empty
|
||||
|
||||
local SEADGroup = EventData.IniGroup -- Wrapper.Group#GROUP
|
||||
local SEADPlanePos = SEADPlane:GetCoordinate() -- Core.Point#COORDINATE
|
||||
local SEADUnit = EventData.IniDCSUnit
|
||||
local SEADUnitName = EventData.IniDCSUnitName
|
||||
|
||||
local WeaponWrapper = WEAPON:New(EventData.Weapon) -- Wrapper.Weapon#WEAPON
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
|
||||
self:T( '*** SEAD - Weapon Match' )
|
||||
if self.WeaponTrack == true then
|
||||
WeaponWrapper:SetFuncTrack(function(weapon) env.info(string.format("*** Weapon Speed: %d m/s",weapon:GetSpeed() or -1)) end)
|
||||
WeaponWrapper:StartTrack(0.1)
|
||||
WeaponWrapper:StopTrack(30)
|
||||
end
|
||||
local _targetskill = "Random"
|
||||
local _targetgroupname = "none"
|
||||
local _target = EventData.Weapon:getTarget() -- Identify target
|
||||
@@ -520,7 +539,7 @@ function SEAD:HandleEventShot( EventData )
|
||||
end
|
||||
if SEADGroupFound == true then -- yes we are being attacked
|
||||
if string.find(SEADWeaponName,"ADM_141",1,true) then
|
||||
self:__ManageEvasion(2,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
|
||||
self:__ManageEvasion(2,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,2,WeaponWrapper)
|
||||
else
|
||||
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
|
||||
end
|
||||
|
||||
@@ -1629,7 +1629,7 @@ WAREHOUSE = {
|
||||
-- @field #boolean arrived If true, asset arrived at its destination.
|
||||
--
|
||||
-- @field #number damage Damage of asset group in percent.
|
||||
-- @field Ops.AirWing#AIRWING.Payload payload The payload of the asset.
|
||||
-- @field Ops.Airwing#AIRWING.Payload payload The payload of the asset.
|
||||
-- @field Ops.OpsGroup#OPSGROUP flightgroup The flightgroup object.
|
||||
-- @field Ops.Cohort#COHORT cohort The cohort this asset belongs to.
|
||||
-- @field Ops.Legion#LEGION legion The legion this asset belonts to.
|
||||
@@ -6732,7 +6732,7 @@ end
|
||||
-- @param Wrapper.Group#GROUP deadgroup Group of unit that died.
|
||||
-- @param #WAREHOUSE.Pendingitem request Request that needs to be updated.
|
||||
function WAREHOUSE:_UnitDead(deadunit, deadgroup, request)
|
||||
self:F(self.lid.."FF unit dead "..deadunit:GetName())
|
||||
--self:F(self.lid.."FF unit dead "..deadunit:GetName())
|
||||
|
||||
-- Find opsgroup.
|
||||
local opsgroup=_DATABASE:FindOpsGroup(deadgroup)
|
||||
@@ -8433,12 +8433,14 @@ function WAREHOUSE:_GetAttribute(group)
|
||||
local attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN --#WAREHOUSE.Attribute
|
||||
|
||||
if group then
|
||||
|
||||
local groupCat=group:GetCategory()
|
||||
|
||||
-----------
|
||||
--- Air ---
|
||||
-----------
|
||||
-- Planes
|
||||
local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes")
|
||||
local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes") and groupCat==Group.Category.AIRPLANE
|
||||
local awacs=group:HasAttribute("AWACS")
|
||||
local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") or (group:HasAttribute("Bombers") and not group:HasAttribute("Strategic bombers"))
|
||||
local bomber=group:HasAttribute("Strategic bombers")
|
||||
@@ -8593,7 +8595,6 @@ end
|
||||
-- @param #WAREHOUSE.Queueitem qitem Item of queue to be removed.
|
||||
-- @param #table queue The queue from which the item should be deleted.
|
||||
function WAREHOUSE:_DeleteQueueItem(qitem, queue)
|
||||
self:F({qitem=qitem, queue=queue})
|
||||
|
||||
for i=1,#queue do
|
||||
local _item=queue[i] --#WAREHOUSE.Queueitem
|
||||
|
||||
@@ -48,6 +48,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Marker.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Weapon.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Net.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Storage.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/DynamicCargo.lua' )
|
||||
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/Cargo.lua' )
|
||||
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Cargo/CargoUnit.lua' )
|
||||
|
||||
@@ -291,7 +291,7 @@
|
||||
-- ## Nevada: Nellis AFB
|
||||
--
|
||||
-- -- ATIS Nellis AFB on 270.10 MHz AM.
|
||||
-- atisNellis=ATIS:New(AIRBASE.Nevada.Nellis_AFB, 270.1)
|
||||
-- atisNellis=ATIS:New(AIRBASE.Nevada.Nellis, 270.1)
|
||||
-- atisNellis:SetRadioRelayUnitName("Radio Relay Nellis")
|
||||
-- atisNellis:SetActiveRunway("21L")
|
||||
-- atisNellis:SetTowerFrequencies({327.000, 132.550})
|
||||
@@ -302,7 +302,7 @@
|
||||
-- ## Persian Gulf: Abu Dhabi International Airport
|
||||
--
|
||||
-- -- ATIS Abu Dhabi International on 125.1 MHz AM.
|
||||
-- atisAbuDhabi=ATIS:New(AIRBASE.PersianGulf.Abu_Dhabi_International_Airport, 125.1)
|
||||
-- atisAbuDhabi=ATIS:New(AIRBASE.PersianGulf.Abu_Dhabi_Intl, 125.1)
|
||||
-- atisAbuDhabi:SetRadioRelayUnitName("Radio Relay Abu Dhabi International Airport")
|
||||
-- atisAbuDhabi:SetMetricUnits()
|
||||
-- atisAbuDhabi:SetActiveRunway("L")
|
||||
@@ -498,6 +498,9 @@ ATIS.Alphabet = {
|
||||
-- @field #number Syria +5° (East).
|
||||
-- @field #number MarianaIslands +2° (East).
|
||||
-- @field #number SinaiMap +5° (East).
|
||||
-- @field #number Kola +15° (East).
|
||||
-- @field #number Afghanistan +3° (East).
|
||||
-- @field #number Iraq +4.4° (East).
|
||||
ATIS.RunwayM2T = {
|
||||
Caucasus = 0,
|
||||
Nevada = 12,
|
||||
@@ -508,6 +511,9 @@ ATIS.RunwayM2T = {
|
||||
MarianaIslands = 2,
|
||||
Falklands = 12,
|
||||
SinaiMap = 5,
|
||||
Kola = 15,
|
||||
Afghanistan = 3,
|
||||
Iraq=4.4
|
||||
}
|
||||
|
||||
--- Whether ICAO phraseology is used for ATIS broadcasts.
|
||||
@@ -521,6 +527,9 @@ ATIS.RunwayM2T = {
|
||||
-- @field #boolean MarianaIslands true.
|
||||
-- @field #boolean Falklands true.
|
||||
-- @field #boolean SinaiMap true.
|
||||
-- @field #boolean Kola true.
|
||||
-- @field #boolean Afghanistan true.
|
||||
-- @field #boolean Iraq true.
|
||||
ATIS.ICAOPhraseology = {
|
||||
Caucasus = true,
|
||||
Nevada = false,
|
||||
@@ -531,6 +540,9 @@ ATIS.ICAOPhraseology = {
|
||||
MarianaIslands = true,
|
||||
Falklands = true,
|
||||
SinaiMap = true,
|
||||
Kola = true,
|
||||
Afghanistan = true,
|
||||
Iraq = true,
|
||||
}
|
||||
|
||||
--- Nav point data.
|
||||
@@ -619,83 +631,83 @@ ATIS.ICAOPhraseology = {
|
||||
-- @field #ATIS.Soundfile TACANChannel
|
||||
-- @field #ATIS.Soundfile VORFrequency
|
||||
ATIS.Sound = {
|
||||
ActiveRunway = { filename = "ActiveRunway.ogg", duration = 0.99 },
|
||||
ActiveRunwayDeparture = { filename = "ActiveRunwayDeparture.ogg", duration = 0.99 },
|
||||
ActiveRunwayArrival = { filename = "ActiveRunwayArrival.ogg", duration = 0.99 },
|
||||
AdviceOnInitial = { filename = "AdviceOnInitial.ogg", duration = 3.00 },
|
||||
Airport = { filename = "Airport.ogg", duration = 0.66 },
|
||||
Altimeter = { filename = "Altimeter.ogg", duration = 0.68 },
|
||||
At = { filename = "At.ogg", duration = 0.41 },
|
||||
CloudBase = { filename = "CloudBase.ogg", duration = 0.82 },
|
||||
CloudCeiling = { filename = "CloudCeiling.ogg", duration = 0.61 },
|
||||
CloudsBroken = { filename = "CloudsBroken.ogg", duration = 1.07 },
|
||||
CloudsFew = { filename = "CloudsFew.ogg", duration = 0.99 },
|
||||
CloudsNo = { filename = "CloudsNo.ogg", duration = 1.01 },
|
||||
CloudsNotAvailable = { filename = "CloudsNotAvailable.ogg", duration = 2.35 },
|
||||
CloudsOvercast = { filename = "CloudsOvercast.ogg", duration = 0.83 },
|
||||
CloudsScattered = { filename = "CloudsScattered.ogg", duration = 1.18 },
|
||||
Decimal = { filename = "Decimal.ogg", duration = 0.54 },
|
||||
DegreesCelsius = { filename = "DegreesCelsius.ogg", duration = 1.27 },
|
||||
DegreesFahrenheit = { filename = "DegreesFahrenheit.ogg", duration = 1.23 },
|
||||
DewPoint = { filename = "DewPoint.ogg", duration = 0.65 },
|
||||
Dust = { filename = "Dust.ogg", duration = 0.54 },
|
||||
Elevation = { filename = "Elevation.ogg", duration = 0.78 },
|
||||
EndOfInformation = { filename = "EndOfInformation.ogg", duration = 1.15 },
|
||||
Feet = { filename = "Feet.ogg", duration = 0.45 },
|
||||
Fog = { filename = "Fog.ogg", duration = 0.47 },
|
||||
Gusting = { filename = "Gusting.ogg", duration = 0.55 },
|
||||
HectoPascal = { filename = "HectoPascal.ogg", duration = 1.15 },
|
||||
Hundred = { filename = "Hundred.ogg", duration = 0.47 },
|
||||
InchesOfMercury = { filename = "InchesOfMercury.ogg", duration = 1.16 },
|
||||
Information = { filename = "Information.ogg", duration = 0.85 },
|
||||
Kilometers = { filename = "Kilometers.ogg", duration = 0.78 },
|
||||
Knots = { filename = "Knots.ogg", duration = 0.59 },
|
||||
Left = { filename = "Left.ogg", duration = 0.54 },
|
||||
MegaHertz = { filename = "MegaHertz.ogg", duration = 0.87 },
|
||||
Meters = { filename = "Meters.ogg", duration = 0.59 },
|
||||
MetersPerSecond = { filename = "MetersPerSecond.ogg", duration = 1.14 },
|
||||
Miles = { filename = "Miles.ogg", duration = 0.60 },
|
||||
MillimetersOfMercury = { filename = "MillimetersOfMercury.ogg", duration = 1.53 },
|
||||
Minus = { filename = "Minus.ogg", duration = 0.64 },
|
||||
N0 = { filename = "N-0.ogg", duration = 0.55 },
|
||||
N1 = { filename = "N-1.ogg", duration = 0.41 },
|
||||
N2 = { filename = "N-2.ogg", duration = 0.37 },
|
||||
N3 = { filename = "N-3.ogg", duration = 0.41 },
|
||||
N4 = { filename = "N-4.ogg", duration = 0.37 },
|
||||
N5 = { filename = "N-5.ogg", duration = 0.43 },
|
||||
N6 = { filename = "N-6.ogg", duration = 0.55 },
|
||||
N7 = { filename = "N-7.ogg", duration = 0.43 },
|
||||
N8 = { filename = "N-8.ogg", duration = 0.38 },
|
||||
N9 = { filename = "N-9.ogg", duration = 0.55 },
|
||||
NauticalMiles = { filename = "NauticalMiles.ogg", duration = 1.04 },
|
||||
None = { filename = "None.ogg", duration = 0.43 },
|
||||
QFE = { filename = "QFE.ogg", duration = 0.63 },
|
||||
QNH = { filename = "QNH.ogg", duration = 0.71 },
|
||||
Rain = { filename = "Rain.ogg", duration = 0.41 },
|
||||
Right = { filename = "Right.ogg", duration = 0.44 },
|
||||
Snow = { filename = "Snow.ogg", duration = 0.48 },
|
||||
SnowStorm = { filename = "SnowStorm.ogg", duration = 0.82 },
|
||||
StatuteMiles = { filename = "StatuteMiles.ogg", duration = 1.15 },
|
||||
SunriseAt = { filename = "SunriseAt.ogg", duration = 0.92 },
|
||||
SunsetAt = { filename = "SunsetAt.ogg", duration = 0.95 },
|
||||
Temperature = { filename = "Temperature.ogg", duration = 0.64 },
|
||||
Thousand = { filename = "Thousand.ogg", duration = 0.55 },
|
||||
ThunderStorm = { filename = "ThunderStorm.ogg", duration = 0.81 },
|
||||
TimeLocal = { filename = "TimeLocal.ogg", duration = 0.90 },
|
||||
TimeZulu = { filename = "TimeZulu.ogg", duration = 0.86 },
|
||||
TowerFrequency = { filename = "TowerFrequency.ogg", duration = 1.19 },
|
||||
Visibilty = { filename = "Visibility.ogg", duration = 0.79 },
|
||||
WeatherPhenomena = { filename = "WeatherPhenomena.ogg", duration = 1.07 },
|
||||
WindFrom = { filename = "WindFrom.ogg", duration = 0.60 },
|
||||
ActiveRunway = { filename = "ActiveRunway.ogg", duration = 0.85 },
|
||||
ActiveRunwayDeparture = { filename = "ActiveRunwayDeparture.ogg", duration = 1.50 },
|
||||
ActiveRunwayArrival = { filename = "ActiveRunwayArrival.ogg", duration = 1.38 },
|
||||
AdviceOnInitial = { filename = "AdviceOnInitial.ogg", duration = 2.98 },
|
||||
Airport = { filename = "Airport.ogg", duration = 0.55 },
|
||||
Altimeter = { filename = "Altimeter.ogg", duration = 0.91 },
|
||||
At = { filename = "At.ogg", duration = 0.32 },
|
||||
CloudBase = { filename = "CloudBase.ogg", duration = 0.69 },
|
||||
CloudCeiling = { filename = "CloudCeiling.ogg", duration = 0.53 },
|
||||
CloudsBroken = { filename = "CloudsBroken.ogg", duration = 0.81 },
|
||||
CloudsFew = { filename = "CloudsFew.ogg", duration = 0.74 },
|
||||
CloudsNo = { filename = "CloudsNo.ogg", duration = 0.69},
|
||||
CloudsNotAvailable = { filename = "CloudsNotAvailable.ogg", duration = 2.64 },
|
||||
CloudsOvercast = { filename = "CloudsOvercast.ogg", duration = 0.82 },
|
||||
CloudsScattered = { filename = "CloudsScattered.ogg", duration = 0.89 },
|
||||
Decimal = { filename = "Decimal.ogg", duration = 0.71 },
|
||||
DegreesCelsius = { filename = "DegreesCelsius.ogg", duration = 1.08 },
|
||||
DegreesFahrenheit = { filename = "DegreesFahrenheit.ogg", duration = 1.07 },
|
||||
DewPoint = { filename = "DewPoint.ogg", duration = 0.59 },
|
||||
Dust = { filename = "Dust.ogg", duration = 0.37 },
|
||||
Elevation = { filename = "Elevation.ogg", duration = 0.92 },
|
||||
EndOfInformation = { filename = "EndOfInformation.ogg", duration = 1.24 },
|
||||
Feet = { filename = "Feet.ogg", duration = 0.34 },
|
||||
Fog = { filename = "Fog.ogg", duration = 0.41 },
|
||||
Gusting = { filename = "Gusting.ogg", duration = 0.58 },
|
||||
HectoPascal = { filename = "HectoPascal.ogg", duration = 0.92 },
|
||||
Hundred = { filename = "Hundred.ogg", duration = 0.53 },
|
||||
ILSFrequency = { filename = "ILSFrequency.ogg", duration = 1.30 },
|
||||
InnerNDBFrequency = { filename = "InnerNDBFrequency.ogg", duration = 1.56 },
|
||||
OuterNDBFrequency = { filename = "OuterNDBFrequency.ogg", duration = 1.59 },
|
||||
RunwayLength = { filename = "RunwayLength.ogg", duration = 0.91 },
|
||||
VORFrequency = { filename = "VORFrequency.ogg", duration = 1.38 },
|
||||
TACANChannel = { filename = "TACANChannel.ogg", duration = 0.88 },
|
||||
PRMGChannel = { filename = "PRMGChannel.ogg", duration = 1.18 },
|
||||
RSBNChannel = { filename = "RSBNChannel.ogg", duration = 1.14 },
|
||||
Zulu = { filename = "Zulu.ogg", duration = 0.62 },
|
||||
InchesOfMercury = { filename = "InchesOfMercury.ogg", duration = 1.26 },
|
||||
Information = { filename = "Information.ogg", duration = 0.99 },
|
||||
InnerNDBFrequency = { filename = "InnerNDBFrequency.ogg", duration = 1.69 },
|
||||
Kilometers = { filename = "Kilometers.ogg", duration = 0.93 },
|
||||
Knots = { filename = "Knots.ogg", duration = 0.46 },
|
||||
Left = { filename = "Left.ogg", duration = 0.41 },
|
||||
MegaHertz = { filename = "MegaHertz.ogg", duration = 0.83 },
|
||||
Meters = { filename = "Meters.ogg", duration = 0.55 },
|
||||
MetersPerSecond = { filename = "MetersPerSecond.ogg", duration = 1.03 },
|
||||
Miles = { filename = "Miles.ogg", duration = 0.44 },
|
||||
MillimetersOfMercury = { filename = "MillimetersOfMercury.ogg", duration = 1.59 },
|
||||
Minus = { filename = "Minus.ogg", duration = 0.55 },
|
||||
N0 = { filename = "N-0.ogg", duration = 0.52 },
|
||||
N1 = { filename = "N-1.ogg", duration = 0.35 },
|
||||
N2 = { filename = "N-2.ogg", duration = 0.41 },
|
||||
N3 = { filename = "N-3.ogg", duration = 0.34 },
|
||||
N4 = { filename = "N-4.ogg", duration = 0.37 },
|
||||
N5 = { filename = "N-5.ogg", duration = 0.40 },
|
||||
N6 = { filename = "N-6.ogg", duration = 0.46 },
|
||||
N7 = { filename = "N-7.ogg", duration = 0.52 },
|
||||
N8 = { filename = "N-8.ogg", duration = 0.36 },
|
||||
N9 = { filename = "N-9.ogg", duration = 0.51 },
|
||||
NauticalMiles = { filename = "NauticalMiles.ogg", duration = 0.93 },
|
||||
None = { filename = "None.ogg", duration = 0.33 },
|
||||
OuterNDBFrequency = { filename = "OuterNDBFrequency.ogg", duration = 1.70 },
|
||||
PRMGChannel = { filename = "PRMGChannel.ogg", duration = 1.27 },
|
||||
QFE = { filename = "QFE.ogg", duration = 0.90 },
|
||||
QNH = { filename = "QNH.ogg", duration = 0.94 },
|
||||
Rain = { filename = "Rain.ogg", duration = 0.35 },
|
||||
Right = { filename = "Right.ogg", duration = 0.31 },
|
||||
RSBNChannel = { filename = "RSBNChannel.ogg", duration = 1.26 },
|
||||
RunwayLength = { filename = "RunwayLength.ogg", duration = 0.81 },
|
||||
Snow = { filename = "Snow.ogg", duration = 0.40 },
|
||||
SnowStorm = { filename = "SnowStorm.ogg", duration = 0.73 },
|
||||
StatuteMiles = { filename = "StatuteMiles.ogg", duration = 0.90 },
|
||||
SunriseAt = { filename = "SunriseAt.ogg", duration = 0.82 },
|
||||
SunsetAt = { filename = "SunsetAt.ogg", duration = 0.87 },
|
||||
TACANChannel = { filename = "TACANChannel.ogg", duration = 0.81 },
|
||||
Temperature = { filename = "Temperature.ogg", duration = 0.70 },
|
||||
Thousand = { filename = "Thousand.ogg", duration = 0.58 },
|
||||
ThunderStorm = { filename = "ThunderStorm.ogg", duration = 0.79 },
|
||||
TimeLocal = { filename = "TimeLocal.ogg", duration = 0.83 },
|
||||
TimeZulu = { filename = "TimeZulu.ogg", duration = 0.83 },
|
||||
TowerFrequency = { filename = "TowerFrequency.ogg", duration = 1.05 },
|
||||
Visibilty = { filename = "Visibility.ogg", duration = 1.16 },
|
||||
VORFrequency = { filename = "VORFrequency.ogg", duration = 1.28 },
|
||||
WeatherPhenomena = { filename = "WeatherPhenomena.ogg", duration = 1.09 },
|
||||
WindFrom = { filename = "WindFrom.ogg", duration = 0.63 },
|
||||
Zulu = { filename = "Zulu.ogg", duration = 0.51 },
|
||||
}
|
||||
|
||||
---
|
||||
@@ -882,6 +894,66 @@ ATIS.Messages = {
|
||||
FARP = "Farp",
|
||||
DELIMITER = "Punto", -- decimal delimiter
|
||||
},
|
||||
-- French messages thanks to @Wojtech and Bing
|
||||
FR = {
|
||||
HOURS = "Heures",
|
||||
TIME = "Temps",
|
||||
NOCLOUDINFO = "Informations sur la couverture nuageuse non disponibles",
|
||||
OVERCAST = "Ciel couvert",
|
||||
BROKEN = "Nuages fragmentés",
|
||||
SCATTERED = "Nuages épars",
|
||||
FEWCLOUDS = "Nuages rares",
|
||||
NOCLOUDS = "Clair",
|
||||
AIRPORT = "Aéroport",
|
||||
INFORMATION ="Information",
|
||||
SUNRISEAT = "Levé du soleil à %s heure locale",
|
||||
SUNSETAT = "Couché du soleil à %s heure locale",
|
||||
WINDFROMMS = "Vent du %s pour %s mètres par seconde",
|
||||
WINDFROMKNOTS = "Vent du %s pour %s noeuds",
|
||||
GUSTING = "Rafale de vent",
|
||||
VISIKM = "Visibilité %s kilomètres",
|
||||
VISISM = "Visibilité %s Miles",
|
||||
RAIN = "Pluie",
|
||||
TSTORM = "Orage",
|
||||
SNOW = "Neige",
|
||||
SSTROM = "Tempête de neige",
|
||||
FOG = "Brouillard",
|
||||
DUST = "Poussière",
|
||||
PHENOMENA = "Phénomène météorologique",
|
||||
CLOUDBASEM = "Couverture nuageuse de %s à %s mètres",
|
||||
CLOUDBASEFT = "Couverture nuageuse de %s à %s pieds",
|
||||
TEMPERATURE = "Température",
|
||||
DEWPOINT = "Point de rosée",
|
||||
ALTIMETER = "Altimètre",
|
||||
ACTIVERUN = "Décollages piste",
|
||||
ACTIVELANDING = "Atterrissages piste",
|
||||
LEFT = "Gauche",
|
||||
RIGHT = "Droite",
|
||||
RWYLENGTH = "Longueur de piste",
|
||||
METERS = "Mètre",
|
||||
FEET = "Pieds",
|
||||
ELEVATION = "Hauteur",
|
||||
TOWERFREQ = "Fréquences de la tour",
|
||||
ILSFREQ = "Fréquences ILS",
|
||||
OUTERNDB = "Fréquences Outer NDB",
|
||||
INNERNDB = "Fréquences Inner NDB",
|
||||
VORFREQ = "Fréquences VOR",
|
||||
VORFREQTTS = "Fréquences V O R",
|
||||
TACANCH = "Canal TACAN %d",
|
||||
RSBNCH = "Canal RSBN",
|
||||
PRMGCH = "Canal PRMG",
|
||||
ADVISE = "Informez le contrôle que vous avez copié l'information",
|
||||
STATUTE = "Statute Miles",
|
||||
DEGREES = "Degré celcius",
|
||||
FAHRENHEIT = "Degré Fahrenheit",
|
||||
INCHHG = "Pouces de mercure",
|
||||
MMHG = "Millimètres de mercure",
|
||||
HECTO = "Hectopascals",
|
||||
METERSPER = "Mètres par seconde",
|
||||
TACAN = "TAKAN",
|
||||
FARP = "FARPE",
|
||||
DELIMITER = "Décimal", -- decimal delimiter
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
@@ -894,7 +966,7 @@ _ATIS = {}
|
||||
|
||||
--- ATIS class version.
|
||||
-- @field #string version
|
||||
ATIS.version = "1.0.0"
|
||||
ATIS.version = "1.0.1"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -1061,7 +1133,7 @@ end
|
||||
-- @return #ATIS self
|
||||
function ATIS:_InitLocalization()
|
||||
self:T(self.lid.."_InitLocalization")
|
||||
self.gettext = TEXTANDSOUND:New("AWACS","en") -- Core.TextAndSound#TEXTANDSOUND
|
||||
self.gettext = TEXTANDSOUND:New("ATIS","en") -- Core.TextAndSound#TEXTANDSOUND
|
||||
self.locale = "en"
|
||||
for locale,table in pairs(self.Messages) do
|
||||
local Locale = string.lower(tostring(locale))
|
||||
@@ -1975,17 +2047,25 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
|
||||
local hours = self.gettext:GetEntry("HOURS",self.locale)
|
||||
local sunrise = coord:GetSunrise()
|
||||
sunrise = UTILS.Split( sunrise, ":" )
|
||||
local SUNRISE = string.format( "%s%s", sunrise[1], sunrise[2] )
|
||||
if self.useSRS then
|
||||
SUNRISE = string.format( "%s %s %s", sunrise[1], sunrise[2], hours )
|
||||
--self:I(sunrise)
|
||||
local SUNRISE = "no time"
|
||||
if tostring(sunrise) ~= "N/S" and tostring(sunrise) ~= "N/R" then
|
||||
sunrise = UTILS.Split( sunrise, ":" )
|
||||
SUNRISE = string.format( "%s%s", sunrise[1], sunrise[2] )
|
||||
if self.useSRS then
|
||||
SUNRISE = string.format( "%s %s %s", sunrise[1], sunrise[2], hours )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local sunset = coord:GetSunset()
|
||||
sunset = UTILS.Split( sunset, ":" )
|
||||
local SUNSET = string.format( "%s%s", sunset[1], sunset[2] )
|
||||
if self.useSRS then
|
||||
SUNSET = string.format( "%s %s %s", sunset[1], sunset[2], hours )
|
||||
--self:I(sunset)
|
||||
local SUNSET = "no time"
|
||||
if tostring(sunset) ~= "N/S" and tostring(sunset) ~= "N/R" then
|
||||
sunset = UTILS.Split( sunset, ":" )
|
||||
SUNSET = string.format( "%s%s", sunset[1], sunset[2] )
|
||||
if self.useSRS then
|
||||
SUNSET = string.format( "%s %s %s", sunset[1], sunset[2], hours )
|
||||
end
|
||||
end
|
||||
|
||||
---------------------------------
|
||||
@@ -2012,34 +2092,32 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
---------------
|
||||
|
||||
-- Get mission weather info. Most of this is static.
|
||||
local clouds, visibility, turbulence, fog, dust, static = self:GetMissionWeather()
|
||||
|
||||
-- Check that fog is actually "thick" enough to reach the airport. If an airport is in the mountains, fog might not affect it as it is measured from sea level.
|
||||
if fog and fog.thickness < height + 25 then
|
||||
fog = nil
|
||||
end
|
||||
|
||||
-- Dust only up to 1500 ft = 457 m ASL.
|
||||
if dust and height + 25 > UTILS.FeetToMeters( 1500 ) then
|
||||
dust = nil
|
||||
end
|
||||
local clouds, visibility, turbulence, dustdens, static = self:GetMissionWeather()
|
||||
|
||||
local dust=false
|
||||
local fog=false
|
||||
|
||||
------------------
|
||||
--- Visibility ---
|
||||
------------------
|
||||
|
||||
-- Get min visibility.
|
||||
local visibilitymin = visibility
|
||||
|
||||
if fog then
|
||||
if fog.visibility < visibilitymin then
|
||||
visibilitymin = fog.visibility
|
||||
if dustdens then
|
||||
|
||||
-- Dust only up to 1500 ft = 457 m ASL.
|
||||
if UTILS.FeetToMeters( 1500 )> height+25 then
|
||||
dust=true
|
||||
visibility=math.min(visibility, dustdens)
|
||||
end
|
||||
end
|
||||
|
||||
if dust then
|
||||
if dust < visibilitymin then
|
||||
visibilitymin = dust
|
||||
|
||||
else -- As of DCS 2.9.10.3948 (December 2024), fog and dust are mutually exclusive!
|
||||
|
||||
-- Get current fog visibility and thickness
|
||||
local fvis=world.weather.getFogVisibilityDistance()
|
||||
local fheight=world.weather.getFogThickness()
|
||||
|
||||
if fvis>0 and fheight>height+25 then
|
||||
fog=true
|
||||
visibility=math.min(visibility, fvis)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2047,7 +2125,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
|
||||
if self.metric then
|
||||
-- Visibility in km.
|
||||
local reportedviz = UTILS.Round( visibilitymin / 1000 )
|
||||
local reportedviz = UTILS.Round( visibility / 1000 )
|
||||
-- max reported visibility 9999 m
|
||||
if reportedviz > 10 then
|
||||
reportedviz = 10
|
||||
@@ -2055,7 +2133,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
VISIBILITY = string.format( "%d", reportedviz )
|
||||
else
|
||||
-- max reported visibility 10 NM
|
||||
local reportedviz = UTILS.Round( UTILS.MetersToSM( visibilitymin ) )
|
||||
local reportedviz = UTILS.Round( UTILS.MetersToSM( visibility ) )
|
||||
if reportedviz > 10 then
|
||||
reportedviz = 10
|
||||
end
|
||||
@@ -2069,7 +2147,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
local cloudbase = clouds.base
|
||||
local cloudceil = clouds.base + clouds.thickness
|
||||
local clouddens = clouds.density
|
||||
|
||||
|
||||
-- Cloud preset (DCS 2.7)
|
||||
local cloudspreset = clouds.preset or "Nothing"
|
||||
|
||||
@@ -2100,6 +2178,39 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
elseif cloudspreset:find( "RainyPreset4" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 5
|
||||
if temperature > 5 then
|
||||
precepitation = 1 -- rain
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
elseif cloudspreset:find( "RainyPreset5" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 5
|
||||
if temperature > 5 then
|
||||
precepitation = 1 -- rain
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
elseif cloudspreset:find( "RainyPreset6" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 5
|
||||
if temperature > 5 then
|
||||
precepitation = 1 -- rain
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
-- NEWRAINPRESET4
|
||||
elseif cloudspreset:find( "NEWRAINPRESET4" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 5
|
||||
if temperature > 5 then
|
||||
precepitation = 1 -- rain
|
||||
else
|
||||
precepitation = 3 -- snow
|
||||
end
|
||||
elseif cloudspreset:find( "RainyPreset" ) then
|
||||
-- Overcast + Rain
|
||||
clouddens = 9
|
||||
@@ -3249,28 +3360,13 @@ function ATIS:GetMissionWeather()
|
||||
dust = weather.dust_density
|
||||
end
|
||||
|
||||
-- Fog
|
||||
--[[
|
||||
["enable_fog"] = false,
|
||||
["fog"] =
|
||||
{
|
||||
["thickness"] = 0,
|
||||
["visibility"] = 25,
|
||||
}, -- end of ["fog"]
|
||||
]]
|
||||
local fog = nil
|
||||
if weather.enable_fog == true then
|
||||
fog = weather.fog
|
||||
end
|
||||
|
||||
self:T( "FF weather:" )
|
||||
self:T( { clouds = clouds } )
|
||||
self:T( { visibility = visibility } )
|
||||
self:T( { turbulence = turbulence } )
|
||||
self:T( { fog = fog } )
|
||||
self:T( { dust = dust } )
|
||||
self:T( { static = static } )
|
||||
return clouds, visibility, turbulence, fog, dust, static
|
||||
return clouds, visibility, turbulence, dust, static
|
||||
end
|
||||
|
||||
--- Get thousands of a number.
|
||||
|
||||
@@ -3614,7 +3614,7 @@ function AIRBOSS:onafterStart( From, Event, To )
|
||||
|
||||
-- Handle events.
|
||||
self:HandleEvent( EVENTS.Birth )
|
||||
self:HandleEvent( EVENTS.Land )
|
||||
self:HandleEvent( EVENTS.RunwayTouch )
|
||||
self:HandleEvent( EVENTS.EngineShutdown )
|
||||
self:HandleEvent( EVENTS.Takeoff )
|
||||
self:HandleEvent( EVENTS.Crash )
|
||||
@@ -4379,7 +4379,7 @@ function AIRBOSS:onafterStop( From, Event, To )
|
||||
|
||||
-- Unhandle events.
|
||||
self:UnHandleEvent( EVENTS.Birth )
|
||||
self:UnHandleEvent( EVENTS.Land )
|
||||
self:UnHandleEvent( EVENTS.RunwayTouch )
|
||||
self:UnHandleEvent( EVENTS.EngineShutdown )
|
||||
self:UnHandleEvent( EVENTS.Takeoff )
|
||||
self:UnHandleEvent( EVENTS.Crash )
|
||||
@@ -8289,7 +8289,7 @@ end
|
||||
--- Airboss event handler for event land.
|
||||
-- @param #AIRBOSS self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AIRBOSS:OnEventLand( EventData )
|
||||
function AIRBOSS:OnEventRunwayTouch( EventData )
|
||||
self:F3( { eventland = EventData } )
|
||||
|
||||
-- Nil checks.
|
||||
@@ -14678,7 +14678,7 @@ function AIRBOSS:_GetPlayerUnitAndName( _unitName )
|
||||
-- Get DCS unit from its name.
|
||||
local DCSunit = Unit.getByName( _unitName )
|
||||
|
||||
if DCSunit then
|
||||
if DCSunit and DCSunit.getPlayerName then
|
||||
|
||||
-- Get player name if any.
|
||||
local playername = DCSunit:getPlayerName()
|
||||
@@ -15649,7 +15649,7 @@ function AIRBOSS:_Number2Sound( playerData, sender, number, delay )
|
||||
end
|
||||
|
||||
-- Split string into characters.
|
||||
local numbers = _split( number )
|
||||
local numbers = _split( tostring(number) )
|
||||
|
||||
local wait = 0
|
||||
for i = 1, #numbers do
|
||||
@@ -15717,7 +15717,7 @@ function AIRBOSS:_Number2Radio( radio, number, delay, interval, pilotcall )
|
||||
end
|
||||
|
||||
-- Split string into characters.
|
||||
local numbers = _split( number )
|
||||
local numbers = _split( tostring(number) )
|
||||
|
||||
local wait = 0
|
||||
for i = 1, #numbers do
|
||||
@@ -18085,7 +18085,7 @@ function AIRBOSS:_MarkCaseZones( _unitName, flare )
|
||||
self:_GetZoneArcIn( case ):FlareZone( FLARECOLOR.White, 45 )
|
||||
text = text .. "\n* arc turn in with WHITE flares"
|
||||
self:_GetZoneArcOut( case ):FlareZone( FLARECOLOR.White, 45 )
|
||||
text = text .. "\n* arc trun out with WHITE flares"
|
||||
text = text .. "\n* arc turn out with WHITE flares"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -18137,7 +18137,7 @@ function AIRBOSS:_MarkCaseZones( _unitName, flare )
|
||||
self:_GetZoneArcIn( case ):SmokeZone( SMOKECOLOR.Blue, 45 )
|
||||
text = text .. "\n* arc turn in with BLUE smoke"
|
||||
self:_GetZoneArcOut( case ):SmokeZone( SMOKECOLOR.Blue, 45 )
|
||||
text = text .. "\n* arc trun out with BLUE smoke"
|
||||
text = text .. "\n* arc turn out with BLUE smoke"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
-- @image OPS_CSAR.jpg
|
||||
|
||||
---
|
||||
-- Last Update July 2024
|
||||
-- Last Update Sep 2024
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **CSAR** class, extends Core.Base#BASE, Core.Fsm#FSM
|
||||
@@ -41,6 +41,7 @@
|
||||
-- @field #string lid Class id string for output to DCS log file.
|
||||
-- @field #number coalition Coalition side number, e.g. `coalition.side.RED`.
|
||||
-- @field Core.Set#SET_GROUP allheligroupset Set of CSAR heli groups.
|
||||
-- @field Core.Set#SET_GROUP UserSetGroup Set of CSAR heli groups as designed by the mission designer (if any set).
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *Combat search and rescue (CSAR) are search and rescue operations that are carried out during war that are within or near combat zones.* (Wikipedia)
|
||||
@@ -116,8 +117,17 @@
|
||||
-- mycsar.topmenuname = "CSAR" -- set the menu entry name
|
||||
-- mycsar.ADFRadioPwr = 1000 -- ADF Beacons sending with 1KW as default
|
||||
-- mycsar.PilotWeight = 80 -- Loaded pilots weigh 80kgs each
|
||||
-- mycsar.AllowIRStrobe = false -- Allow a menu item to request an IR strobe to find a downed pilot at night (requires NVGs to see it).
|
||||
-- mycsar.IRStrobeRuntime = 300 -- If an IR Strobe is activated, it runs for 300 seconds (5 mins).
|
||||
--
|
||||
-- ## 2.1 Create own SET_GROUP to manage CTLD Pilot groups
|
||||
--
|
||||
-- -- Parameter: Set The SET_GROUP object created by the mission designer/user to represent the CSAR pilot groups.
|
||||
-- -- Needs to be set before starting the CSAR instance.
|
||||
-- local myset = SET_GROUP:New():FilterPrefixes("Helikopter"):FilterCoalitions("red"):FilterStart()
|
||||
-- mycsar:SetOwnSetPilotGroups(myset)
|
||||
--
|
||||
-- ## 2.1 SRS Features and Other Features
|
||||
-- ## 2.2 SRS Features and Other Features
|
||||
--
|
||||
-- mycsar.useSRS = false -- Set true to use FF\'s SRS integration
|
||||
-- mycsar.SRSPath = "C:\\Progra~1\\DCS-SimpleRadio-Standalone\\" -- adjust your own path in your SRS installation -- server(!)
|
||||
@@ -136,6 +146,7 @@
|
||||
-- mycsar.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection. Requires mycsar.enableForAI to be set to true. --shagrat
|
||||
-- mycsar.wetfeettemplate = "man in floating thingy" -- if you use a mod to have a pilot in a rescue float, put the template name in here for wet feet spawns. Note: in conjunction with csarUsePara this might create dual ejected pilots in edge cases.
|
||||
-- mycsar.allowbronco = false -- set to true to use the Bronco mod as a CSAR plane
|
||||
-- mycsar.CreateRadioBeacons = true -- set to false to disallow creating ADF radio beacons.
|
||||
--
|
||||
-- ## 3. Results
|
||||
--
|
||||
@@ -256,6 +267,10 @@ CSAR = {
|
||||
topmenuname = "CSAR",
|
||||
ADFRadioPwr = 1000,
|
||||
PilotWeight = 80,
|
||||
CreateRadioBeacons = true,
|
||||
UserSetGroup = nil,
|
||||
AllowIRStrobe = false,
|
||||
IRStrobeRuntime = 300,
|
||||
}
|
||||
|
||||
--- Downed pilots info.
|
||||
@@ -272,6 +287,7 @@ CSAR = {
|
||||
-- @field #number timestamp Timestamp for approach process.
|
||||
-- @field #boolean alive Group is alive or dead/rescued.
|
||||
-- @field #boolean wetfeet Group is spawned over (deep) water.
|
||||
-- @field #string BeaconName Name of radio beacon - if any.
|
||||
|
||||
--- All slot / Limit settings
|
||||
-- @type CSAR.AircraftType
|
||||
@@ -292,12 +308,12 @@ CSAR.AircraftType["AH-64D_BLK_II"] = 2
|
||||
CSAR.AircraftType["Bronco-OV-10A"] = 2
|
||||
CSAR.AircraftType["MH-60R"] = 10
|
||||
CSAR.AircraftType["OH-6A"] = 2
|
||||
CSAR.AircraftType["OH-58D"] = 2
|
||||
CSAR.AircraftType["OH58D"] = 2
|
||||
CSAR.AircraftType["CH-47Fbl1"] = 31
|
||||
|
||||
--- CSAR class version.
|
||||
-- @field #string version
|
||||
CSAR.version="1.0.26"
|
||||
CSAR.version="1.0.29"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@@ -456,6 +472,9 @@ function CSAR:New(Coalition, Template, Alias)
|
||||
|
||||
-- added 1.0.16
|
||||
self.PilotWeight = 80
|
||||
|
||||
-- Own SET_GROUP if any
|
||||
self.UserSetGroup = nil
|
||||
|
||||
-- WARNING - here\'ll be dragons
|
||||
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
|
||||
@@ -634,7 +653,7 @@ end
|
||||
-- @param #string Playername Name of Player (if applicable)
|
||||
-- @param #boolean Wetfeet Ejected over water
|
||||
-- @return #CSAR self.
|
||||
function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername,Wetfeet)
|
||||
function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername,Wetfeet,BeaconName)
|
||||
self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername})
|
||||
|
||||
-- create new entry
|
||||
@@ -642,7 +661,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript
|
||||
DownedPilot.desc = Description or ""
|
||||
DownedPilot.frequency = Frequency or 0
|
||||
DownedPilot.index = self.downedpilotcounter
|
||||
DownedPilot.name = Groupname or ""
|
||||
DownedPilot.name = Groupname or Playername or ""
|
||||
DownedPilot.originalUnit = OriginalUnit or ""
|
||||
DownedPilot.player = Playername or ""
|
||||
DownedPilot.side = Side or 0
|
||||
@@ -651,6 +670,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript
|
||||
DownedPilot.timestamp = 0
|
||||
DownedPilot.alive = true
|
||||
DownedPilot.wetfeet = Wetfeet or false
|
||||
DownedPilot.BeaconName = BeaconName
|
||||
|
||||
-- Add Pilot
|
||||
local PilotTable = self.downedPilots
|
||||
@@ -737,7 +757,6 @@ function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet)
|
||||
:NewWithAlias(template,alias)
|
||||
:InitCoalition(coalition)
|
||||
:InitCountry(country)
|
||||
--:InitAIOnOff(pilotcacontrol)
|
||||
:InitDelayOff()
|
||||
:SpawnFromCoordinate(point)
|
||||
|
||||
@@ -819,8 +838,18 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
|
||||
end
|
||||
end
|
||||
|
||||
local BeaconName
|
||||
|
||||
if _playerName then
|
||||
BeaconName = _playerName..math.random(1,10000)
|
||||
elseif _unitName then
|
||||
BeaconName = _unitName..math.random(1,10000)
|
||||
else
|
||||
BeaconName = "Ghost-1-1"..math.random(1,10000)
|
||||
end
|
||||
|
||||
if (_freq and _freq ~= 0) then --shagrat only add beacon if _freq is NOT 0
|
||||
self:_AddBeaconToGroup(_spawnedGroup, _freq)
|
||||
self:_AddBeaconToGroup(_spawnedGroup, _freq, BeaconName)
|
||||
end
|
||||
|
||||
self:_AddSpecialOptions(_spawnedGroup)
|
||||
@@ -845,7 +874,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
|
||||
|
||||
local _GroupName = _spawnedGroup:GetName() or _alias
|
||||
|
||||
self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName,wetfeet)
|
||||
self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName,wetfeet,BeaconName)
|
||||
|
||||
self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage, _playerName) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc.
|
||||
|
||||
@@ -963,7 +992,6 @@ end
|
||||
-- @param Core.Point#COORDINATE Point
|
||||
-- @param #number Coalition Coalition.
|
||||
-- @param #string Description (optional) Description.
|
||||
-- @param #boolean addBeacon (optional) yes or no.
|
||||
-- @param #boolean Nomessage (optional) If true, don\'t send a message to SAR.
|
||||
-- @param #string Unitname (optional) Name of the lost unit.
|
||||
-- @param #string Typename (optional) Type of plane.
|
||||
@@ -1793,9 +1821,6 @@ function CSAR:_DisplayMessageToSAR(_unit, _text, _time, _clear, _speak, _overrid
|
||||
end
|
||||
_text = string.gsub(_text,"km"," kilometer")
|
||||
_text = string.gsub(_text,"nm"," nautical miles")
|
||||
--self.msrs:SetVoice(self.SRSVoice)
|
||||
--self.SRSQueue:NewTransmission(_text,nil,self.msrs,nil,1)
|
||||
--self:I("Voice = "..self.SRSVoice)
|
||||
self.SRSQueue:NewTransmission(_text,duration,self.msrs,tstart,2,subgroups,subtitle,subduration,self.SRSchannel,self.SRSModulation,gender,culture,self.SRSVoice,volume,label,coord)
|
||||
end
|
||||
return self
|
||||
@@ -1856,11 +1881,11 @@ function CSAR:_DisplayActiveSAR(_unitName)
|
||||
else
|
||||
distancetext = string.format("%.1fkm", _distance/1000.0)
|
||||
end
|
||||
if _value.frequency == 0 then--shagrat insert CASEVAC without Frequency
|
||||
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %s ", _value.desc, _coordinatesText, distancetext) })
|
||||
else
|
||||
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %.2f KHz ADF - %s ", _value.desc, _coordinatesText, _value.frequency / 1000, distancetext) })
|
||||
end
|
||||
if _value.frequency == 0 or self.CreateRadioBeacons == false then--shagrat insert CASEVAC without Frequency
|
||||
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %s ", _value.desc, _coordinatesText, distancetext) })
|
||||
else
|
||||
table.insert(_csarList, { dist = _distance, msg = string.format("%s at %s - %.2f KHz ADF - %s ", _value.desc, _coordinatesText, _value.frequency / 1000, distancetext) })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1941,7 +1966,7 @@ function CSAR:_SignalFlare(_unitName)
|
||||
else
|
||||
_distance = string.format("%.1fkm",_closest.distance/1000)
|
||||
end
|
||||
local _msg = string.format("%s - Popping signal flare at your %s o\'clock. Distance %s", self:_GetCustomCallSign(_unitName), _clockDir, _distance)
|
||||
local _msg = string.format("%s - Firing signal flare at your %s o\'clock. Distance %s", self:_GetCustomCallSign(_unitName), _clockDir, _distance)
|
||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true, true)
|
||||
|
||||
local _coord = _closest.pilot:GetCoordinate()
|
||||
@@ -1975,7 +2000,7 @@ function CSAR:_DisplayToAllSAR(_message, _side, _messagetime,ToSRS,ToScreen)
|
||||
if self.msrs:GetProvider() == MSRS.Provider.WINDOWS then
|
||||
voice = self.CSARVoiceMS or MSRS.Voices.Microsoft.Hedda
|
||||
end
|
||||
self:F("Voice = "..voice)
|
||||
--self:F("Voice = "..voice)
|
||||
self.SRSQueue:NewTransmission(_message,duration,self.msrs,tstart,2,subgroups,subtitle,subduration,self.SRSchannel,self.SRSModulation,gender,culture,voice,volume,label,self.coordinate)
|
||||
end
|
||||
if ToScreen == true or ToScreen == nil then
|
||||
@@ -1989,6 +2014,41 @@ function CSAR:_DisplayToAllSAR(_message, _side, _messagetime,ToSRS,ToScreen)
|
||||
return self
|
||||
end
|
||||
|
||||
---(Internal) Request IR Strobe at closest downed pilot.
|
||||
--@param #CSAR self
|
||||
--@param #string _unitName Name of the helicopter
|
||||
function CSAR:_ReqIRStrobe( _unitName )
|
||||
self:T(self.lid .. " _ReqIRStrobe")
|
||||
local _heli = self:_GetSARHeli(_unitName)
|
||||
if _heli == nil then
|
||||
return
|
||||
end
|
||||
local smokedist = 8000
|
||||
if smokedist < self.approachdist_far then smokedist = self.approachdist_far end
|
||||
local _closest = self:_GetClosestDownedPilot(_heli)
|
||||
if _closest ~= nil and _closest.pilot ~= nil and _closest.distance > 0 and _closest.distance < smokedist then
|
||||
local _clockDir = self:_GetClockDirection(_heli, _closest.pilot)
|
||||
local _distance = string.format("%.1fkm",_closest.distance/1000)
|
||||
if _SETTINGS:IsImperial() then
|
||||
_distance = string.format("%.1fnm",UTILS.MetersToNM(_closest.distance))
|
||||
else
|
||||
_distance = string.format("%.1fkm",_closest.distance/1000)
|
||||
end
|
||||
local _msg = string.format("%s - IR Strobe active at your %s o\'clock. Distance %s", self:_GetCustomCallSign(_unitName), _clockDir, _distance)
|
||||
self:_DisplayMessageToSAR(_heli, _msg, self.messageTime, false, true, true)
|
||||
_closest.pilot:NewIRMarker(true,self.IRStrobeRuntime or 300)
|
||||
else
|
||||
local _distance = string.format("%.1fkm",smokedist/1000)
|
||||
if _SETTINGS:IsImperial() then
|
||||
_distance = string.format("%.1fnm",UTILS.MetersToNM(smokedist))
|
||||
else
|
||||
_distance = string.format("%.1fkm",smokedist/1000)
|
||||
end
|
||||
self:_DisplayMessageToSAR(_heli, string.format("No Pilots within %s",_distance), self.messageTime, false, false, true)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
---(Internal) Request smoke at closest downed pilot.
|
||||
--@param #CSAR self
|
||||
--@param #string _unitName Name of the helicopter
|
||||
@@ -2140,7 +2200,12 @@ function CSAR:_AddMedevacMenuItem()
|
||||
local _rootMenu1 = MENU_GROUP_COMMAND:New(_group,"List Active CSAR",_rootPath, self._DisplayActiveSAR,self,_unitName)
|
||||
local _rootMenu2 = MENU_GROUP_COMMAND:New(_group,"Check Onboard",_rootPath, self._CheckOnboard,self,_unitName)
|
||||
local _rootMenu3 = MENU_GROUP_COMMAND:New(_group,"Request Signal Flare",_rootPath, self._SignalFlare,self,_unitName)
|
||||
local _rootMenu4 = MENU_GROUP_COMMAND:New(_group,"Request Smoke",_rootPath, self._Reqsmoke,self,_unitName):Refresh()
|
||||
local _rootMenu4 = MENU_GROUP_COMMAND:New(_group,"Request Smoke",_rootPath, self._Reqsmoke,self,_unitName)
|
||||
if self.AllowIRStrobe then
|
||||
local _rootMenu5 = MENU_GROUP_COMMAND:New(_group,"Request IR Strobe",_rootPath, self._ReqIRStrobe,self,_unitName):Refresh()
|
||||
else
|
||||
_rootMenu4:Refresh()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2231,9 +2296,13 @@ end
|
||||
-- @param #CSAR self
|
||||
-- @param Wrapper.Group#GROUP _group Group #GROUP object.
|
||||
-- @param #number _freq Frequency to use
|
||||
function CSAR:_AddBeaconToGroup(_group, _freq)
|
||||
-- @param #string _name Beacon Name to use
|
||||
-- @return #CSAR self
|
||||
function CSAR:_AddBeaconToGroup(_group, _freq, _name)
|
||||
self:T(self.lid .. " _AddBeaconToGroup")
|
||||
if self.CreateRadioBeacons == false then return end
|
||||
local _group = _group
|
||||
|
||||
if _group == nil then
|
||||
--return frequency to pool of available
|
||||
for _i, _current in ipairs(self.UsedVHFFrequencies) do
|
||||
@@ -2248,22 +2317,24 @@ function CSAR:_AddBeaconToGroup(_group, _freq)
|
||||
if _group:IsAlive() then
|
||||
local _radioUnit = _group:GetUnit(1)
|
||||
if _radioUnit then
|
||||
local name = _radioUnit:GetName()
|
||||
local name = _radioUnit:GetName()
|
||||
local Frequency = _freq -- Freq in Hertz
|
||||
local name = _radioUnit:GetName()
|
||||
local Sound = "l10n/DEFAULT/"..self.radioSound
|
||||
local vec3 = _radioUnit:GetVec3() or _radioUnit:GetPositionVec3() or {x=0,y=0,z=0}
|
||||
trigger.action.radioTransmission(Sound, vec3, 0, false, Frequency, self.ADFRadioPwr or 1000,name..math.random(1,10000)) -- Beacon in MP only runs for exactly 30secs straight
|
||||
trigger.action.radioTransmission(Sound, vec3, 0, false, Frequency, self.ADFRadioPwr or 1000,_name) -- Beacon in MP only runs for exactly 30secs straight
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Helper function to (re-)add beacon to downed pilot.
|
||||
-- @param #CSAR self
|
||||
-- @param #table _args Arguments
|
||||
-- @return #CSAR self
|
||||
function CSAR:_RefreshRadioBeacons()
|
||||
self:T(self.lid .. " _RefreshRadioBeacons")
|
||||
if self.CreateRadioBeacons == false then return end
|
||||
if self:_CountActiveDownedPilots() > 0 then
|
||||
local PilotTable = self.downedPilots
|
||||
for _,_pilot in pairs (PilotTable) do
|
||||
@@ -2271,8 +2342,10 @@ function CSAR:_RefreshRadioBeacons()
|
||||
local pilot = _pilot -- #CSAR.DownedPilot
|
||||
local group = pilot.group
|
||||
local frequency = pilot.frequency or 0 -- thanks to @Thrud
|
||||
local bname = pilot.BeaconName or pilot.name..math.random(1,100000)
|
||||
trigger.action.stopRadioTransmission(bname)
|
||||
if group and group:IsAlive() and frequency > 0 then
|
||||
self:_AddBeaconToGroup(group,frequency)
|
||||
self:_AddBeaconToGroup(group,frequency,bname)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2309,6 +2382,16 @@ function CSAR:_ReachedPilotLimit()
|
||||
end
|
||||
end
|
||||
|
||||
--- User - Function to add onw SET_GROUP Set-up for pilot filtering and assignment.
|
||||
-- Needs to be set before starting the CSAR instance.
|
||||
-- @param #CSAR self
|
||||
-- @param Core.Set#SET_GROUP Set The SET_GROUP object created by the mission designer/user to represent the CSAR pilot groups.
|
||||
-- @return #CSAR self
|
||||
function CSAR:SetOwnSetPilotGroups(Set)
|
||||
self.UserSetGroup = Set
|
||||
return self
|
||||
end
|
||||
|
||||
------------------------------
|
||||
--- FSM internal Functions ---
|
||||
------------------------------
|
||||
@@ -2330,7 +2413,9 @@ function CSAR:onafterStart(From, Event, To)
|
||||
self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
|
||||
|
||||
if self.allowbronco then
|
||||
if self.UserSetGroup then
|
||||
self.allheligroupset = self.UserSetGroup
|
||||
elseif self.allowbronco then
|
||||
local prefixes = self.csarPrefix or {}
|
||||
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(prefixes):FilterStart()
|
||||
elseif self.useprefix then
|
||||
@@ -2339,7 +2424,9 @@ function CSAR:onafterStart(From, Event, To)
|
||||
else
|
||||
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
|
||||
end
|
||||
|
||||
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
|
||||
|
||||
if not self.coordinate then
|
||||
local csarhq = self.mash:GetRandom()
|
||||
if csarhq then
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,6 +52,7 @@
|
||||
-- @field #table poptions Provider options. Each element is a data structure of type `MSRS.ProvierOptions`.
|
||||
-- @field #string provider Provider of TTS (win, gcloud, azure, amazon).
|
||||
-- @field #string backend Backend used as interface to SRS (MSRS.Backend.SRSEXE or MSRS.Backend.GRPC).
|
||||
-- @field #boolean UsePowerShell Use PowerShell to execute the command and not cmd.exe
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
||||
@@ -256,11 +257,12 @@ MSRS = {
|
||||
ConfigFilePath = "Config\\",
|
||||
ConfigLoaded = false,
|
||||
poptions = {},
|
||||
UsePowerShell = false,
|
||||
}
|
||||
|
||||
--- MSRS class version.
|
||||
-- @field #string version
|
||||
MSRS.version="0.3.0"
|
||||
MSRS.version="0.3.3"
|
||||
|
||||
--- Voices
|
||||
-- @type MSRS.Voices
|
||||
@@ -271,11 +273,11 @@ MSRS.Voices = {
|
||||
["David"] = "Microsoft David Desktop", -- en-US
|
||||
["Zira"] = "Microsoft Zira Desktop", -- en-US
|
||||
["Hortense"] = "Microsoft Hortense Desktop", --fr-FR
|
||||
["de-DE-Hedda"] = "Microsoft Hedda Desktop", -- de-DE
|
||||
["en-GB-Hazel"] = "Microsoft Hazel Desktop", -- en-GB
|
||||
["en-US-David"] = "Microsoft David Desktop", -- en-US
|
||||
["en-US-Zira"] = "Microsoft Zira Desktop", -- en-US
|
||||
["fr-FR-Hortense"] = "Microsoft Hortense Desktop", --fr-FR
|
||||
["de_DE_Hedda"] = "Microsoft Hedda Desktop", -- de-DE
|
||||
["en_GB_Hazel"] = "Microsoft Hazel Desktop", -- en-GB
|
||||
["en_US_David"] = "Microsoft David Desktop", -- en-US
|
||||
["en_US_Zira"] = "Microsoft Zira Desktop", -- en-US
|
||||
["fr_FR_Hortense"] = "Microsoft Hortense Desktop", --fr-FR
|
||||
},
|
||||
MicrosoftGRPC = { -- en-US/GB voices only as of Jan 2024, working ones if using gRPC and MS, if voice packs are installed
|
||||
--["Hedda"] = "Hedda", -- de-DE
|
||||
@@ -304,8 +306,7 @@ MSRS.Voices = {
|
||||
["en_CA_Linda"] = "Linda", --en-CA
|
||||
["en_IN_Ravi"] = "Ravi", --en-IN
|
||||
["en_IN_Heera"] = "Heera", --en-IN
|
||||
["en_IR_Sean"] = "Sean", --en-IR
|
||||
--]]
|
||||
["en_IR_Sean"] = "Sean", --en-IR
|
||||
},
|
||||
Google = {
|
||||
Standard = {
|
||||
@@ -589,7 +590,7 @@ function MSRS:SetBackendSRSEXE()
|
||||
end
|
||||
|
||||
--- Set the default backend.
|
||||
-- @param #MSRS self
|
||||
-- @param #string Backend
|
||||
function MSRS.SetDefaultBackend(Backend)
|
||||
MSRS.backend=Backend or MSRS.Backend.SRSEXE
|
||||
end
|
||||
@@ -1238,7 +1239,7 @@ function MSRS:PlayTextExt(Text, Delay, Frequencies, Modulations, Gender, Culture
|
||||
self:T({Text, Delay, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate} )
|
||||
|
||||
if Delay and Delay>0 then
|
||||
self:ScheduleOnce(Delay, MSRS.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate)
|
||||
self:ScheduleOnce(Delay, self.PlayTextExt, self, Text, 0, Frequencies, Modulations, Gender, Culture, Voice, Volume, Label, Coordinate)
|
||||
else
|
||||
|
||||
Frequencies = Frequencies or self:GetFrequencies()
|
||||
@@ -1376,20 +1377,25 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
modus=modus:gsub("1", "FM")
|
||||
|
||||
-- Command.
|
||||
local pwsh = string.format('Start-Process -WindowStyle Hidden -WorkingDirectory \"%s\" -FilePath \"%s\" -ArgumentList \'-f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume )
|
||||
|
||||
local command=string.format('"%s\\%s" -f "%s" -m "%s" -c %s -p %s -n "%s" -v "%.1f"', path, exe, freqs, modus, coal, port, label,volume)
|
||||
|
||||
-- Set voice or gender/culture.
|
||||
if voice then
|
||||
if voice and self.UsePowerShell ~= true then
|
||||
-- Use a specific voice (no need for gender and/or culture.
|
||||
command=command..string.format(" --voice=\"%s\"", tostring(voice))
|
||||
pwsh=pwsh..string.format(" --voice=\"%s\"", tostring(voice))
|
||||
else
|
||||
-- Add gender.
|
||||
if gender and gender~="female" then
|
||||
command=command..string.format(" -g %s", tostring(gender))
|
||||
pwsh=pwsh..string.format(" -g %s", tostring(gender))
|
||||
end
|
||||
-- Add culture.
|
||||
if culture and culture~="en-GB" then
|
||||
command=command..string.format(" -l %s", tostring(culture))
|
||||
pwsh=pwsh..string.format(" -l %s", tostring(culture))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1397,12 +1403,14 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
if coordinate then
|
||||
local lat,lon,alt=self:_GetLatLongAlt(coordinate)
|
||||
command=command..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt)
|
||||
pwsh=pwsh..string.format(" -L %.4f -O %.4f -A %d", lat, lon, alt)
|
||||
end
|
||||
|
||||
-- Set provider options
|
||||
if self.provider==MSRS.Provider.GOOGLE then
|
||||
local pops=self:GetProviderOptions()
|
||||
command=command..string.format(' --ssml -G "%s"', pops.credentials)
|
||||
pwsh=pwsh..string.format(' --ssml -G "%s"', pops.credentials)
|
||||
elseif self.provider==MSRS.Provider.WINDOWS then
|
||||
-- Nothing to do.
|
||||
else
|
||||
@@ -1416,8 +1424,12 @@ function MSRS:_GetCommand(freqs, modus, coal, gender, voice, culture, volume, sp
|
||||
|
||||
-- Debug output.
|
||||
self:T("MSRS command from _GetCommand="..command)
|
||||
|
||||
return command
|
||||
|
||||
if self.UsePowerShell == true then
|
||||
return pwsh
|
||||
else
|
||||
return command
|
||||
end
|
||||
end
|
||||
|
||||
--- Execute SRS command to play sound using the `DCS-SR-ExternalAudio.exe`.
|
||||
@@ -1425,7 +1437,7 @@ end
|
||||
-- @param #string command Command to executer
|
||||
-- @return #number Return value of os.execute() command.
|
||||
function MSRS:_ExecCommand(command)
|
||||
self:F( {command=command} )
|
||||
self:T2( {command=command} )
|
||||
|
||||
-- Skip this function if _GetCommand was not able to find the executable
|
||||
if string.find(command, "CommandNotFound") then return 0 end
|
||||
@@ -1433,7 +1445,13 @@ function MSRS:_ExecCommand(command)
|
||||
local batContent = command.." && exit"
|
||||
-- Create a tmp file.
|
||||
local filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".bat"
|
||||
|
||||
|
||||
if self.UsePowerShell == true then
|
||||
filename=os.getenv('TMP').."\\MSRS-"..MSRS.uuid()..".ps1"
|
||||
batContent = command .. "\'"
|
||||
self:I({batContent=batContent})
|
||||
end
|
||||
|
||||
local script=io.open(filename, "w+")
|
||||
script:write(batContent)
|
||||
script:close()
|
||||
@@ -1442,7 +1460,7 @@ function MSRS:_ExecCommand(command)
|
||||
self:T("MSRS batch content: "..batContent)
|
||||
|
||||
local res=nil
|
||||
if true then
|
||||
if self.UsePowerShell ~= true then
|
||||
|
||||
-- Create a tmp file.
|
||||
local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs"
|
||||
@@ -1470,23 +1488,20 @@ function MSRS:_ExecCommand(command)
|
||||
timer.scheduleFunction(os.remove, filenvbs, timer.getTime()+1)
|
||||
self:T("MSRS vbs and batch file removed")
|
||||
|
||||
elseif false then
|
||||
|
||||
-- Create a tmp file.
|
||||
local filenvbs = os.getenv('TMP') .. "\\MSRS-"..MSRS.uuid()..".vbs"
|
||||
|
||||
-- VBS script
|
||||
local script = io.open(filenvbs, "w+")
|
||||
script:write(string.format('Set oShell = CreateObject ("Wscript.Shell")\n'))
|
||||
script:write(string.format('Dim strArgs\n'))
|
||||
script:write(string.format('strArgs = "cmd /c %s"\n', filename))
|
||||
script:write(string.format('oShell.Run strArgs, 0, false'))
|
||||
script:close()
|
||||
|
||||
local runvbs=string.format('cscript.exe //Nologo //B "%s"', filenvbs)
|
||||
elseif self.UsePowerShell == true then
|
||||
|
||||
local pwsh = string.format('start /min "" powershell.exe -ExecutionPolicy Unrestricted -WindowStyle Hidden -Command "%s"',filename)
|
||||
--env.info("[MSRS] TextToSpeech Command :\n" .. pwsh.."\n")
|
||||
|
||||
if string.len(pwsh) > 255 then
|
||||
self:E("[MSRS] - pwsh string too long")
|
||||
end
|
||||
|
||||
-- Play file in 0.01 seconds
|
||||
res=os.execute(runvbs)
|
||||
res=os.execute(pwsh)
|
||||
|
||||
-- Remove file in 1 second.
|
||||
timer.scheduleFunction(os.remove, filename, timer.getTime()+1)
|
||||
|
||||
else
|
||||
-- Play command.
|
||||
@@ -1560,8 +1575,8 @@ end
|
||||
function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate)
|
||||
|
||||
-- Debug info.
|
||||
self:F("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()")
|
||||
self:F({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate})
|
||||
self:T("MSRS_BACKEND_DCSGRPC:_DCSgRPCtts()")
|
||||
self:T({Text, Frequencies, Gender, Culture, Voice, Volume, Label, Coordinate})
|
||||
|
||||
local options = {} -- #MSRS.GRPCOptions
|
||||
|
||||
@@ -1587,7 +1602,6 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab
|
||||
|
||||
-- Provider (win, gcloud, ...)
|
||||
local provider = self.provider or MSRS.Provider.WINDOWS
|
||||
self:F({provider=provider})
|
||||
|
||||
-- Provider options: voice, credentials
|
||||
options.provider = {}
|
||||
@@ -1595,7 +1609,7 @@ function MSRS:_DCSgRPCtts(Text, Frequencies, Gender, Culture, Voice, Volume, Lab
|
||||
|
||||
-- Voice
|
||||
Voice=Voice or self:GetVoice(self.provider) or self.voice
|
||||
|
||||
|
||||
if Voice then
|
||||
-- We use a specific voice
|
||||
options.provider[provider].voice = Voice
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
do -- Sound Base
|
||||
|
||||
--- @type SOUNDBASE
|
||||
-- @type SOUNDBASE
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
@@ -100,7 +100,7 @@ end
|
||||
|
||||
do -- Sound File
|
||||
|
||||
--- @type SOUNDFILE
|
||||
-- @type SOUNDFILE
|
||||
-- @field #string ClassName Name of the class
|
||||
-- @field #string filename Name of the flag.
|
||||
-- @field #string path Directory path, where the sound file is located. This includes the final slash "/".
|
||||
@@ -292,7 +292,7 @@ end
|
||||
|
||||
do -- Text-To-Speech
|
||||
|
||||
--- @type SOUNDTEXT
|
||||
-- @type SOUNDTEXT
|
||||
-- @field #string ClassName Name of the class
|
||||
-- @field #string text Text to speak.
|
||||
-- @field #number duration Duration in seconds.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
do -- UserSound
|
||||
|
||||
--- @type USERSOUND
|
||||
-- @type USERSOUND
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
|
||||
@@ -580,6 +580,18 @@ ENUMS.Link16Power = {
|
||||
--- Enums for the STORAGE class for stores - which need to be in ""
|
||||
-- @type ENUMS.Storage
|
||||
-- @type ENUMS.Storage.weapons
|
||||
-- @type ENUMS.Storage.weapons.missiles
|
||||
-- @type ENUMS.Storage.weapons.bombs
|
||||
-- @type ENUMS.Storage.weapons.nurs
|
||||
-- @type ENUMS.Storage.weapons.containers
|
||||
-- @type ENUMS.Storage.weapons.droptanks
|
||||
-- @type ENUMS.Storage.weapons.adapters
|
||||
-- @type ENUMS.Storage.weapons.torpedoes
|
||||
-- @type ENUMS.Storage.weapons.Gazelle
|
||||
-- @type ENUMS.Storage.weapons.CH47
|
||||
-- @type ENUMS.Storage.weapons.OH58
|
||||
-- @type ENUMS.Storage.weapons.UH1H
|
||||
-- @type ENUMS.Storage.weapons.AH64D
|
||||
ENUMS.Storage = {
|
||||
weapons = {
|
||||
missiles = {}, -- Missiles
|
||||
@@ -589,6 +601,11 @@ ENUMS.Storage = {
|
||||
droptanks = {}, -- Droptanks
|
||||
adapters = {}, -- Adapter
|
||||
torpedoes = {}, -- Torpedoes
|
||||
Gazelle = {}, -- Gazelle specifics
|
||||
CH47 = {}, -- Chinook specifics
|
||||
OH58 = {}, -- Kiowa specifics
|
||||
UH1H = {}, -- Huey specifics
|
||||
AH64D = {}, -- Huey specifics
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1148,4 +1165,75 @@ ENUMS.Storage.weapons.bombs.BDU_50LD = "weapons.bombs.BDU_50LD"
|
||||
ENUMS.Storage.weapons.bombs.AGM_62 = "weapons.bombs.AGM_62"
|
||||
ENUMS.Storage.weapons.containers.US_M10_SMOKE_TANK_WHITE = "weapons.containers.{US_M10_SMOKE_TANK_WHITE}"
|
||||
ENUMS.Storage.weapons.missiles.MICA_T = "weapons.missiles.MICA_T"
|
||||
ENUMS.Storage.weapons.containers.HVAR_rocket = "weapons.containers.HVAR_rocket"
|
||||
ENUMS.Storage.weapons.containers.HVAR_rocket = "weapons.containers.HVAR_rocket"
|
||||
-- Gazelle
|
||||
ENUMS.Storage.weapons.Gazelle.HMP400_100RDS = {4,15,46,1771}
|
||||
ENUMS.Storage.weapons.Gazelle.HMP400_200RDS = {4,15,46,1770}
|
||||
ENUMS.Storage.weapons.Gazelle.HMP400_400RDS = {4,15,46,1769}
|
||||
ENUMS.Storage.weapons.Gazelle.GIAT_M261_AP = {4,15,46,1768}
|
||||
ENUMS.Storage.weapons.Gazelle.GIAT_M261_SAPHEI = {4,15,46,1767}
|
||||
ENUMS.Storage.weapons.Gazelle.GIAT_M261_HE = {4,15,46,1766}
|
||||
ENUMS.Storage.weapons.Gazelle.GIAT_M261_HEAP = {4,15,46,1765}
|
||||
ENUMS.Storage.weapons.Gazelle.GIAT_M261_APHE = {4,15,46,1764}
|
||||
ENUMS.Storage.weapons.Gazelle.GAZELLE_IR_DEFLECTOR = {4,15,47,680}
|
||||
ENUMS.Storage.weapons.Gazelle.GAZELLE_FAS_SANDFILTER = {4,15,47,679}
|
||||
-- Chinook
|
||||
ENUMS.Storage.weapons.CH47.CH47_PORT_M60D = {4,15,46,2476}
|
||||
ENUMS.Storage.weapons.CH47.CH47_STBD_M60D = {4,15,46,2477}
|
||||
ENUMS.Storage.weapons.CH47.CH47_AFT_M60D = {4,15,46,2478}
|
||||
ENUMS.Storage.weapons.CH47.CH47_PORT_M134D = {4,15,46,2482}
|
||||
ENUMS.Storage.weapons.CH47.CH47_STBD_M134D = {4,15,46,2483}
|
||||
ENUMS.Storage.weapons.CH47.CH47_AFT_M3M = {4,15,46,2484}
|
||||
ENUMS.Storage.weapons.CH47.CH47_PORT_M240H = {4,15,46,2479}
|
||||
ENUMS.Storage.weapons.CH47.CH47_STBD_M240H = {4,15,46,2480}
|
||||
ENUMS.Storage.weapons.CH47.CH47_AFT_M240H = {4,15,46,2481}
|
||||
-- Huey
|
||||
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Right = {4,15,46,161}
|
||||
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Left = {4,15,46,160}
|
||||
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Right_Door = {4,15,46,175}
|
||||
ENUMS.Storage.weapons.UH1H.M60_MG_Right_Door = {4,15,46,177}
|
||||
ENUMS.Storage.weapons.UH1H.M134_MiniGun_Left_Door = {4,15,46,174}
|
||||
ENUMS.Storage.weapons.UH1H.M60_MG_Left_Door = {4,15,46,176}
|
||||
-- Kiowa
|
||||
ENUMS.Storage.weapons.OH58.FIM92 = {4,4,7,449}
|
||||
ENUMS.Storage.weapons.OH58.MG_M3P100 = {4,15,46,2608}
|
||||
ENUMS.Storage.weapons.OH58.MG_M3P200 = {4,15,46,2607}
|
||||
ENUMS.Storage.weapons.OH58.MG_M3P300 = {4,15,46,2606}
|
||||
ENUMS.Storage.weapons.OH58.MG_M3P400 = {4,15,46,2605}
|
||||
ENUMS.Storage.weapons.OH58.MG_M3P500 = {4,15,46,2604}
|
||||
ENUMS.Storage.weapons.OH58.Smk_Grenade_Blue = {4,5,9,486}
|
||||
ENUMS.Storage.weapons.OH58.Smk_Grenade_Green = {4,5,9,487}
|
||||
ENUMS.Storage.weapons.OH58.Smk_Grenade_Red = {4,5,9,485}
|
||||
ENUMS.Storage.weapons.OH58.Smk_Grenade_Violet = {4,5,9,488}
|
||||
ENUMS.Storage.weapons.OH58.Smk_Grenade_White = {4,5,9,490}
|
||||
ENUMS.Storage.weapons.OH58.Smk_Grenade_Yellow = {4,5,9,489}
|
||||
-- Apache
|
||||
ENUMS.Storage.weapons.AH64D.AN_APG78 = {4,15,44,2138}
|
||||
ENUMS.Storage.weapons.AH64D.Internal_Aux_FuelTank = {1,3,43,1700}
|
||||
|
||||
---
|
||||
-- @type ENUMS.FARPType
|
||||
-- @field #string FARP
|
||||
-- @field #string INVISIBLE
|
||||
-- @field #string HELIPADSINGLE
|
||||
-- @field #string PADSINGLE
|
||||
ENUMS.FARPType = {
|
||||
FARP = "FARP",
|
||||
INVISIBLE = "INVISIBLE",
|
||||
HELIPADSINGLE = "HELIPADSINGLE",
|
||||
PADSINGLE = "PADSINGLE",
|
||||
}
|
||||
|
||||
|
||||
---
|
||||
-- @type ENUMS.FARPObjectTypeNamesAndShape
|
||||
-- @field #string FARP
|
||||
-- @field #string INVISIBLE
|
||||
-- @field #string HELIPADSINGLE
|
||||
-- @field #string PADSINGLE
|
||||
ENUMS.FARPObjectTypeNamesAndShape ={
|
||||
[ENUMS.FARPType.FARP] = { TypeName="FARP", ShapeName="FARPS"},
|
||||
[ENUMS.FARPType.INVISIBLE] = { TypeName="Invisible FARP", ShapeName="invisiblefarp"},
|
||||
[ENUMS.FARPType.HELIPADSINGLE] = { TypeName="SINGLE_HELIPAD", ShapeName="FARP"},
|
||||
[ENUMS.FARPType.PADSINGLE] = { TypeName="FARP_SINGLE_01", ShapeName="FARP_SINGLE_01"},
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ BIGSMOKEPRESET = {
|
||||
-- @field #string Falklands South Atlantic map.
|
||||
-- @field #string Sinai Sinai map.
|
||||
-- @field #string Kola Kola map.
|
||||
-- @field #string Afghanistan Afghanistan map
|
||||
-- @field #string Iraq Iraq map
|
||||
DCSMAP = {
|
||||
Caucasus="Caucasus",
|
||||
NTTR="Nevada",
|
||||
@@ -68,6 +70,7 @@ DCSMAP = {
|
||||
Sinai="SinaiMap",
|
||||
Kola="Kola",
|
||||
Afghanistan="Afghanistan",
|
||||
Iraq="Iraq"
|
||||
}
|
||||
|
||||
|
||||
@@ -1221,7 +1224,7 @@ function UTILS.SecondsToClock(seconds, short)
|
||||
end
|
||||
|
||||
-- Seconds
|
||||
local seconds = tonumber(seconds)
|
||||
local seconds = tonumber(seconds) or 0
|
||||
|
||||
-- Seconds of this day.
|
||||
local _seconds=seconds%(60*60*24)
|
||||
@@ -1786,6 +1789,8 @@ function UTILS.GetMagneticDeclination(map)
|
||||
declination=15
|
||||
elseif map==DCSMAP.Afghanistan then
|
||||
declination=3
|
||||
elseif map==DCSMAP.Iraq then
|
||||
declination=4.4
|
||||
else
|
||||
declination=0
|
||||
end
|
||||
@@ -2122,9 +2127,9 @@ function UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, Rising, Tlocal)
|
||||
local cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude))
|
||||
|
||||
if rising and cosH > 1 then
|
||||
return "N/R" -- The sun never rises on this location on the specified date
|
||||
return "N/S" -- The sun never rises on this location on the specified date
|
||||
elseif cosH < -1 then
|
||||
return "N/S" -- The sun never sets on this location on the specified date
|
||||
return "N/R" -- The sun never sets on this location on the specified date
|
||||
end
|
||||
|
||||
-- Finish calculating H and convert into hours
|
||||
@@ -2315,9 +2320,9 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
||||
return true
|
||||
end
|
||||
|
||||
if type_name == "OH-58D" and (unit:getDrawArgumentValue(35) > 0 or unit:getDrawArgumentValue(421) == -1) then
|
||||
BASE:T(unit_name .. " cargo door is open")
|
||||
return true
|
||||
if type_name == "OH58D" then
|
||||
BASE:T(unit_name .. " front door(s) are open")
|
||||
return true -- no doors on this one ;)
|
||||
end
|
||||
|
||||
if type_name == "CH-47Fbl1" and (unit:getDrawArgumentValue(86) > 0.5) then
|
||||
@@ -2350,17 +2355,19 @@ end
|
||||
--- Function to generate valid VHF frequencies in kHz for radio beacons (FM).
|
||||
-- @return #table VHFrequencies
|
||||
function UTILS.GenerateVHFrequencies()
|
||||
|
||||
|
||||
-- known and sorted map-wise NDBs in kHz
|
||||
local _skipFrequencies = {
|
||||
214,274,291.5,295,297.5,
|
||||
300.5,304,305,307,309.5,311,312,312.5,316,
|
||||
320,324,328,329,330,332,336,337,
|
||||
342,343,348,351,352,353,358,
|
||||
363,365,368,372.5,374,
|
||||
380,381,384,385,389,395,396,
|
||||
414,420,430,432,435,440,450,455,462,470,485,
|
||||
507,515,520,525,528,540,550,560,570,577,580,
|
||||
214,243,264,273,274,288,291.5,295,297.5,
|
||||
300.5,304,305,307,309.5,310,311,312,312.5,316,317,
|
||||
320,323,324,325,326,328,329,330,332,335,336,337,
|
||||
340,342,343,346,348,351,352,353,358,
|
||||
360,363,364,365,368,372.5,373,374,
|
||||
380,381,384,385,387,389,391,395,396,399,
|
||||
403,404,410,412,414,418,420,423,
|
||||
430,432,435,440,445,
|
||||
450,455,462,470,485,490,
|
||||
507,515,520,525,528,540,550,560,563,570,577,580,595,
|
||||
602,625,641,662,670,680,682,690,
|
||||
705,720,722,730,735,740,745,750,770,795,
|
||||
822,830,862,866,
|
||||
@@ -2521,7 +2528,7 @@ end
|
||||
--- Function to save an object to a file
|
||||
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||
-- @param #string Filename The name of the file. Existing file will be overwritten.
|
||||
-- @param #table Data The LUA data structure to save. This will be e.g. a table of text lines with an \\n at the end of each line.
|
||||
-- @param #string Data The data structure to save. This will be e.g. a string of text lines with an \\n at the end of each line.
|
||||
-- @return #boolean outcome True if saving is possible, else false.
|
||||
function UTILS.SaveToFile(Path,Filename,Data)
|
||||
-- Thanks to @FunkyFranky
|
||||
@@ -4090,3 +4097,110 @@ function UTILS.LCGRandom()
|
||||
UTILS.lcg.seed = (UTILS.lcg.a * UTILS.lcg.seed + UTILS.lcg.c) % UTILS.lcg.m
|
||||
return UTILS.lcg.seed / UTILS.lcg.m
|
||||
end
|
||||
|
||||
--- Spawns a new FARP of a defined type and coalition and functional statics (fuel depot, ammo storage, tent, windsock) around that FARP to make it operational.
|
||||
-- Adds vehicles from template if given. Fills the FARP warehouse with liquids and known materiels.
|
||||
-- References: [DCS Forum Topic](https://forum.dcs.world/topic/282989-farp-equipment-to-run-it)
|
||||
-- @param #string Name Name of this FARP installation. Must be unique.
|
||||
-- @param Core.Point#COORDINATE Coordinate Where to spawn the FARP.
|
||||
-- @param #string FARPType Type of FARP, can be one of the known types ENUMS.FARPType.FARP, ENUMS.FARPType.INVISIBLE, ENUMS.FARPType.HELIPADSINGLE, ENUMS.FARPType.PADSINGLE. Defaults to ENUMS.FARPType.FARP.
|
||||
-- @param #number Coalition Coalition of this FARP, i.e. coalition.side.BLUE or coalition.side.RED, defaults to coalition.side.BLUE.
|
||||
-- @param #number Country Country of this FARP, defaults to country.id.USA (blue) or country.id.RUSSIA (red).
|
||||
-- @param #number CallSign Callsign of the FARP ATC, defaults to CALLSIGN.FARP.Berlin.
|
||||
-- @param #number Frequency Frequency of the FARP ATC Radio, defaults to 127.5 (MHz).
|
||||
-- @param #number Modulation Modulation of the FARP ATC Radio, defaults to radio.modulation.AM.
|
||||
-- @param #number ADF ADF Beacon (FM) Frequency in KHz, e.g. 428. If not nil, creates an VHF/FM ADF Beacon for this FARP. Requires a sound called "beacon.ogg" to be in the mission (trigger "sound to" ...)
|
||||
-- @param #number SpawnRadius Radius of the FARP, i.e. where the FARP objects will be placed in meters, not more than 150m away. Defaults to 100.
|
||||
-- @param #string VehicleTemplate, template name for additional vehicles. Can be nil for no additional vehicles.
|
||||
-- @param #number Liquids Tons of fuel to be added initially to the FARP. Defaults to 10 (tons). Set to 0 for no fill.
|
||||
-- @param #number Equipment Number of equipment items per known item to be added initially to the FARP. Defaults to 10 (items). Set to 0 for no fill.
|
||||
-- @return #list<Wrapper.Static#STATIC> Table of spawned objects and vehicle object (if given).
|
||||
-- @return #string ADFBeaconName Name of the ADF beacon, to be able to remove/stop it later.
|
||||
function UTILS.SpawnFARPAndFunctionalStatics(Name,Coordinate,FARPType,Coalition,Country,CallSign,Frequency,Modulation,ADF,SpawnRadius,VehicleTemplate,Liquids,Equipment)
|
||||
|
||||
-- Set Defaults
|
||||
local farplocation = Coordinate
|
||||
local farptype = FARPType or ENUMS.FARPType.FARP
|
||||
local Coalition = Coalition or coalition.side.BLUE
|
||||
local callsign = CallSign or CALLSIGN.FARP.Berlin
|
||||
local freq = Frequency or 127.5
|
||||
local mod = Modulation or radio.modulation.AM
|
||||
local radius = SpawnRadius or 100
|
||||
if radius < 0 or radius > 150 then radius = 100 end
|
||||
local liquids = Liquids or 10
|
||||
liquids = liquids * 1000 -- tons to kg
|
||||
local equip = Equipment or 10
|
||||
local statictypes = ENUMS.FARPObjectTypeNamesAndShape[farptype] or {TypeName="FARP", ShapeName="FARPS"}
|
||||
local STypeName = statictypes.TypeName
|
||||
local SShapeName = statictypes.ShapeName
|
||||
local Country = Country or (Coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA)
|
||||
local ReturnObjects = {}
|
||||
|
||||
-- Spawn FARP
|
||||
local newfarp = SPAWNSTATIC:NewFromType(STypeName,"Heliports",Country) -- "Invisible FARP" "FARP"
|
||||
newfarp:InitShape(SShapeName) -- "invisiblefarp" "FARPS"
|
||||
newfarp:InitFARP(callsign,freq,mod)
|
||||
local spawnedfarp = newfarp:SpawnFromCoordinate(farplocation,0,Name)
|
||||
table.insert(ReturnObjects,spawnedfarp)
|
||||
-- Spawn Objects
|
||||
local FARPStaticObjectsNato = {
|
||||
["FUEL"] = { TypeName = "FARP Fuel Depot", ShapeName = "GSM Rus", Category = "Fortifications"},
|
||||
["AMMO"] = { TypeName = "FARP Ammo Dump Coating", ShapeName = "SetkaKP", Category = "Fortifications"},
|
||||
["TENT"] = { TypeName = "FARP Tent", ShapeName = "PalatkaB", Category = "Fortifications"},
|
||||
["WINDSOCK"] = { TypeName = "Windsock", ShapeName = "H-Windsock_RW", Category = "Fortifications"},
|
||||
}
|
||||
|
||||
local farpobcount = 0
|
||||
for _name,_object in pairs(FARPStaticObjectsNato) do
|
||||
local objloc = farplocation:Translate(radius,farpobcount*30)
|
||||
local heading = objloc:HeadingTo(farplocation)
|
||||
local newobject = SPAWNSTATIC:NewFromType(_object.TypeName,_object.Category,Country)
|
||||
newobject:InitShape(_object.ShapeName)
|
||||
newobject:InitHeading(heading)
|
||||
newobject:SpawnFromCoordinate(objloc,farpobcount*30,_name.." - "..Name)
|
||||
table.insert(ReturnObjects,newobject)
|
||||
farpobcount = farpobcount + 1
|
||||
end
|
||||
|
||||
-- Vehicle if any
|
||||
if VehicleTemplate and type(VehicleTemplate) == "string" then
|
||||
local vcoordinate = farplocation:Translate(radius,farpobcount*30)
|
||||
local heading = vcoordinate:HeadingTo(farplocation)
|
||||
local vehicles = SPAWN:NewWithAlias(VehicleTemplate,"FARP Vehicles - "..Name)
|
||||
vehicles:InitGroupHeading(heading)
|
||||
vehicles:InitCountry(Country)
|
||||
vehicles:InitCoalition(Coalition)
|
||||
vehicles:InitDelayOff()
|
||||
local spawnedvehicle = vehicles:SpawnFromCoordinate(vcoordinate)
|
||||
table.insert(ReturnObjects,spawnedvehicle)
|
||||
end
|
||||
|
||||
local newWH = STORAGE:New(Name)
|
||||
if liquids and liquids > 0 then
|
||||
-- Storage fill-up
|
||||
newWH:SetLiquid(STORAGE.Liquid.DIESEL,liquids) -- kgs to tons
|
||||
newWH:SetLiquid(STORAGE.Liquid.GASOLINE,liquids)
|
||||
newWH:SetLiquid(STORAGE.Liquid.JETFUEL,liquids)
|
||||
newWH:SetLiquid(STORAGE.Liquid.MW50,liquids)
|
||||
end
|
||||
|
||||
if equip and equip > 0 then
|
||||
for cat,nitem in pairs(ENUMS.Storage.weapons) do
|
||||
for name,item in pairs(nitem) do
|
||||
newWH:SetItem(item,equip)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local ADFName
|
||||
if ADF and type(ADF) == "number" then
|
||||
local ADFFreq = ADF*1000 -- KHz to Hz
|
||||
local Sound = "l10n/DEFAULT/beacon.ogg"
|
||||
local vec3 = farplocation:GetVec3()
|
||||
ADFName = Name .. " ADF "..tostring(ADF).."KHz"
|
||||
--BASE:I(string.format("Adding FARP Beacon %d KHz Name %s",ADF,ADFName))
|
||||
trigger.action.radioTransmission(Sound, vec3, 0, true, ADFFreq, 250, ADFName)
|
||||
end
|
||||
|
||||
return ReturnObjects, ADFName
|
||||
end
|
||||
|
||||
@@ -63,6 +63,11 @@
|
||||
-- To be able to distinguish easily in your code the difference between a AIRBASE API call and a DCS Airbase API call,
|
||||
-- the first letter of the method is also capitalized. So, by example, the DCS Airbase method DCSWrapper.Airbase#Airbase.getName()
|
||||
-- is implemented in the AIRBASE class as @{#AIRBASE.GetName}().
|
||||
--
|
||||
-- ## Note on the "H" heli pads in the Syria map:
|
||||
--
|
||||
-- As of the time of writing (Oct 2024, DCS DCS 2.9.8.1107), these 143 objects have the **same name and object ID**, which makes them unusable in Moose, e.g. you cannot find a specific one for spawning etc.
|
||||
-- Waiting for Ugra and ED to fix this issue.
|
||||
--
|
||||
-- @field #AIRBASE AIRBASE
|
||||
AIRBASE = {
|
||||
@@ -248,6 +253,9 @@ AIRBASE.Nevada = {
|
||||
-- * AIRBASE.Normandy.Villacoublay
|
||||
-- * AIRBASE.Normandy.Vrigny
|
||||
-- * AIRBASE.Normandy.West_Malling
|
||||
-- * AIRBASE.Normandy.Eastchurch
|
||||
-- * AIRBASE.Normandy.Headcorn
|
||||
-- * AIRBASE.Normandy.Hawkinge
|
||||
--
|
||||
-- @field Normandy
|
||||
AIRBASE.Normandy = {
|
||||
@@ -330,6 +338,9 @@ AIRBASE.Normandy = {
|
||||
["Villacoublay"] = "Villacoublay",
|
||||
["Vrigny"] = "Vrigny",
|
||||
["West_Malling"] = "West Malling",
|
||||
["Eastchurch"] = "Eastchurch",
|
||||
["Headcorn"] = "Headcorn",
|
||||
["Hawkinge"] = "Hawkinge",
|
||||
}
|
||||
|
||||
--- Airbases of the Persion Gulf Map:
|
||||
@@ -450,6 +461,7 @@ AIRBASE.TheChannel = {
|
||||
-- * AIRBASE.Syria.Gaziantep
|
||||
-- * AIRBASE.Syria.Gazipasa
|
||||
-- * AIRBASE.Syria.Gecitkale
|
||||
-- * AIRBASE.Syria.H
|
||||
-- * AIRBASE.Syria.H3
|
||||
-- * AIRBASE.Syria.H3_Northwest
|
||||
-- * AIRBASE.Syria.H3_Southwest
|
||||
@@ -497,6 +509,10 @@ AIRBASE.TheChannel = {
|
||||
-- * AIRBASE.Syria.Tha_lah
|
||||
-- * AIRBASE.Syria.Tiyas
|
||||
-- * AIRBASE.Syria.Wujah_Al_Hajar
|
||||
-- * AIRBASE.Syria.Ben_Gurion
|
||||
-- * AIRBASE.Syria.Hatzor
|
||||
-- * AIRBASE.Syria.Palmashim
|
||||
-- * AIRBASE.Syria.Tel_Nof
|
||||
--
|
||||
--@field Syria
|
||||
AIRBASE.Syria={
|
||||
@@ -518,6 +534,7 @@ AIRBASE.Syria={
|
||||
["Gaziantep"] = "Gaziantep",
|
||||
["Gazipasa"] = "Gazipasa",
|
||||
["Gecitkale"] = "Gecitkale",
|
||||
["H"] = "H",
|
||||
["H3"] = "H3",
|
||||
["H3_Northwest"] = "H3 Northwest",
|
||||
["H3_Southwest"] = "H3 Southwest",
|
||||
@@ -565,6 +582,10 @@ AIRBASE.Syria={
|
||||
["Tha_lah"] = "Tha'lah",
|
||||
["Tiyas"] = "Tiyas",
|
||||
["Wujah_Al_Hajar"] = "Wujah Al Hajar",
|
||||
["Ben_Gurion"] = "Ben Gurion",
|
||||
["Hatzor"] = "Hatzor",
|
||||
["Palmashim"] = "Palmashim",
|
||||
["Tel_Nof"] = "Tel Nof",
|
||||
}
|
||||
|
||||
--- Airbases of the Mariana Islands map:
|
||||
@@ -752,12 +773,14 @@ AIRBASE.Sinai = {
|
||||
--
|
||||
-- * AIRBASE.Kola.Banak
|
||||
-- * AIRBASE.Kola.Bodo
|
||||
-- * AIRBASE.Kola.Ivalo
|
||||
-- * AIRBASE.Kola.Jokkmokk
|
||||
-- * AIRBASE.Kola.Kalixfors
|
||||
-- * AIRBASE.Kola.Kallax
|
||||
-- * AIRBASE.Kola.Kemi_Tornio
|
||||
-- * AIRBASE.Kola.Kirkenes
|
||||
-- * AIRBASE.Kola.Kiruna
|
||||
-- * AIRBASE.Kola.Kuusamo
|
||||
-- * AIRBASE.Kola.Monchegorsk
|
||||
-- * AIRBASE.Kola.Murmansk_International
|
||||
-- * AIRBASE.Kola.Olenya
|
||||
@@ -766,25 +789,31 @@ AIRBASE.Sinai = {
|
||||
-- * AIRBASE.Kola.Severomorsk_3
|
||||
-- * AIRBASE.Kola.Vidsel
|
||||
-- * AIRBASE.Kola.Vuojarvi
|
||||
--
|
||||
-- * AIRBASE.Kola.Andoya
|
||||
-- * AIRBASE.Kola.Alakourtti
|
||||
--
|
||||
-- @field Kola
|
||||
AIRBASE.Kola = {
|
||||
["Banak"] = "Banak",
|
||||
["Bodo"] = "Bodo",
|
||||
["Ivalo"] = "Ivalo",
|
||||
["Jokkmokk"] = "Jokkmokk",
|
||||
["Kalixfors"] = "Kalixfors",
|
||||
["Kallax"] = "Kallax",
|
||||
["Kemi_Tornio"] = "Kemi Tornio",
|
||||
["Kirkenes"] = "Kirkenes",
|
||||
["Kiruna"] = "Kiruna",
|
||||
["Kuusamo"] = "Kuusamo",
|
||||
["Monchegorsk"] = "Monchegorsk",
|
||||
["Murmansk_International"] = "Murmansk International",
|
||||
["Olenya"] = "Olenya",
|
||||
["Rovaniemi"] = "Rovaniemi",
|
||||
["Severomorsk_1"] = "Severomorsk-1",
|
||||
["Severomorsk_3"] = "Severomorsk-3",
|
||||
["Vuojarvi"] = "Vuojarvi",
|
||||
["Kirkenes"] = "Kirkenes",
|
||||
["Kallax"] = "Kallax",
|
||||
["Vidsel"] = "Vidsel",
|
||||
["Vuojarvi"] = "Vuojarvi",
|
||||
["Andoya"] = "Andoya",
|
||||
["Alakourtti"] = "Alakourtti",
|
||||
}
|
||||
|
||||
--- Airbases of the Afghanistan map
|
||||
@@ -824,6 +853,39 @@ AIRBASE.Afghanistan = {
|
||||
["Tarinkot"] = "Tarinkot",
|
||||
}
|
||||
|
||||
--- Airbases of the Iraq map
|
||||
--
|
||||
-- * `AIRBASE.Iraq.Baghdad_International_Airport` Baghdad International Airport
|
||||
-- * `AIRBASE.Iraq.Sulaimaniyah_International_Airport` Sulaimaniyah International Airport
|
||||
-- * `AIRBASE.Iraq.Al_Sahra_Airport` Al-Sahra Airport
|
||||
-- * `AIRBASE.Iraq.Erbil_International_Airport` Erbil International Airport
|
||||
-- * `AIRBASE.Iraq.Al_Taji_Airport` Al-Taji Airport
|
||||
-- * `AIRBASE.Iraq.Al_Asad_Airbase` Al-Asad Airbase
|
||||
-- * `AIRBASE.Iraq.Al_Salam_Airbase` Al-Salam Airbase
|
||||
-- * `AIRBASE.Iraq.Balad_Airbase` Balad Airbase
|
||||
-- * `AIRBASE.Iraq.Kirkuk_International_Airport` Kirkuk International Airport
|
||||
-- * `AIRBASE.Iraq.Bashur_Airport` Bashur Airport
|
||||
-- * `AIRBASE.Iraq.Al_Taquddum_Airport` Al-Taquddum Airport
|
||||
-- * `AIRBASE.Iraq.Qayyarah_Airfield_West` Qayyarah Airfield West
|
||||
-- * `AIRBASE.Iraq.K1_Base` K1 Base
|
||||
--
|
||||
-- @field Iraq
|
||||
AIRBASE.Iraq = {
|
||||
["Baghdad_International_Airport"] = "Baghdad International Airport",
|
||||
["Sulaimaniyah_International_Airport"] = "Sulaimaniyah International Airport",
|
||||
["Al_Sahra_Airport"] = "Al-Sahra Airport",
|
||||
["Erbil_International_Airport"] = "Erbil International Airport",
|
||||
["Al_Taji_Airport"] = "Al-Taji Airport",
|
||||
["Al_Asad_Airbase"] = "Al-Asad Airbase",
|
||||
["Al_Salam_Airbase"] = "Al-Salam Airbase",
|
||||
["Balad_Airbase"] = "Balad Airbase",
|
||||
["Kirkuk_International_Airport"] = "Kirkuk International Airport",
|
||||
["Bashur_Airport"] = "Bashur Airport",
|
||||
["Al_Taquddum_Airport"] = "Al-Taquddum Airport",
|
||||
["Qayyarah_Airfield_West"] = "Qayyarah Airfield West",
|
||||
["K1_Base"] = "K1 Base",
|
||||
}
|
||||
|
||||
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||
-- @type AIRBASE.ParkingSpot
|
||||
-- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot.
|
||||
@@ -926,7 +988,7 @@ function AIRBASE:Register(AirbaseName)
|
||||
|
||||
-- Debug info.
|
||||
--self:I({airbase=AirbaseName, descriptors=self.descriptors})
|
||||
|
||||
|
||||
-- Category.
|
||||
self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME
|
||||
|
||||
@@ -937,22 +999,22 @@ function AIRBASE:Register(AirbaseName)
|
||||
--end
|
||||
|
||||
-- Set category.
|
||||
if self.category==Airbase.Category.AIRDROME then
|
||||
self.isAirdrome=true
|
||||
elseif self.category==Airbase.Category.HELIPAD then
|
||||
if self.category==Airbase.Category.AIRDROME then
|
||||
self.isAirdrome=true
|
||||
elseif self.category==Airbase.Category.HELIPAD or self.descriptors.typeName=="FARP_SINGLE_01" then
|
||||
self.isHelipad=true
|
||||
elseif self.category==Airbase.Category.SHIP then
|
||||
self.isShip=true
|
||||
-- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects()
|
||||
if self.descriptors.typeName=="Oil rig" or self.descriptors.typeName=="Ga" then
|
||||
self.isHelipad=true
|
||||
elseif self.category==Airbase.Category.SHIP then
|
||||
self.isShip=true
|
||||
-- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects()
|
||||
if self.descriptors.typeName=="Oil rig" or self.descriptors.typeName=="Ga" then
|
||||
self.isHelipad=true
|
||||
self.isShip=false
|
||||
self.category=Airbase.Category.HELIPAD
|
||||
_DATABASE:AddStatic(AirbaseName)
|
||||
end
|
||||
else
|
||||
self:E("ERROR: Unknown airbase category!")
|
||||
self.isShip=false
|
||||
self.category=Airbase.Category.HELIPAD
|
||||
_DATABASE:AddStatic(AirbaseName)
|
||||
end
|
||||
else
|
||||
self:E("ERROR: Unknown airbase category!")
|
||||
end
|
||||
|
||||
-- Init Runways.
|
||||
self:_InitRunways()
|
||||
|
||||
@@ -174,7 +174,10 @@
|
||||
-- * @{#CONTROLLABLE.OptionKeepWeaponsOnThreat}
|
||||
--
|
||||
-- ## 5.5) Air-2-Air missile attack range:
|
||||
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets .
|
||||
-- * @{#CONTROLLABLE.OptionAAAttackRange}(): Defines the usage of A2A missiles against possible targets.
|
||||
--
|
||||
-- # 6) [GROUND] IR Maker Beacons for GROUPs and UNITs
|
||||
-- * @{#CONTROLLABLE:NewIRMarker}(): Create a blinking IR Marker on a GROUP or UNIT.
|
||||
--
|
||||
-- @field #CONTROLLABLE
|
||||
CONTROLLABLE = {
|
||||
@@ -899,7 +902,11 @@ function CONTROLLABLE:CommandEPLRS( SwitchOnOff, Delay )
|
||||
groupId = self:GetID(),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
--if self:IsGround() then
|
||||
--CommandEPLRS.params.groupId = self:GetID()
|
||||
--end
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.CommandEPLRS, { self, SwitchOnOff }, Delay )
|
||||
else
|
||||
@@ -934,7 +941,7 @@ function CONTROLLABLE:CommandSetUnlimitedFuel(OnOff, Delay)
|
||||
end
|
||||
|
||||
|
||||
--- Set radio frequency. See [DCS command EPLRS](https://wiki.hoggitworld.com/view/DCS_command_setFrequency)
|
||||
--- Set radio frequency. See [DCS command SetFrequency](https://wiki.hoggitworld.com/view/DCS_command_setFrequency)
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Frequency Radio frequency in MHz.
|
||||
-- @param #number Modulation Radio modulation. Default `radio.modulation.AM`.
|
||||
@@ -953,7 +960,7 @@ function CONTROLLABLE:CommandSetFrequency( Frequency, Modulation, Power, Delay )
|
||||
}
|
||||
|
||||
if Delay and Delay > 0 then
|
||||
SCHEDULER:New( nil, self.CommandSetFrequency, { self, Frequency, Modulation, Power } )
|
||||
SCHEDULER:New( nil, self.CommandSetFrequency, { self, Frequency, Modulation, Power },Delay )
|
||||
else
|
||||
self:SetCommand( CommandSetFrequency )
|
||||
end
|
||||
@@ -961,7 +968,7 @@ function CONTROLLABLE:CommandSetFrequency( Frequency, Modulation, Power, Delay )
|
||||
return self
|
||||
end
|
||||
|
||||
--- [AIR] Set radio frequency. See [DCS command EPLRS](https://wiki.hoggitworld.com/view/DCS_command_setFrequencyForUnit)
|
||||
--- [AIR] Set radio frequency. See [DCS command SetFrequencyForUnit](https://wiki.hoggitworld.com/view/DCS_command_setFrequencyForUnit)
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Frequency Radio frequency in MHz.
|
||||
-- @param #number Modulation Radio modulation. Default `radio.modulation.AM`.
|
||||
@@ -980,7 +987,7 @@ function CONTROLLABLE:CommandSetFrequencyForUnit(Frequency,Modulation,Power,Unit
|
||||
},
|
||||
}
|
||||
if Delay and Delay>0 then
|
||||
SCHEDULER:New(nil,self.CommandSetFrequencyForUnit,{self,Frequency,Modulation,Power,UnitID})
|
||||
SCHEDULER:New(nil,self.CommandSetFrequencyForUnit,{self,Frequency,Modulation,Power,UnitID},Delay)
|
||||
else
|
||||
self:SetCommand(CommandSetFrequencyForUnit)
|
||||
end
|
||||
@@ -1006,7 +1013,11 @@ function CONTROLLABLE:TaskEPLRS( SwitchOnOff, idx )
|
||||
groupId = self:GetID(),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
--if self:IsGround() then
|
||||
--CommandEPLRS.params.groupId = self:GetID()
|
||||
--end
|
||||
|
||||
return self:TaskWrappedAction( CommandEPLRS, idx or 1 )
|
||||
end
|
||||
|
||||
@@ -1148,10 +1159,10 @@ function CONTROLLABLE:TaskStrafing( Vec2, AttackQty, Length, WeaponType, WeaponE
|
||||
id = 'Strafing',
|
||||
params = {
|
||||
point = Vec2, -- req
|
||||
weaponType = WeaponType or 1073741822,
|
||||
weaponType = WeaponType or 805337088, -- Default 805337088 corresponds to guns/cannons (805306368) + any rocket (30720). You can set other types but then the AI uses even bombs for a strafing run!
|
||||
expend = WeaponExpend or "Auto",
|
||||
attackQty = AttackQty or 1, -- req
|
||||
attackQtyLimit = AttackQty >1 and true or false,
|
||||
attackQtyLimit = AttackQty~=nil and true or false,
|
||||
direction = Direction and math.rad(Direction) or 0,
|
||||
directionEnabled = Direction and true or false,
|
||||
groupAttack = GroupAttack or false,
|
||||
@@ -2994,7 +3005,7 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad
|
||||
if DCSControllable then
|
||||
|
||||
local DetectionVisual = (DetectVisual and DetectVisual == true) and Controller.Detection.VISUAL or nil
|
||||
local DetectionOptical = (DetectOptical and DetectOptical == true) and Controller.Detection.OPTICAL or nil
|
||||
local DetectionOptical = (DetectOptical and DetectOptical == true) and Controller.Detection.OPTIC or nil
|
||||
local DetectionRadar = (DetectRadar and DetectRadar == true) and Controller.Detection.RADAR or nil
|
||||
local DetectionIRST = (DetectIRST and DetectIRST == true) and Controller.Detection.IRST or nil
|
||||
local DetectionRWR = (DetectRWR and DetectRWR == true) and Controller.Detection.RWR or nil
|
||||
@@ -3028,26 +3039,27 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Check if a target is detected.
|
||||
--- Check if a DCS object (unit or static) is detected by the controllable.
|
||||
-- Note that after a target is detected it remains "detected" for a certain amount of time, even if the controllable cannot "see" the target any more with it's sensors.
|
||||
-- The optional parametes specify the detection methods that can be applied.
|
||||
--
|
||||
-- If **no** detection method is given, the detection will use **all** the available methods by default.
|
||||
-- If **at least one** detection method is specified, only the methods set to *true* will be used.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param DCS#Object DCSObject The DCS object that is checked.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #boolean DetectVisual (Optional) If *false*, do not include visually detected targets.
|
||||
-- @param #boolean DetectOptical (Optional) If *false*, do not include optically detected targets.
|
||||
-- @param #boolean DetectRadar (Optional) If *false*, do not include targets detected by radar.
|
||||
-- @param #boolean DetectIRST (Optional) If *false*, do not include targets detected by IRST.
|
||||
-- @param #boolean DetectRWR (Optional) If *false*, do not include targets detected by RWR.
|
||||
-- @param #boolean DetectDLINK (Optional) If *false*, do not include targets detected by data link.
|
||||
-- @return #boolean True if target is detected.
|
||||
-- @return #boolean True if target is visible by line of sight.
|
||||
-- @return #number Mission time when target was detected.
|
||||
-- @return #boolean True if target type is known.
|
||||
-- @return #boolean True if distance to target is known.
|
||||
-- @return DCS#Vec3 Last known position vector of the target.
|
||||
-- @return DCS#Vec3 Last known velocity vector of the target.
|
||||
-- @return #boolean `true` if target is detected.
|
||||
-- @return #boolean `true` if target is *currently* visible by line of sight. Target must be detected (first parameter returns `true`).
|
||||
-- @return #boolean `true` if target type is known. Target must be detected (first parameter returns `true`).
|
||||
-- @return #boolean `true` if distance to target is known. Target must be detected (first parameter returns `true`).
|
||||
-- @return #number Mission time in seconds when target was last detected. Only present if the target is currently not visible (second parameter returns `false`) otherwise `nil` is returned.
|
||||
-- @return DCS#Vec3 Last known position vector of the target. Only present if the target is currently not visible (second parameter returns `false`) otherwise `nil` is returned.
|
||||
-- @return DCS#Vec3 Last known velocity vector of the target. Only present if the target is currently not visible (second parameter returns `false`) otherwise `nil` is returned.
|
||||
function CONTROLLABLE:IsTargetDetected( DCSObject, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK )
|
||||
self:F2( self.ControllableName )
|
||||
|
||||
@@ -3056,7 +3068,7 @@ function CONTROLLABLE:IsTargetDetected( DCSObject, DetectVisual, DetectOptical,
|
||||
if DCSControllable then
|
||||
|
||||
local DetectionVisual = (DetectVisual and DetectVisual == true) and Controller.Detection.VISUAL or nil
|
||||
local DetectionOptical = (DetectOptical and DetectOptical == true) and Controller.Detection.OPTICAL or nil
|
||||
local DetectionOptical = (DetectOptical and DetectOptical == true) and Controller.Detection.OPTIC or nil
|
||||
local DetectionRadar = (DetectRadar and DetectRadar == true) and Controller.Detection.RADAR or nil
|
||||
local DetectionIRST = (DetectIRST and DetectIRST == true) and Controller.Detection.IRST or nil
|
||||
local DetectionRWR = (DetectRWR and DetectRWR == true) and Controller.Detection.RWR or nil
|
||||
@@ -3064,10 +3076,10 @@ function CONTROLLABLE:IsTargetDetected( DCSObject, DetectVisual, DetectOptical,
|
||||
|
||||
local Controller = self:_GetController()
|
||||
|
||||
local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity
|
||||
local TargetIsDetected, TargetIsVisible, TargetKnowType, TargetKnowDistance, TargetLastTime, TargetLastPos, TargetLastVelocity
|
||||
= Controller:isTargetDetected( DCSObject, DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK )
|
||||
|
||||
return TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity
|
||||
return TargetIsDetected, TargetIsVisible, TargetKnowType, TargetKnowDistance, TargetLastTime, TargetLastPos, TargetLastVelocity
|
||||
end
|
||||
|
||||
return nil
|
||||
@@ -3075,6 +3087,7 @@ end
|
||||
|
||||
--- Check if a certain UNIT is detected by the controllable.
|
||||
-- The optional parametes specify the detection methods that can be applied.
|
||||
--
|
||||
-- If **no** detection method is given, the detection will use **all** the available methods by default.
|
||||
-- If **at least one** detection method is specified, only the methods set to *true* will be used.
|
||||
-- @param #CONTROLLABLE self
|
||||
@@ -3085,13 +3098,13 @@ end
|
||||
-- @param #boolean DetectIRST (Optional) If *false*, do not include targets detected by IRST.
|
||||
-- @param #boolean DetectRWR (Optional) If *false*, do not include targets detected by RWR.
|
||||
-- @param #boolean DetectDLINK (Optional) If *false*, do not include targets detected by data link.
|
||||
-- @return #boolean True if target is detected.
|
||||
-- @return #boolean True if target is visible by line of sight.
|
||||
-- @return #number Mission time when target was detected.
|
||||
-- @return #boolean True if target type is known.
|
||||
-- @return #boolean True if distance to target is known.
|
||||
-- @return DCS#Vec3 Last known position vector of the target.
|
||||
-- @return DCS#Vec3 Last known velocity vector of the target.
|
||||
-- @return #boolean `true` if target is detected.
|
||||
-- @return #boolean `true` if target is *currently* visible by line of sight. Target must be detected (first parameter returns `true`).
|
||||
-- @return #boolean `true` if target type is known. Target must be detected (first parameter returns `true`).
|
||||
-- @return #boolean `true` if distance to target is known. Target must be detected (first parameter returns `true`).
|
||||
-- @return #number Mission time in seconds when target was last detected. Only present if the target is currently not visible (second parameter returns `false`) otherwise `nil` is returned.
|
||||
-- @return DCS#Vec3 Last known position vector of the target. Only present if the target is currently not visible (second parameter returns `false`) otherwise `nil` is returned.
|
||||
-- @return DCS#Vec3 Last known velocity vector of the target. Only present if the target is currently not visible (second parameter returns `false`) otherwise `nil` is returned.
|
||||
function CONTROLLABLE:IsUnitDetected( Unit, DetectVisual, DetectOptical, DetectRadar, DetectIRST, DetectRWR, DetectDLINK )
|
||||
self:F2( self.ControllableName )
|
||||
|
||||
@@ -5619,3 +5632,128 @@ function CONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- IR Marker courtesy Florian Brinker (fbrinker)
|
||||
|
||||
--- [GROUND] Create and enable a new IR Marker for the given controllable UNIT or GROUP.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #boolean EnableImmediately (Optionally) If true start up the IR Marker immediately. Else you need to call `myobject:EnableIRMarker()` later on.
|
||||
-- @param #number Runtime (Optionally) Run this IR Marker for the given number of seconds, then stop. Use in conjunction with EnableImmediately.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:NewIRMarker(EnableImmediately, Runtime)
|
||||
--sefl:F("NewIRMarker")
|
||||
if self.ClassName == "GROUP" then
|
||||
self.IRMarkerGroup = true
|
||||
self.IRMarkerUnit = false
|
||||
elseif self.ClassName == "UNIT" then
|
||||
self.IRMarkerGroup = false
|
||||
self.IRMarkerUnit = true
|
||||
end
|
||||
|
||||
self.spot = nil
|
||||
self.timer = nil
|
||||
self.stoptimer = nil
|
||||
|
||||
if EnableImmediately and EnableImmediately == true then
|
||||
self:EnableIRMarker(Runtime)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- [GROUND] Enable the IR marker.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #number Runtime (Optionally) Run this IR Marker for the given number of seconds, then stop. Else run until you call `myobject:DisableIRMarker()`.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:EnableIRMarker(Runtime)
|
||||
--sefl:F("EnableIRMarker")
|
||||
if self.IRMarkerGroup == nil then
|
||||
self:NewIRMarker(true,Runtime)
|
||||
return
|
||||
end
|
||||
|
||||
if (self.IRMarkerGroup == true) then
|
||||
self:EnableIRMarkerForGroup()
|
||||
return
|
||||
end
|
||||
|
||||
self.timer = TIMER:New(CONTROLLABLE._MarkerBlink, self)
|
||||
self.timer:Start(nil, 1 - math.random(1, 5) / 10 / 2, Runtime) -- start randomized
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- [GROUND] Disable the IR marker.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:DisableIRMarker()
|
||||
--sefl:F("DisableIRMarker")
|
||||
if (self.IRMarkerGroup == true) then
|
||||
self:DisableIRMarkerForGroup()
|
||||
return
|
||||
end
|
||||
|
||||
if self.spot then
|
||||
self.spot:destroy()
|
||||
self.spot = nil
|
||||
if self.timer and self.timer:IsRunning() then
|
||||
self.timer:Stop()
|
||||
self.timer = nil
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [GROUND] Enable the IR markers for a whole group.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:EnableIRMarkerForGroup()
|
||||
--sefl:F("EnableIRMarkerForGroup")
|
||||
if self.ClassName == "GROUP" then
|
||||
local units = self:GetUnits() or {}
|
||||
for _,_unit in pairs(units) do
|
||||
_unit:EnableIRMarker()
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [GROUND] Disable the IR markers for a whole group.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:DisableIRMarkerForGroup()
|
||||
--sefl:F("DisableIRMarkerForGroup")
|
||||
if self.ClassName == "GROUP" then
|
||||
local units = self:GetUnits() or {}
|
||||
for _,_unit in pairs(units) do
|
||||
_unit:DisableIRMarker()
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] This method is called by the scheduler after enabling the IR marker.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:_MarkerBlink()
|
||||
--sefl:F("_MarkerBlink")
|
||||
if self:IsAlive() ~= true then
|
||||
self:DisableIRMarker()
|
||||
return
|
||||
end
|
||||
|
||||
self.timer.dT = 1 - (math.random(1, 2) / 10 / 2) -- randomize the blinking by a small amount
|
||||
|
||||
local _, _, unitBBHeight, _ = self:GetObjectSize()
|
||||
local unitPos = self:GetPositionVec3()
|
||||
|
||||
self.spot = Spot.createInfraRed(
|
||||
self.DCSUnit,
|
||||
{ x = 0, y = (unitBBHeight + 1), z = 0 },
|
||||
{ x = unitPos.x, y = (unitPos.y + unitBBHeight), z = unitPos.z }
|
||||
)
|
||||
|
||||
local offTimer = TIMER:New(function() if self.spot then self.spot:destroy() end end)
|
||||
offTimer:Start(0.5)
|
||||
return self
|
||||
end
|
||||
|
||||
506
Moose Development/Moose/Wrapper/DynamicCargo.lua
Normal file
506
Moose Development/Moose/Wrapper/DynamicCargo.lua
Normal file
@@ -0,0 +1,506 @@
|
||||
--- **Wrapper** - Dynamic Cargo create from the F8 menu.
|
||||
--
|
||||
-- ## Main Features:
|
||||
--
|
||||
-- * Convenient access to DCS API functions
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Wrapper/Storage).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **Applevangelist**
|
||||
--
|
||||
-- ===
|
||||
-- @module Wrapper.DynamicCargo
|
||||
-- @image Wrapper_Storage.png
|
||||
|
||||
|
||||
--- DYNAMICCARGO class.
|
||||
-- @type DYNAMICCARGO
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity level.
|
||||
-- @field #string lid Class id string for output to DCS log file.
|
||||
-- @field Wrapper.Storage#STORAGE warehouse The STORAGE object.
|
||||
-- @field #string version.
|
||||
-- @field #string CargoState.
|
||||
-- @field #table DCS#Vec3 LastPosition.
|
||||
-- @field #number Interval Check Interval. 20 secs default.
|
||||
-- @field #boolean testing
|
||||
-- @field Core.Timer#TIMER timer Timmer to run intervals
|
||||
-- @field #string Owner The playername who has created, loaded or unloaded this cargo. Depends on state.
|
||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||
|
||||
--- *The capitalist cannot store labour-power in warehouses after he has bought it, as he may do with the raw material.* -- Karl Marx
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The DYNAMICCARGO Concept
|
||||
--
|
||||
-- The DYNAMICCARGO class offers an easy-to-use wrapper interface to all DCS API functions of DCS dynamically spawned cargo crates.
|
||||
-- We named the class DYNAMICCARGO, because the name WAREHOUSE is already taken by another MOOSE class..
|
||||
--
|
||||
-- # Constructor
|
||||
--
|
||||
-- @field #DYNAMICCARGO
|
||||
DYNAMICCARGO = {
|
||||
ClassName = "DYNAMICCARGO",
|
||||
verbose = 0,
|
||||
testing = false,
|
||||
Interval = 10,
|
||||
|
||||
}
|
||||
|
||||
--- Liquid types.
|
||||
-- @type DYNAMICCARGO.Liquid
|
||||
-- @field #number JETFUEL Jet fuel (0).
|
||||
-- @field #number GASOLINE Aviation gasoline (1).
|
||||
-- @field #number MW50 MW50 (2).
|
||||
-- @field #number DIESEL Diesel (3).
|
||||
DYNAMICCARGO.Liquid = {
|
||||
JETFUEL = 0,
|
||||
GASOLINE = 1,
|
||||
MW50 = 2,
|
||||
DIESEL = 3,
|
||||
}
|
||||
|
||||
--- Liquid Names for the static cargo resource table.
|
||||
-- @type DYNAMICCARGO.LiquidName
|
||||
-- @field #number JETFUEL "jet_fuel".
|
||||
-- @field #number GASOLINE "gasoline".
|
||||
-- @field #number MW50 "methanol_mixture".
|
||||
-- @field #number DIESEL "diesel".
|
||||
DYNAMICCARGO.LiquidName = {
|
||||
GASOLINE = "gasoline",
|
||||
DIESEL = "diesel",
|
||||
MW50 = "methanol_mixture",
|
||||
JETFUEL = "jet_fuel",
|
||||
}
|
||||
|
||||
--- Storage types.
|
||||
-- @type DYNAMICCARGO.Type
|
||||
-- @field #number WEAPONS weapons.
|
||||
-- @field #number LIQUIDS liquids. Also see #list<#DYNAMICCARGO.Liquid> for types of liquids.
|
||||
-- @field #number AIRCRAFT aircraft.
|
||||
DYNAMICCARGO.Type = {
|
||||
WEAPONS = "weapons",
|
||||
LIQUIDS = "liquids",
|
||||
AIRCRAFT = "aircrafts",
|
||||
}
|
||||
|
||||
--- State types
|
||||
-- @type DYNAMICCARGO.State
|
||||
-- @field #string NEW
|
||||
-- @field #string LOADED
|
||||
-- @field #string UNLOADED
|
||||
-- @field #string REMOVED
|
||||
DYNAMICCARGO.State = {
|
||||
NEW = "NEW",
|
||||
LOADED = "LOADED",
|
||||
UNLOADED = "UNLOADED",
|
||||
REMOVED = "REMOVED",
|
||||
}
|
||||
|
||||
--- Helo types possible.
|
||||
-- @type DYNAMICCARGO.AircraftTypes
|
||||
DYNAMICCARGO.AircraftTypes = {
|
||||
["CH-47Fbl1"] = "CH-47Fbl1",
|
||||
}
|
||||
|
||||
--- Helo types possible.
|
||||
-- @type DYNAMICCARGO.AircraftDimensions
|
||||
DYNAMICCARGO.AircraftDimensions = {
|
||||
-- CH-47 model start coordinate is quite exactly in the middle of the model, so half values here
|
||||
["CH-47Fbl1"] = {
|
||||
["width"] = 4,
|
||||
["height"] = 6,
|
||||
["length"] = 11,
|
||||
["ropelength"] = 30,
|
||||
},
|
||||
}
|
||||
|
||||
--- DYNAMICCARGO class version.
|
||||
-- @field #string version
|
||||
DYNAMICCARGO.version="0.0.5"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new DYNAMICCARGO object from the DCS static cargo object.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @param #string CargoName Name of the Cargo.
|
||||
-- @return #DYNAMICCARGO self
|
||||
function DYNAMICCARGO:Register(CargoName)
|
||||
|
||||
-- Inherit everything from a BASE class.
|
||||
local self=BASE:Inherit(self, POSITIONABLE:New(CargoName)) -- #DYNAMICCARGO
|
||||
|
||||
self.StaticName = CargoName
|
||||
|
||||
self.LastPosition = self:GetCoordinate()
|
||||
|
||||
self.CargoState = DYNAMICCARGO.State.NEW
|
||||
|
||||
self.Interval = DYNAMICCARGO.Interval or 10
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
|
||||
if DCSObject then
|
||||
local warehouse = STORAGE:NewFromDynamicCargo(CargoName)
|
||||
self.warehouse = warehouse
|
||||
end
|
||||
|
||||
self.lid = string.format("DYNAMICCARGO %s", CargoName)
|
||||
|
||||
self.Owner = string.match(CargoName,"^(.+)|%d%d:%d%d|PKG%d+") or "None"
|
||||
|
||||
self.timer = TIMER:New(DYNAMICCARGO._UpdatePosition,self)
|
||||
self.timer:Start(self.Interval,self.Interval)
|
||||
|
||||
if not _DYNAMICCARGO_HELOS then
|
||||
_DYNAMICCARGO_HELOS = SET_CLIENT:New():FilterAlive():FilterFunction(DYNAMICCARGO._FilterHeloTypes):FilterStart()
|
||||
end
|
||||
|
||||
if self.testing then
|
||||
BASE:TraceOn()
|
||||
BASE:TraceClass("DYNAMICCARGO")
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get DCS object.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return DCS static object
|
||||
function DYNAMICCARGO:GetDCSObject()
|
||||
local DCSStatic = Unit.getByName( self.StaticName )
|
||||
if DCSStatic then
|
||||
return DCSStatic
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User API Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get last known owner name of this DYNAMICCARGO
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #string Owner
|
||||
function DYNAMICCARGO:GetLastOwner()
|
||||
return self.Owner
|
||||
end
|
||||
|
||||
--- Returns true if the cargo is new and has never been loaded into a Helo.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #boolean Outcome
|
||||
function DYNAMICCARGO:IsNew()
|
||||
if self.CargoState and self.CargoState == DYNAMICCARGO.State.NEW then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns true if the cargo been loaded into a Helo.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #boolean Outcome
|
||||
function DYNAMICCARGO:IsLoaded()
|
||||
if self.CargoState and self.CargoState == DYNAMICCARGO.State.LOADED then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns true if the cargo has been unloaded from a Helo.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #boolean Outcome
|
||||
function DYNAMICCARGO:IsUnloaded()
|
||||
if self.CargoState and self.CargoState == DYNAMICCARGO.State.REMOVED then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns true if the cargo has been removed.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #boolean Outcome
|
||||
function DYNAMICCARGO:IsRemoved()
|
||||
if self.CargoState and self.CargoState == DYNAMICCARGO.State.UNLOADED then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- [CTLD] Get number of crates this DYNAMICCARGO consists of. Always one.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #number crate number, always one
|
||||
function DYNAMICCARGO:GetCratesNeeded()
|
||||
return 1
|
||||
end
|
||||
|
||||
--- [CTLD] Get this DYNAMICCARGO drop state. True if DYNAMICCARGO.State.UNLOADED
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #boolean Dropped
|
||||
function DYNAMICCARGO:WasDropped()
|
||||
return self.CargoState == DYNAMICCARGO.State.UNLOADED and true or false
|
||||
end
|
||||
|
||||
--- [CTLD] Get CTLD_CARGO.Enum type of this DYNAMICCARGO
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #string Type, only one at the moment is CTLD_CARGO.Enum.GCLOADABLE
|
||||
function DYNAMICCARGO:GetType()
|
||||
return CTLD_CARGO.Enum.GCLOADABLE
|
||||
end
|
||||
|
||||
|
||||
--- Find last known position of this DYNAMICCARGO
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return DCS#Vec3 Position in 3D space
|
||||
function DYNAMICCARGO:GetLastPosition()
|
||||
return self.LastPosition
|
||||
end
|
||||
|
||||
--- Find current state of this DYNAMICCARGO
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return string The current state
|
||||
function DYNAMICCARGO:GetState()
|
||||
return self.CargoState
|
||||
end
|
||||
|
||||
--- Find a DYNAMICCARGO in the **_DATABASE** using the name associated with it.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @param #string Name The dynamic cargo name
|
||||
-- @return #DYNAMICCARGO self
|
||||
function DYNAMICCARGO:FindByName( Name )
|
||||
local storage = _DATABASE:FindDynamicCargo( Name )
|
||||
return storage
|
||||
end
|
||||
|
||||
--- Find the first(!) DYNAMICCARGO matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
|
||||
-- @param #DYNAMICCARGO 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.
|
||||
-- @return #DYNAMICCARGO The DYNAMICCARGO.
|
||||
-- @usage
|
||||
-- -- Find a dynamic cargo with a partial dynamic cargo name
|
||||
-- local grp = DYNAMICCARGO:FindByMatching( "Apple" )
|
||||
-- -- will return e.g. a dynamic cargo named "Apple|08:00|PKG08"
|
||||
--
|
||||
-- -- using a pattern
|
||||
-- local grp = DYNAMICCARGO:FindByMatching( ".%d.%d$" )
|
||||
-- -- will return the first dynamic cargo found ending in "-1-1" to "-9-9", but not e.g. "-10-1"
|
||||
function DYNAMICCARGO:FindByMatching( Pattern )
|
||||
local GroupFound = nil
|
||||
|
||||
for name,static in pairs(_DATABASE.DYNAMICCARGO) do
|
||||
if string.match(name, Pattern ) then
|
||||
GroupFound = static
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return GroupFound
|
||||
end
|
||||
|
||||
--- Find all DYNAMICCARGO objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
|
||||
-- @param #DYNAMICCARGO 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.
|
||||
-- @return #table Groups Table of matching #DYNAMICCARGO objects found
|
||||
-- @usage
|
||||
-- -- Find all dynamic cargo with a partial dynamic cargo name
|
||||
-- local grptable = DYNAMICCARGO:FindAllByMatching( "Apple" )
|
||||
-- -- will return all dynamic cargos with "Apple" in the name
|
||||
--
|
||||
-- -- using a pattern
|
||||
-- local grp = DYNAMICCARGO:FindAllByMatching( ".%d.%d$" )
|
||||
-- -- will return the all dynamic cargos found ending in "-1-1" to "-9-9", but not e.g. "-10-1" or "-1-10"
|
||||
function DYNAMICCARGO:FindAllByMatching( Pattern )
|
||||
local GroupsFound = {}
|
||||
|
||||
for name,static in pairs(_DATABASE.DYNAMICCARGO) do
|
||||
if string.match(name, Pattern ) then
|
||||
GroupsFound[#GroupsFound+1] = static
|
||||
end
|
||||
end
|
||||
|
||||
return GroupsFound
|
||||
end
|
||||
|
||||
--- Get the #STORAGE object from this dynamic cargo.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return Wrapper.Storage#STORAGE Storage The #STORAGE object
|
||||
function DYNAMICCARGO:GetStorageObject()
|
||||
return self.warehouse
|
||||
end
|
||||
|
||||
--- Get the weight in kgs from this dynamic cargo.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #number Weight in kgs.
|
||||
function DYNAMICCARGO:GetCargoWeight()
|
||||
local DCSObject = self:GetDCSObject()
|
||||
if DCSObject then
|
||||
local weight = DCSObject:getCargoWeight()
|
||||
return weight
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the cargo display name from this dynamic cargo.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #string The display name
|
||||
function DYNAMICCARGO:GetCargoDisplayName()
|
||||
local DCSObject = self:GetDCSObject()
|
||||
if DCSObject then
|
||||
local weight = DCSObject:getCargoDisplayName()
|
||||
return weight
|
||||
else
|
||||
return self.StaticName
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- [Internal] _Get Possible Player Helo Nearby
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @param Core.Point#COORDINATE pos
|
||||
-- @param #boolean loading If true measure distance for loading else for unloading
|
||||
-- @return #boolean Success
|
||||
-- @return Wrapper.Client#CLIENT Helo
|
||||
-- @return #string PlayerName
|
||||
function DYNAMICCARGO:_GetPossibleHeloNearby(pos,loading)
|
||||
local set = _DYNAMICCARGO_HELOS:GetAliveSet()
|
||||
local success = false
|
||||
local Helo = nil
|
||||
local Playername = nil
|
||||
for _,_helo in pairs (set or {}) do
|
||||
local helo = _helo -- Wrapper.Client#CLIENT
|
||||
local name = helo:GetPlayerName() or _DATABASE:_FindPlayerNameByUnitName(helo:GetName()) or "None"
|
||||
self:T(self.lid.." Checking: "..name)
|
||||
local hpos = helo:GetCoordinate()
|
||||
-- TODO Unloading via sling load?
|
||||
--local inair = hpos.y-hpos:GetLandHeight() > 4.5 and true or false -- Standard FARP is 4.5m
|
||||
local inair = helo:InAir()
|
||||
self:T(self.lid.." InAir: AGL/InAir: "..hpos.y-hpos:GetLandHeight().."/"..tostring(inair))
|
||||
local typename = helo:GetTypeName()
|
||||
if hpos and typename and inair == false then
|
||||
local dimensions = DYNAMICCARGO.AircraftDimensions[typename]
|
||||
if dimensions then
|
||||
local delta2D = hpos:Get2DDistance(pos)
|
||||
local delta3D = hpos:Get3DDistance(pos)
|
||||
if self.testing then
|
||||
self:T(string.format("Cargo relative position: 2D %dm | 3D %dm",delta2D,delta3D))
|
||||
self:T(string.format("Helo dimension: length %dm | width %dm | rope %dm",dimensions.length,dimensions.width,dimensions.ropelength))
|
||||
end
|
||||
if loading~=true and delta2D > dimensions.length or delta2D > dimensions.width or delta3D > dimensions.ropelength then
|
||||
success = true
|
||||
Helo = helo
|
||||
Playername = name
|
||||
end
|
||||
if loading == true and delta2D < dimensions.length or delta2D < dimensions.width or delta3D < dimensions.ropelength then
|
||||
success = true
|
||||
Helo = helo
|
||||
Playername = name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return success,Helo,Playername
|
||||
end
|
||||
|
||||
--- [Internal] Update internal states.
|
||||
-- @param #DYNAMICCARGO self
|
||||
-- @return #DYNAMICCARGO self
|
||||
function DYNAMICCARGO:_UpdatePosition()
|
||||
self:T(self.lid.." _UpdatePositionAndState")
|
||||
if self:IsAlive() then
|
||||
local pos = self:GetCoordinate()
|
||||
if self.testing then
|
||||
self:T(string.format("Cargo position: x=%d, y=%d, z=%d",pos.x,pos.y,pos.z))
|
||||
self:T(string.format("Last position: x=%d, y=%d, z=%d",self.LastPosition.x,self.LastPosition.y,self.LastPosition.z))
|
||||
end
|
||||
if UTILS.Round(UTILS.VecDist3D(pos,self.LastPosition),2) > 0.5 then
|
||||
---------------
|
||||
-- LOAD Cargo
|
||||
---------------
|
||||
if self.CargoState == DYNAMICCARGO.State.NEW then
|
||||
local isloaded, client, playername = self:_GetPossibleHeloNearby(pos,true)
|
||||
self:T(self.lid.." moved! NEW -> LOADED by "..tostring(playername))
|
||||
self.CargoState = DYNAMICCARGO.State.LOADED
|
||||
self.Owner = playername
|
||||
_DATABASE:CreateEventDynamicCargoLoaded(self)
|
||||
---------------
|
||||
-- UNLOAD Cargo
|
||||
---------------
|
||||
elseif self.CargoState == DYNAMICCARGO.State.LOADED then
|
||||
-- TODO add checker if we are in flight somehow
|
||||
-- ensure not just the helo is moving
|
||||
local count = _DYNAMICCARGO_HELOS:CountAlive()
|
||||
-- Testing
|
||||
local landheight = pos:GetLandHeight()
|
||||
local agl = pos.y-landheight
|
||||
agl = UTILS.Round(agl,2)
|
||||
self:T(self.lid.." AGL: "..agl or -1)
|
||||
local isunloaded = true
|
||||
local client
|
||||
local playername = self.Owner
|
||||
if count > 0 and (agl > 0 or self.testing) then
|
||||
self:T(self.lid.." Possible alive helos: "..count or -1)
|
||||
if agl ~= 0 or self.testing then
|
||||
isunloaded, client, playername = self:_GetPossibleHeloNearby(pos,false)
|
||||
end
|
||||
if isunloaded then
|
||||
self:T(self.lid.." moved! LOADED -> UNLOADED by "..tostring(playername))
|
||||
self.CargoState = DYNAMICCARGO.State.UNLOADED
|
||||
self.Owner = playername
|
||||
_DATABASE:CreateEventDynamicCargoUnloaded(self)
|
||||
end
|
||||
elseif count > 0 and agl == 0 then
|
||||
self:T(self.lid.." moved! LOADED -> UNLOADED by "..tostring(playername))
|
||||
self.CargoState = DYNAMICCARGO.State.UNLOADED
|
||||
self.Owner = playername
|
||||
_DATABASE:CreateEventDynamicCargoUnloaded(self)
|
||||
end
|
||||
end
|
||||
self.LastPosition = pos
|
||||
end
|
||||
else
|
||||
---------------
|
||||
-- REMOVED Cargo
|
||||
---------------
|
||||
if self.timer and self.timer:IsRunning() then self.timer:Stop() end
|
||||
self:T(self.lid.." dead! " ..self.CargoState.."-> REMOVED")
|
||||
self.CargoState = DYNAMICCARGO.State.REMOVED
|
||||
_DATABASE:CreateEventDynamicCargoRemoved(self)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Track helos for loaded/unloaded decision making.
|
||||
-- @param Wrapper.Client#CLIENT client
|
||||
-- @return #boolean IsIn
|
||||
function DYNAMICCARGO._FilterHeloTypes(client)
|
||||
if not client then return false end
|
||||
local typename = client:GetTypeName()
|
||||
local isinclude = DYNAMICCARGO.AircraftTypes[typename] ~= nil and true or false
|
||||
return isinclude
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -360,14 +360,25 @@ end
|
||||
-- @return DCS#Group The DCS Group.
|
||||
function GROUP:GetDCSObject()
|
||||
|
||||
-- Get DCS group.
|
||||
local DCSGroup = Group.getByName( self.GroupName )
|
||||
--if (not self.LastCallDCSObject) or (self.LastCallDCSObject and timer.getTime() - self.LastCallDCSObject > 1) then
|
||||
|
||||
if DCSGroup then
|
||||
return DCSGroup
|
||||
end
|
||||
-- Get DCS group.
|
||||
local DCSGroup = Group.getByName( self.GroupName )
|
||||
|
||||
self:T2(string.format("ERROR: Could not get DCS group object of group %s because DCS object could not be found!", tostring(self.GroupName)))
|
||||
if DCSGroup then
|
||||
self.LastCallDCSObject = timer.getTime()
|
||||
self.DCSObject = DCSGroup
|
||||
return DCSGroup
|
||||
-- else
|
||||
-- self.DCSObject = nil
|
||||
-- self.LastCallDCSObject = nil
|
||||
end
|
||||
|
||||
--else
|
||||
--return self.DCSObject
|
||||
--end
|
||||
|
||||
--self:E(string.format("ERROR: Could not get DCS group object of group %s because DCS object could not be found!", tostring(self.GroupName)))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -375,7 +386,7 @@ end
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return DCS#Position The 3D position vectors of the POSITIONABLE or #nil if the groups not existing or alive.
|
||||
function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3()
|
||||
self:F2( self.PositionableName )
|
||||
--self:F2( self.PositionableName )
|
||||
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
@@ -383,7 +394,7 @@ function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3
|
||||
local unit = DCSPositionable:getUnits()[1]
|
||||
if unit then
|
||||
local PositionablePosition = unit:getPosition().p
|
||||
self:T3( PositionablePosition )
|
||||
--self:T3( PositionablePosition )
|
||||
return PositionablePosition
|
||||
end
|
||||
end
|
||||
@@ -403,7 +414,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean `true` if the group is alive *and* active, `false` if the group is alive but inactive or `#nil` if the group does not exist anymore.
|
||||
function GROUP:IsAlive()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject() -- DCS#Group
|
||||
|
||||
@@ -412,7 +423,7 @@ function GROUP:IsAlive()
|
||||
local DCSUnit = DCSGroup:getUnit(1) -- DCS#Unit
|
||||
if DCSUnit then
|
||||
local GroupIsAlive = DCSUnit:isActive()
|
||||
self:T3( GroupIsAlive )
|
||||
--self:T3( GroupIsAlive )
|
||||
return GroupIsAlive
|
||||
end
|
||||
end
|
||||
@@ -425,7 +436,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean `true` if group is activated or `#nil` The group is not existing or alive.
|
||||
function GROUP:IsActive()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject() -- DCS#Group
|
||||
|
||||
@@ -468,26 +479,30 @@ end
|
||||
-- Ship:Destroy( false ) -- Don't generate an event upon destruction.
|
||||
--
|
||||
function GROUP:Destroy( GenerateEvent, delay )
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
if delay and delay>0 then
|
||||
self:ScheduleOnce(delay, GROUP.Destroy, self, GenerateEvent)
|
||||
else
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
--local DCSGroup = self:GetDCSObject()
|
||||
local DCSGroup = Group.getByName( self.GroupName )
|
||||
|
||||
if DCSGroup then
|
||||
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
|
||||
if GenerateEvent and GenerateEvent == true then
|
||||
if self:IsAir() then
|
||||
self:CreateEventCrash( timer.getTime(), UnitData )
|
||||
--self:ScheduleOnce(1,self.CreateEventCrash,self,timer.getTime(),UnitData)
|
||||
else
|
||||
self:CreateEventDead( timer.getTime(), UnitData )
|
||||
--self:ScheduleOnce(1,self.CreateEventDead,self,timer.getTime(),UnitData)
|
||||
end
|
||||
elseif GenerateEvent == false then
|
||||
-- Do nothing!
|
||||
else
|
||||
self:CreateEventRemoveUnit( timer.getTime(), UnitData )
|
||||
--self:ScheduleOnce(1,self.CreateEventRemoveUnit,self,timer.getTime(),UnitData)
|
||||
end
|
||||
end
|
||||
USERFLAG:New( self:GetName() ):Set( 100 )
|
||||
@@ -511,12 +526,12 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return DCS#Group.Category The category ID.
|
||||
function GROUP:GetCategory()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
if DCSGroup then
|
||||
local GroupCategory = DCSGroup:getCategory()
|
||||
self:T3( GroupCategory )
|
||||
--self:T3( GroupCategory )
|
||||
return GroupCategory
|
||||
end
|
||||
|
||||
@@ -527,7 +542,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship, Train.
|
||||
function GROUP:GetCategoryName()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
if DCSGroup then
|
||||
@@ -539,7 +554,7 @@ function GROUP:GetCategoryName()
|
||||
[Group.Category.TRAIN] = "Train",
|
||||
}
|
||||
local GroupCategory = DCSGroup:getCategory()
|
||||
self:T3( GroupCategory )
|
||||
--self:T3( GroupCategory )
|
||||
|
||||
return CategoryNames[GroupCategory]
|
||||
end
|
||||
@@ -547,20 +562,22 @@ function GROUP:GetCategoryName()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns the coalition of the DCS Group.
|
||||
-- @param #GROUP self
|
||||
-- @return DCS#coalition.side The coalition side of the DCS Group.
|
||||
function GROUP:GetCoalition()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
if DCSGroup then
|
||||
local GroupCoalition = DCSGroup:getCoalition()
|
||||
self:T3( GroupCoalition )
|
||||
return GroupCoalition
|
||||
--self:F2( self.GroupName )
|
||||
if self.GroupCoalition ~= nil then
|
||||
return self.GroupCoalition
|
||||
else
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
if DCSGroup then
|
||||
local GroupCoalition = DCSGroup:getCoalition()
|
||||
--self:T3( GroupCoalition )
|
||||
self.GroupCoalition = GroupCoalition
|
||||
return GroupCoalition
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -568,12 +585,12 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return DCS#country.id The country identifier or nil if the DCS Group is not existing or alive.
|
||||
function GROUP:GetCountry()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
if DCSGroup then
|
||||
local GroupCountry = DCSGroup:getUnit(1):getCountry()
|
||||
self:T3( GroupCountry )
|
||||
--self:T3( GroupCountry )
|
||||
return GroupCountry
|
||||
end
|
||||
|
||||
@@ -625,7 +642,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #number Speed in km/h.
|
||||
function GROUP:GetSpeedMax()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
if DCSGroup then
|
||||
@@ -658,7 +675,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #number Range in meters.
|
||||
function GROUP:GetRange()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
if DCSGroup then
|
||||
@@ -689,7 +706,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #table of Wrapper.Unit#UNIT objects, indexed by number.
|
||||
function GROUP:GetUnits()
|
||||
self:F2( { self.GroupName } )
|
||||
--self:F2( { self.GroupName } )
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
@@ -706,7 +723,7 @@ function GROUP:GetUnits()
|
||||
Units[#Units+1]=unit
|
||||
end
|
||||
end
|
||||
self:T3( Units )
|
||||
--self:T3( Units )
|
||||
return Units
|
||||
end
|
||||
|
||||
@@ -717,7 +734,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #list<Wrapper.Unit#UNIT> The list of player occupied @{Wrapper.Unit} objects of the @{Wrapper.Group}.
|
||||
function GROUP:GetPlayerUnits()
|
||||
self:F2( { self.GroupName } )
|
||||
--self:F2( { self.GroupName } )
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
@@ -729,7 +746,7 @@ function GROUP:GetPlayerUnits()
|
||||
Units[#Units+1] = PlayerUnit
|
||||
end
|
||||
end
|
||||
self:T3( Units )
|
||||
--self:T3( Units )
|
||||
return Units
|
||||
end
|
||||
|
||||
@@ -828,7 +845,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #number Number of alive units. If DCS group is nil, 0 is returned.
|
||||
function GROUP:CountAliveUnits()
|
||||
self:F3( { self.GroupName } )
|
||||
--self:F3( { self.GroupName } )
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
@@ -850,7 +867,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return Wrapper.Unit#UNIT First unit alive.
|
||||
function GROUP:GetFirstUnitAlive()
|
||||
self:F3({self.GroupName})
|
||||
--self:F3({self.GroupName})
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
@@ -870,7 +887,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return Wrapper.Unit#UNIT First unit or nil if it does not exist.
|
||||
function GROUP:GetFirstUnit()
|
||||
self:F3({self.GroupName})
|
||||
--self:F3({self.GroupName})
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
@@ -885,7 +902,7 @@ end
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
-- @return DCS#Vec3 The velocity Vec3 vector or `#nil` if the GROUP is not existing or alive.
|
||||
function GROUP:GetVelocityVec3()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
@@ -919,7 +936,7 @@ end
|
||||
-- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
|
||||
-- @return #number The altitude of the group or nil if is not existing or alive.
|
||||
function GROUP:GetAltitude(FromGround)
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
return self:GetHeight(FromGround)
|
||||
end
|
||||
|
||||
@@ -928,7 +945,7 @@ end
|
||||
-- @param #boolean FromGround Measure from the ground or from sea level (ASL). Provide **true** for measuring from the ground (AGL). **false** or **nil** if you measure from sea level.
|
||||
-- @return #number The height of the group or nil if is not existing or alive.
|
||||
function GROUP:GetHeight( FromGround )
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
@@ -964,12 +981,12 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #number The DCS Group initial size.
|
||||
function GROUP:GetInitialSize()
|
||||
self:F3( { self.GroupName } )
|
||||
--self:F3( { self.GroupName } )
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local GroupInitialSize = DCSGroup:getInitialSize()
|
||||
self:T3( GroupInitialSize )
|
||||
--self:T3( GroupInitialSize )
|
||||
return GroupInitialSize
|
||||
end
|
||||
|
||||
@@ -981,12 +998,12 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #table The DCS Units.
|
||||
function GROUP:GetDCSUnits()
|
||||
self:F2( { self.GroupName } )
|
||||
--self:F2( { self.GroupName } )
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local DCSUnits = DCSGroup:getUnits()
|
||||
self:T3( DCSUnits )
|
||||
--self:T3( DCSUnits )
|
||||
return DCSUnits
|
||||
end
|
||||
|
||||
@@ -999,7 +1016,7 @@ end
|
||||
-- @param #number delay Delay in seconds, before the group is activated.
|
||||
-- @return #GROUP self
|
||||
function GROUP:Activate(delay)
|
||||
self:F2( { self.GroupName } )
|
||||
--self:F2( { self.GroupName } )
|
||||
if delay and delay>0 then
|
||||
self:ScheduleOnce(delay, GROUP.Activate, self)
|
||||
else
|
||||
@@ -1008,18 +1025,32 @@ function GROUP:Activate(delay)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Deactivates an activated GROUP.
|
||||
-- @param #GROUP self
|
||||
-- @param #number delay Delay in seconds, before the group is activated.
|
||||
-- @return #GROUP self
|
||||
function GROUP:Deactivate(delay)
|
||||
--self:F2( { self.GroupName } )
|
||||
if delay and delay>0 then
|
||||
self:ScheduleOnce(delay, GROUP.Deactivate, self)
|
||||
else
|
||||
trigger.action.deactivateGroup( self:GetDCSObject() )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Gets the type name of the group.
|
||||
-- @param #GROUP self
|
||||
-- @return #string The type name of the group.
|
||||
function GROUP:GetTypeName()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local GroupTypeName = DCSGroup:getUnit(1):getTypeName()
|
||||
self:T3( GroupTypeName )
|
||||
--self:T3( GroupTypeName )
|
||||
return( GroupTypeName )
|
||||
end
|
||||
|
||||
@@ -1030,13 +1061,13 @@ end
|
||||
--@param #GROUP self
|
||||
--@return #string NatoReportingName or "Bogey" if unknown.
|
||||
function GROUP:GetNatoReportingName()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local GroupTypeName = DCSGroup:getUnit(1):getTypeName()
|
||||
self:T3( GroupTypeName )
|
||||
--self:T3( GroupTypeName )
|
||||
return UTILS.GetReportingName(GroupTypeName)
|
||||
end
|
||||
|
||||
@@ -1048,13 +1079,13 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #string The player name of the group.
|
||||
function GROUP:GetPlayerName()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local PlayerName = DCSGroup:getUnit(1):getPlayerName()
|
||||
self:T3( PlayerName )
|
||||
--self:T3( PlayerName )
|
||||
return( PlayerName )
|
||||
end
|
||||
|
||||
@@ -1066,13 +1097,13 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #string The CallSign of the first DCS Unit of the DCS Group.
|
||||
function GROUP:GetCallsign()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local GroupCallSign = DCSGroup:getUnit(1):getCallsign()
|
||||
self:T3( GroupCallSign )
|
||||
--self:T3( GroupCallSign )
|
||||
return GroupCallSign
|
||||
end
|
||||
|
||||
@@ -1149,13 +1180,13 @@ end
|
||||
-- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP.
|
||||
-- @return #nil The first UNIT is not existing or alive.
|
||||
function GROUP:GetPointVec2()
|
||||
self:F2(self.GroupName)
|
||||
--self:F2(self.GroupName)
|
||||
|
||||
local FirstUnit = self:GetUnit(1)
|
||||
|
||||
if FirstUnit then
|
||||
local FirstUnitPointVec2 = FirstUnit:GetPointVec2()
|
||||
self:T3(FirstUnitPointVec2)
|
||||
--self:T3(FirstUnitPointVec2)
|
||||
return FirstUnitPointVec2
|
||||
end
|
||||
|
||||
@@ -1237,13 +1268,13 @@ end
|
||||
-- @usage
|
||||
-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP
|
||||
function GROUP:GetRandomVec3(Radius)
|
||||
self:F2(self.GroupName)
|
||||
--self:F2(self.GroupName)
|
||||
|
||||
local FirstUnit = self:GetUnit(1)
|
||||
|
||||
if FirstUnit then
|
||||
local FirstUnitRandomPointVec3 = FirstUnit:GetRandomVec3(Radius)
|
||||
self:T3(FirstUnitRandomPointVec3)
|
||||
--self:T3(FirstUnitRandomPointVec3)
|
||||
return FirstUnitRandomPointVec3
|
||||
end
|
||||
|
||||
@@ -1256,9 +1287,9 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #number Mean heading of the GROUP in degrees or #nil The first UNIT is not existing or alive.
|
||||
function GROUP:GetHeading()
|
||||
self:F2(self.GroupName)
|
||||
--self:F2(self.GroupName)
|
||||
|
||||
self:F2(self.GroupName)
|
||||
--self:F2(self.GroupName)
|
||||
|
||||
local GroupSize = self:GetSize()
|
||||
local HeadingAccumulator = 0
|
||||
@@ -1287,7 +1318,7 @@ end
|
||||
-- @return #number The fuel state of the unit with the least amount of fuel.
|
||||
-- @return Wrapper.Unit#UNIT reference to #Unit object for further processing.
|
||||
function GROUP:GetFuelMin()
|
||||
self:F3(self.ControllableName)
|
||||
--self:F3(self.ControllableName)
|
||||
|
||||
if not self:GetDCSObject() then
|
||||
BASE:E( { "Cannot GetFuel", Group = self, Alive = self:IsAlive() } )
|
||||
@@ -1318,7 +1349,7 @@ end
|
||||
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
|
||||
-- @return #nil The GROUP is not existing or alive.
|
||||
function GROUP:GetFuelAvg()
|
||||
self:F( self.ControllableName )
|
||||
--self:F( self.ControllableName )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
|
||||
@@ -1328,7 +1359,7 @@ function GROUP:GetFuelAvg()
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
local UnitFuel = Unit:GetFuel() or 0
|
||||
self:F( { Fuel = UnitFuel } )
|
||||
--self:F( { Fuel = UnitFuel } )
|
||||
TotalFuel = TotalFuel + UnitFuel
|
||||
end
|
||||
local GroupFuel = TotalFuel / GroupSize
|
||||
@@ -1358,7 +1389,7 @@ end
|
||||
-- @return #number Number of missiles left.
|
||||
-- @return #number Number of artillery shells left (with explosive mass, included in shells; shells can also be machine gun ammo)
|
||||
function GROUP:GetAmmunition()
|
||||
self:F( self.ControllableName )
|
||||
--self:F( self.ControllableName )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
|
||||
@@ -1429,7 +1460,7 @@ end
|
||||
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
|
||||
-- @return #boolean Returns true if the Group is completely within the @{Core.Zone#ZONE_BASE}
|
||||
function GROUP:IsCompletelyInZone( Zone )
|
||||
self:F2( { self.GroupName, Zone } )
|
||||
--self:F2( { self.GroupName, Zone } )
|
||||
|
||||
if not self:IsAlive() then return false end
|
||||
|
||||
@@ -1449,7 +1480,7 @@ end
|
||||
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
|
||||
-- @return #boolean Returns true if the Group is partially within the @{Core.Zone#ZONE_BASE}
|
||||
function GROUP:IsPartlyInZone( Zone )
|
||||
self:F2( { self.GroupName, Zone } )
|
||||
--self:F2( { self.GroupName, Zone } )
|
||||
|
||||
local IsOneUnitInZone = false
|
||||
local IsOneUnitOutsideZone = false
|
||||
@@ -1485,7 +1516,7 @@ end
|
||||
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
|
||||
-- @return #boolean Returns true if the Group is not within the @{Core.Zone#ZONE_BASE}
|
||||
function GROUP:IsNotInZone( Zone )
|
||||
self:F2( { self.GroupName, Zone } )
|
||||
--self:F2( { self.GroupName, Zone } )
|
||||
|
||||
if not self:IsAlive() then return true end
|
||||
|
||||
@@ -1521,7 +1552,7 @@ end
|
||||
-- @param Core.Zone#ZONE_BASE Zone The zone to test.
|
||||
-- @return #number The number of UNITs that are in the @{Core.Zone}
|
||||
function GROUP:CountInZone( Zone )
|
||||
self:F2( {self.GroupName, Zone} )
|
||||
--self:F2( {self.GroupName, Zone} )
|
||||
local Count = 0
|
||||
|
||||
if not self:IsAlive() then return Count end
|
||||
@@ -1541,13 +1572,13 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean Air category evaluation result.
|
||||
function GROUP:IsAir()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local IsAirResult = DCSGroup:getCategory() == Group.Category.AIRPLANE or DCSGroup:getCategory() == Group.Category.HELICOPTER
|
||||
self:T3( IsAirResult )
|
||||
--self:T3( IsAirResult )
|
||||
return IsAirResult
|
||||
end
|
||||
|
||||
@@ -1558,13 +1589,13 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean true if DCS Group contains Helicopters.
|
||||
function GROUP:IsHelicopter()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local GroupCategory = DCSGroup:getCategory()
|
||||
self:T2( GroupCategory )
|
||||
--self:T2( GroupCategory )
|
||||
return GroupCategory == Group.Category.HELICOPTER
|
||||
end
|
||||
|
||||
@@ -1575,13 +1606,13 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean true if DCS Group contains AirPlanes.
|
||||
function GROUP:IsAirPlane()
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local GroupCategory = DCSGroup:getCategory()
|
||||
self:T2( GroupCategory )
|
||||
--self:T2( GroupCategory )
|
||||
return GroupCategory == Group.Category.AIRPLANE
|
||||
end
|
||||
|
||||
@@ -1592,13 +1623,13 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean true if DCS Group contains Ground troops.
|
||||
function GROUP:IsGround()
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local GroupCategory = DCSGroup:getCategory()
|
||||
self:T2( GroupCategory )
|
||||
--self:T2( GroupCategory )
|
||||
return GroupCategory == Group.Category.GROUND
|
||||
end
|
||||
|
||||
@@ -1609,13 +1640,13 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean true if DCS Group contains Ships.
|
||||
function GROUP:IsShip()
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
if DCSGroup then
|
||||
local GroupCategory = DCSGroup:getCategory()
|
||||
self:T2( GroupCategory )
|
||||
--self:T2( GroupCategory )
|
||||
return GroupCategory == Group.Category.SHIP
|
||||
end
|
||||
|
||||
@@ -1627,7 +1658,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #boolean All units on the ground result.
|
||||
function GROUP:AllOnGround()
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
@@ -1640,7 +1671,7 @@ function GROUP:AllOnGround()
|
||||
end
|
||||
end
|
||||
|
||||
self:T3( AllOnGroundResult )
|
||||
--self:T3( AllOnGroundResult )
|
||||
return AllOnGroundResult
|
||||
end
|
||||
|
||||
@@ -1695,7 +1726,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #number Maximum velocity found.
|
||||
function GROUP:GetMaxVelocity()
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
@@ -1723,7 +1754,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #number Minimum height found.
|
||||
function GROUP:GetMinHeight()
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
@@ -1751,7 +1782,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #number Maximum height found.
|
||||
function GROUP:GetMaxHeight()
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
@@ -1893,7 +1924,7 @@ end
|
||||
-- @param Core.Point#COORDINATE coordinate Coordinate where the group should be respawned.
|
||||
-- @return #GROUP self
|
||||
function GROUP:InitCoordinate(coordinate)
|
||||
self:F({coordinate=coordinate})
|
||||
--self:F({coordinate=coordinate})
|
||||
self.InitCoord=coordinate
|
||||
return self
|
||||
end
|
||||
@@ -1903,7 +1934,7 @@ end
|
||||
-- @param #boolean switch If true (or nil), enables the radio comms. If false, disables the radio for the spawned group.
|
||||
-- @return #GROUP self
|
||||
function GROUP:InitRadioCommsOnOff(switch)
|
||||
self:F({switch=switch})
|
||||
--self:F({switch=switch})
|
||||
if switch==true or switch==nil then
|
||||
self.InitRespawnRadio=true
|
||||
else
|
||||
@@ -1917,7 +1948,7 @@ end
|
||||
-- @param #number frequency The frequency in MHz.
|
||||
-- @return #GROUP self
|
||||
function GROUP:InitRadioFrequency(frequency)
|
||||
self:F({frequency=frequency})
|
||||
--self:F({frequency=frequency})
|
||||
|
||||
self.InitRespawnFreq=frequency
|
||||
|
||||
@@ -1929,7 +1960,7 @@ end
|
||||
-- @param #string modulation Either "FM" or "AM". If no value is given, modulation is set to AM.
|
||||
-- @return #GROUP self
|
||||
function GROUP:InitRadioModulation(modulation)
|
||||
self:F({modulation=modulation})
|
||||
--self:F({modulation=modulation})
|
||||
if modulation and modulation:lower()=="fm" then
|
||||
self.InitRespawnModu=radio.modulation.FM
|
||||
else
|
||||
@@ -1943,7 +1974,7 @@ end
|
||||
-- @param #string modex Tail number of the first unit.
|
||||
-- @return #GROUP self
|
||||
function GROUP:InitModex(modex)
|
||||
self:F({modex=modex})
|
||||
--self:F({modex=modex})
|
||||
if modex then
|
||||
self.InitRespawnModex=tonumber(modex)
|
||||
end
|
||||
@@ -1958,15 +1989,11 @@ end
|
||||
-- - @{#GROUP.InitHeight}: Set the height for the units in meters for the respawned group. (This is applicable for air units).
|
||||
-- - @{#GROUP.InitRandomizeHeading}: Randomize the headings for the units within the respawned group.
|
||||
-- - @{#GROUP.InitZone}: Set the respawn @{Core.Zone} for the respawned group.
|
||||
-- - @{#GROUP.InitRandomizeZones}: Randomize the respawn @{Core.Zone} between one of the @{Core.Zone}s given for the respawned group.
|
||||
-- - @{#GROUP.InitRandomizePositionZone}: Randomize the positions of the units of the respawned group within the @{Core.Zone}.
|
||||
-- - @{#GROUP.InitRandomizePositionRadius}: Randomize the positions of the units of the respawned group in a circle band.
|
||||
-- - @{#GROUP.InitRandomizeTemplates}: Randomize the Template for the respawned group.
|
||||
--
|
||||
--
|
||||
-- Notes:
|
||||
--
|
||||
-- - When InitZone or InitRandomizeZones is not used, the position of the respawned group will be its current position.
|
||||
-- - The current alive group will always be destroyed and respawned using the template definition.
|
||||
--
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
@@ -1988,10 +2015,24 @@ function GROUP:Respawn( Template, Reset )
|
||||
end
|
||||
return h
|
||||
end
|
||||
|
||||
local function TransFormRoute(Template,OldPos,NewPos)
|
||||
if Template.route and Template.route.points then
|
||||
for _,_point in ipairs(Template.route.points) do
|
||||
--self:I(string.format("Point x = %f Point y = %f",_point.x,_point.y))
|
||||
_point.x = _point.x - OldPos.x + NewPos.x
|
||||
_point.y = _point.y - OldPos.y + NewPos.y
|
||||
--self:I(string.format("Point x = %f Point y = %f",_point.x,_point.y))
|
||||
end
|
||||
end
|
||||
return Template
|
||||
end
|
||||
|
||||
-- First check if group is alive.
|
||||
if self:IsAlive() then
|
||||
|
||||
|
||||
local OldPos = self:GetVec2()
|
||||
|
||||
-- Respawn zone.
|
||||
local Zone = self.InitRespawnZone -- Core.Zone#ZONE
|
||||
|
||||
@@ -2004,12 +2045,14 @@ function GROUP:Respawn( Template, Reset )
|
||||
-- X, Y
|
||||
Template.x = Vec3.x
|
||||
Template.y = Vec3.z
|
||||
|
||||
local NewPos = { x = Vec3.x, y = Vec3.z }
|
||||
|
||||
--Template.x = nil
|
||||
--Template.y = nil
|
||||
|
||||
-- Debug number of units.
|
||||
self:F( #Template.units )
|
||||
--self:F( #Template.units )
|
||||
|
||||
-- Reset position etc?
|
||||
if Reset == true then
|
||||
@@ -2017,10 +2060,10 @@ function GROUP:Respawn( Template, Reset )
|
||||
-- Loop over units in group.
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local GroupUnit = UnitData -- Wrapper.Unit#UNIT
|
||||
self:F(GroupUnit:GetName())
|
||||
--self:F(GroupUnit:GetName())
|
||||
|
||||
if GroupUnit:IsAlive() then
|
||||
self:I("FF Alive")
|
||||
--self:I("FF Alive")
|
||||
|
||||
-- Get unit position vector.
|
||||
local GroupUnitVec3 = GroupUnit:GetVec3()
|
||||
@@ -2058,18 +2101,20 @@ function GROUP:Respawn( Template, Reset )
|
||||
-- Set heading.
|
||||
Template.units[UnitID].heading = _Heading(self.InitRespawnHeading and self.InitRespawnHeading or GroupUnit:GetHeading())
|
||||
Template.units[UnitID].psi = -Template.units[UnitID].heading
|
||||
|
||||
|
||||
-- Debug.
|
||||
self:F( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
|
||||
--self:F( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
|
||||
end
|
||||
end
|
||||
|
||||
Template = TransFormRoute(Template,OldPos,NewPos)
|
||||
|
||||
elseif Reset==false then -- Reset=false or nil
|
||||
|
||||
-- Loop over template units.
|
||||
for UnitID, TemplateUnitData in pairs( Template.units ) do
|
||||
|
||||
self:F( "Reset" )
|
||||
--self:F( "Reset" )
|
||||
|
||||
-- Position from template.
|
||||
local GroupUnitVec3 = { x = TemplateUnitData.x, y = TemplateUnitData.alt, z = TemplateUnitData.y }
|
||||
@@ -2101,11 +2146,13 @@ function GROUP:Respawn( Template, Reset )
|
||||
|
||||
-- Heading
|
||||
Template.units[UnitID].heading = self.InitRespawnHeading and self.InitRespawnHeading or TemplateUnitData.heading
|
||||
|
||||
|
||||
-- Debug.
|
||||
self:F( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
|
||||
--self:F( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
|
||||
end
|
||||
|
||||
|
||||
Template = TransFormRoute(Template,OldPos,NewPos)
|
||||
|
||||
else
|
||||
|
||||
local units=self:GetUnits()
|
||||
@@ -2154,10 +2201,11 @@ function GROUP:Respawn( Template, Reset )
|
||||
-- Destroy old group. Dont trigger any dead/crash events since this is a respawn.
|
||||
self:Destroy(false)
|
||||
|
||||
self:T({Template=Template})
|
||||
--UTILS.PrintTableToLog(Template)
|
||||
|
||||
-- Spawn new group.
|
||||
_DATABASE:Spawn(Template)
|
||||
self:ScheduleOnce(0.1,_DATABASE.Spawn,_DATABASE,Template)
|
||||
--_DATABASE:Spawn(Template)
|
||||
|
||||
-- Reset events.
|
||||
self:ResetEvents()
|
||||
@@ -2165,6 +2213,29 @@ function GROUP:Respawn( Template, Reset )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Respawn the @{Wrapper.Group} at a @{Core.Point#COORDINATE}.
|
||||
-- The method will setup the new group template according the Init(Respawn) settings provided for the group.
|
||||
-- These settings can be provided by calling the relevant Init...() methods of the Group prior.
|
||||
--
|
||||
-- - @{#GROUP.InitHeading}: Set the heading for the units in degrees within the respawned group.
|
||||
-- - @{#GROUP.InitHeight}: Set the height for the units in meters for the respawned group. (This is applicable for air units).
|
||||
-- - @{#GROUP.InitRandomizeHeading}: Randomize the headings for the units within the respawned group.
|
||||
-- - @{#GROUP.InitRandomizePositionZone}: Randomize the positions of the units of the respawned group within the @{Core.Zone}.
|
||||
-- - @{#GROUP.InitRandomizePositionRadius}: Randomize the positions of the units of the respawned group in a circle band.
|
||||
--
|
||||
-- Notes:
|
||||
--
|
||||
-- - When no coordinate is given, the position of the respawned group will be its current position.
|
||||
-- - The current alive group will always be destroyed first.
|
||||
-- - The new group will have all of its original units and health restored.
|
||||
--
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
-- @param Core.Point#COORDINATE Coordinate Where to respawn the group. Can be handed as a @{Core.Zone#ZONE_BASE} object.
|
||||
-- @return Wrapper.Group#GROUP self
|
||||
function GROUP:Teleport(Coordinate)
|
||||
self:InitZone(Coordinate)
|
||||
return self:Respawn(nil,false)
|
||||
end
|
||||
|
||||
--- Respawn a group at an airbase.
|
||||
-- Note that the group has to be on parking spots at the airbase already in order for this to work.
|
||||
@@ -2175,7 +2246,7 @@ end
|
||||
-- @param #boolean Uncontrolled (Optional) If true, spawn in uncontrolled state.
|
||||
-- @return Wrapper.Group#GROUP Group spawned at airbase or nil if group could not be spawned.
|
||||
function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- R2.4
|
||||
self:F2( { SpawnTemplate, Takeoff, Uncontrolled} )
|
||||
--self:F2( { SpawnTemplate, Takeoff, Uncontrolled} )
|
||||
|
||||
if self and self:IsAlive() then
|
||||
|
||||
@@ -2183,7 +2254,7 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) --
|
||||
local airbase=self:GetCoordinate():GetClosestAirbase()
|
||||
|
||||
if airbase then
|
||||
self:F2("Closest airbase = "..airbase:GetName())
|
||||
--self:F2("Closest airbase = "..airbase:GetName())
|
||||
else
|
||||
self:E("ERROR: could not find closest airbase!")
|
||||
return nil
|
||||
@@ -2234,7 +2305,7 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) --
|
||||
local Parkingspot, TermialID, Distance=unit:GetCoordinate():GetClosestParkingSpot(airbase)
|
||||
|
||||
--Parkingspot:MarkToAll("parking spot")
|
||||
self:T2(string.format("Closest parking spot distance = %s, terminal ID=%s", tostring(Distance), tostring(TermialID)))
|
||||
--self:T2(string.format("Closest parking spot distance = %s, terminal ID=%s", tostring(Distance), tostring(TermialID)))
|
||||
|
||||
-- Get unit coordinates for respawning position.
|
||||
local uc=unit:GetCoordinate()
|
||||
@@ -2296,7 +2367,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #table The MissionTemplate
|
||||
function GROUP:GetTaskMission()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
return UTILS.DeepCopy( _DATABASE.Templates.Groups[self.GroupName].Template )
|
||||
end
|
||||
@@ -2305,9 +2376,12 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #table The mission route defined by points.
|
||||
function GROUP:GetTaskRoute()
|
||||
self:F2( self.GroupName )
|
||||
|
||||
return UTILS.DeepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points )
|
||||
--self:F2( self.GroupName )
|
||||
if _DATABASE.Templates.Groups[self.GroupName].Template and _DATABASE.Templates.Groups[self.GroupName].Template.route and _DATABASE.Templates.Groups[self.GroupName].Template.route.points then
|
||||
return UTILS.DeepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points )
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
--- Return the route of a group by using the global _DATABASE object (an instance of @{Core.Database#DATABASE}).
|
||||
@@ -2317,7 +2391,7 @@ end
|
||||
-- @param #boolean Randomize Randomization of the route, when true.
|
||||
-- @param #number Radius When randomization is on, the randomization is within the radius.
|
||||
function GROUP:CopyRoute( Begin, End, Randomize, Radius )
|
||||
self:F2( { Begin, End } )
|
||||
--self:F2( { Begin, End } )
|
||||
|
||||
local Points = {}
|
||||
|
||||
@@ -2329,7 +2403,7 @@ function GROUP:CopyRoute( Begin, End, Randomize, Radius )
|
||||
GroupName = self:GetName()
|
||||
end
|
||||
|
||||
self:T3( { GroupName } )
|
||||
--self:T3( { GroupName } )
|
||||
|
||||
local Template = _DATABASE.Templates.Groups[GroupName].Template
|
||||
|
||||
@@ -2375,7 +2449,7 @@ function GROUP:CalculateThreatLevelA2G()
|
||||
end
|
||||
end
|
||||
|
||||
self:T3( MaxThreatLevelA2G )
|
||||
--self:T3( MaxThreatLevelA2G )
|
||||
return MaxThreatLevelA2G
|
||||
end
|
||||
|
||||
@@ -2402,7 +2476,7 @@ end
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
-- @return #boolean true if in the first unit of the group is in the air or #nil if the GROUP is not existing or not alive.
|
||||
function GROUP:InAir()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
@@ -2410,7 +2484,7 @@ function GROUP:InAir()
|
||||
local DCSUnit = DCSGroup:getUnit(1)
|
||||
if DCSUnit then
|
||||
local GroupInAir = DCSGroup:getUnit(1):inAir()
|
||||
self:T3( GroupInAir )
|
||||
--self:T3( GroupInAir )
|
||||
return GroupInAir
|
||||
end
|
||||
end
|
||||
@@ -2423,7 +2497,7 @@ end
|
||||
-- @param #boolean AllUnits (Optional) If true, check whether all units of the group are airborne.
|
||||
-- @return #boolean True if at least one (optionally all) unit(s) is(are) airborne or false otherwise. Nil if no unit exists or is alive.
|
||||
function GROUP:IsAirborne(AllUnits)
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
|
||||
-- Get all units of the group.
|
||||
local units=self:GetUnits()
|
||||
@@ -2634,7 +2708,7 @@ do -- Route methods
|
||||
-- @param #number Speed (optional) The Speed, if no Speed is given, 80% of maximum Speed of the group is selected.
|
||||
-- @return #GROUP self
|
||||
function GROUP:RouteRTB( RTBAirbase, Speed )
|
||||
self:F( { RTBAirbase:GetName(), Speed } )
|
||||
--self:F( { RTBAirbase:GetName(), Speed } )
|
||||
|
||||
local DCSGroup = self:GetDCSObject()
|
||||
|
||||
@@ -2660,7 +2734,7 @@ do -- Route methods
|
||||
--local Points={PointFrom, PointAirbase, PointLanding}
|
||||
|
||||
-- Debug info.
|
||||
self:T3(Points)
|
||||
--self:T3(Points)
|
||||
|
||||
-- Get group template.
|
||||
local Template=self:GetTemplate()
|
||||
@@ -2756,7 +2830,7 @@ do -- Players
|
||||
end
|
||||
|
||||
if HasPlayers == true then
|
||||
self:F2( PlayerNames )
|
||||
--self:F2( PlayerNames )
|
||||
return PlayerNames
|
||||
end
|
||||
|
||||
@@ -2790,7 +2864,7 @@ end
|
||||
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||
-- @return #GROUP self
|
||||
function GROUP:EnableEmission(switch)
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
local switch = switch or false
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
@@ -2817,7 +2891,7 @@ end
|
||||
-- @param #boolean switch If true, Invisible is enabled. If false, Invisible is disabled.
|
||||
-- @return #GROUP self
|
||||
function GROUP:CommandSetInvisible(switch)
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
if switch==nil then
|
||||
switch=false
|
||||
end
|
||||
@@ -2839,7 +2913,7 @@ end
|
||||
-- @param #boolean switch If true, Immortal is enabled. If false, Immortal is disabled.
|
||||
-- @return #GROUP self
|
||||
function GROUP:CommandSetImmortal(switch)
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
if switch==nil then
|
||||
switch=false
|
||||
end
|
||||
@@ -2852,7 +2926,7 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @return #string Skill String of skill name.
|
||||
function GROUP:GetSkill()
|
||||
self:F2( self.GroupName )
|
||||
--self:F2( self.GroupName )
|
||||
local unit = self:GetUnit(1)
|
||||
local name = unit:GetName()
|
||||
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||
@@ -2899,8 +2973,10 @@ end
|
||||
-- @param #GROUP self
|
||||
-- @param #boolean ShortCallsign Return a shortened customized callsign, i.e. "Ghostrider 9" and not "Ghostrider 9 1"
|
||||
-- @param #boolean Keepnumber (Player only) Return customized callsign, incl optional numbers at the end, e.g. "Aerial 1-1#Ghostrider 109" results in "Ghostrider 109", if you want to e.g. use historical US Navy Callsigns
|
||||
-- @param #table CallsignTranslations Table to translate between DCS standard callsigns and bespoke ones. Overrides personal/parsed callsigns if set
|
||||
-- @param #table CallsignTranslations (Optional) Table to translate between DCS standard callsigns and bespoke ones. Overrides personal/parsed callsigns if set
|
||||
-- callsigns from playername or group name.
|
||||
-- @param #func CustomFunction (Optional) For player names only(!). If given, this function will return the callsign. Needs to take the groupname and the playername as first arguments.
|
||||
-- @param #arg ... (Optional) Comma separated arguments to add to the CustomFunction call after groupname and playername.
|
||||
-- @return #string Callsign
|
||||
-- @usage
|
||||
-- -- suppose there are three groups with one (client) unit each:
|
||||
@@ -2921,8 +2997,12 @@ end
|
||||
-- -- Apollo for Slot 2 or Apollo 403 if Keepnumber is set
|
||||
-- -- Apollo for Slot 3
|
||||
-- -- Bengal-4 for Slot 4
|
||||
|
||||
function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
--
|
||||
-- -- Using a custom function (for player units **only**):
|
||||
-- -- Imagine your playernames are looking like so: "[Squadname] | Cpt Apple" and you only want to have the last word as callsign, i.e. "Apple" here. Then this custom function will return this:
|
||||
-- local callsign = mygroup:GetCustomCallSign(true,false,nil,function(groupname,playername) return string.match(playername,"([%a]+)$") end)
|
||||
--
|
||||
function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations,CustomFunction,...)
|
||||
--self:I("GetCustomCallSign")
|
||||
|
||||
local callsign = "Ghost 1"
|
||||
@@ -2936,7 +3016,14 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9
|
||||
local callnumberminor = string.char(string.byte(callnumber,2)) -- 1
|
||||
local personalized = false
|
||||
|
||||
local playername = IsPlayer == true and self:GetPlayerName() or shortcallsign
|
||||
|
||||
if CustomFunction and IsPlayer then
|
||||
local arguments = arg or {}
|
||||
local callsign = CustomFunction(groupname,playername,unpack(arguments))
|
||||
return callsign
|
||||
end
|
||||
|
||||
-- prioritize bespoke callsigns over parsing, prefer parsing over default callsigns
|
||||
if CallsignTranslations and CallsignTranslations[callsignroot] then
|
||||
callsignroot = CallsignTranslations[callsignroot]
|
||||
@@ -2948,9 +3035,9 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||
shortcallsign = string.match(groupname,"#%s*([%a]+)") or "Ghost" -- Ghostrider
|
||||
end
|
||||
personalized = true
|
||||
elseif IsPlayer and string.find(self:GetPlayerName(),"|") then
|
||||
elseif IsPlayer and string.find(playername,"|") then
|
||||
-- personalized flight name in group naming
|
||||
shortcallsign = string.match(self:GetPlayerName(),"|%s*([%a]+)") or string.match(self:GetPlayerName(),"|%s*([%d]+)") or "Ghost" -- Ghostrider
|
||||
shortcallsign = string.match(playername,"|%s*([%a]+)") or string.match(self:GetPlayerName(),"|%s*([%d]+)") or "Ghost" -- Ghostrider
|
||||
personalized = true
|
||||
end
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ do
|
||||
-- @field #NET
|
||||
NET = {
|
||||
ClassName = "NET",
|
||||
Version = "0.1.3",
|
||||
Version = "0.1.4",
|
||||
BlockTime = 600,
|
||||
BlockedPilots = {},
|
||||
BlockedUCIDs = {},
|
||||
@@ -67,6 +67,9 @@ function NET:New()
|
||||
self.KnownPilots = {}
|
||||
self:SetBlockMessage()
|
||||
self:SetUnblockMessage()
|
||||
self.BlockedSides = {}
|
||||
self.BlockedSides[1] = false
|
||||
self.BlockedSides[2] = false
|
||||
|
||||
-- Start State.
|
||||
self:SetStartState("Stopped")
|
||||
@@ -160,11 +163,12 @@ end
|
||||
-- @param #string PlayerSlot
|
||||
-- @return #boolean IsBlocked
|
||||
function NET:IsAnyBlocked(UCID,Name,PlayerID,PlayerSide,PlayerSlot)
|
||||
self:T({UCID,Name,PlayerID,PlayerSide,PlayerSlot})
|
||||
local blocked = false
|
||||
local TNow = timer.getTime()
|
||||
-- UCID
|
||||
if UCID and self.BlockedUCIDs[UCID] and TNow < self.BlockedUCIDs[UCID] then
|
||||
return true
|
||||
blocked = true
|
||||
end
|
||||
-- ID/Name
|
||||
if PlayerID and not Name then
|
||||
@@ -172,16 +176,18 @@ function NET:IsAnyBlocked(UCID,Name,PlayerID,PlayerSide,PlayerSlot)
|
||||
end
|
||||
-- Name
|
||||
if Name and self.BlockedPilots[Name] and TNow < self.BlockedPilots[Name] then
|
||||
return true
|
||||
blocked = true
|
||||
end
|
||||
-- Side
|
||||
if PlayerSide and self.BlockedSides[PlayerSide] and TNow < self.BlockedSides[PlayerSide] then
|
||||
return true
|
||||
self:T({time = self.BlockedSides[PlayerSide]})
|
||||
if PlayerSide and type(self.BlockedSides[PlayerSide]) == "number" and TNow < self.BlockedSides[PlayerSide] then
|
||||
blocked = true
|
||||
end
|
||||
-- Slot
|
||||
if PlayerSlot and self.BlockedSlots[PlayerSlot] and TNow < self.BlockedSlots[PlayerSlot] then
|
||||
return true
|
||||
blocked = true
|
||||
end
|
||||
self:T("IsAnyBlocked: "..tostring(blocked))
|
||||
return blocked
|
||||
end
|
||||
|
||||
@@ -200,19 +206,27 @@ function NET:_EventHandler(EventData)
|
||||
local ucid = self:GetPlayerUCID(nil,name) or "none"
|
||||
local PlayerID = self:GetPlayerIDByName(name) or "none"
|
||||
local PlayerSide, PlayerSlot = self:GetSlot(data.IniUnit)
|
||||
if not PlayerSide then PlayerSide = EventData.IniCoalition end
|
||||
if not PlayerSlot then PlayerSlot = EventData.IniUnit:GetID() end
|
||||
local TNow = timer.getTime()
|
||||
|
||||
self:T(self.lid.."Event for: "..name.." | UCID: "..ucid)
|
||||
self:T(self.lid.."Event for: "..name.." | UCID: "..ucid .. " | ID/SIDE/SLOT "..PlayerID.."/"..PlayerSide.."/"..PlayerSlot)
|
||||
|
||||
-- Joining
|
||||
if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then
|
||||
self:T(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid.." | Event ID: "..data.id)
|
||||
-- Check for blockages
|
||||
local blocked = self:IsAnyBlocked(ucid,name,PlayerID,PlayerSide,PlayerSlot)
|
||||
|
||||
if blocked and PlayerID and tonumber(PlayerID) ~= 1 then
|
||||
if blocked and PlayerID then -- and tonumber(PlayerID) ~= 1 then
|
||||
self:T("Player blocked")
|
||||
-- block pilot
|
||||
local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' )
|
||||
local outcome = net.force_player_slot(tonumber(PlayerID), PlayerSide, data.IniUnit:GetID() )
|
||||
self:T({Blocked_worked=outcome})
|
||||
if outcome == false then
|
||||
local unit = data.IniUnit
|
||||
local sched = TIMER:New(unit.Destroy,unit,3):Start(3)
|
||||
self:__PlayerBlocked(5,unit,name,1)
|
||||
end
|
||||
else
|
||||
local client = CLIENT:FindByPlayerName(name) or data.IniUnit
|
||||
if not self.KnownPilots[name] or (self.KnownPilots[name] and TNow-self.KnownPilots[name].timestamp > 3) then
|
||||
@@ -225,6 +239,7 @@ function NET:_EventHandler(EventData)
|
||||
slot = PlayerSlot,
|
||||
timestamp = TNow,
|
||||
}
|
||||
--UTILS.PrintTableToLog(self.KnownPilots[name])
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -350,11 +365,10 @@ end
|
||||
|
||||
--- Block a specific coalition side, does NOT automatically kick all players of that side or kick out joined players
|
||||
-- @param #NET self
|
||||
-- @param #number side The side to block - 1 : Red, 2 : Blue
|
||||
-- @param #number Side The side to block - 1 : Red, 2 : Blue
|
||||
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||
-- @return #NET self
|
||||
function NET:BlockSide(Side,Seconds)
|
||||
self:T({Side,Seconds})
|
||||
local addon = Seconds or self.BlockTime
|
||||
if Side == 1 or Side == 2 then
|
||||
self.BlockedSides[Side] = timer.getTime()+addon
|
||||
@@ -367,10 +381,9 @@ end
|
||||
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||
-- @return #NET self
|
||||
function NET:UnblockSide(Side,Seconds)
|
||||
self:T({Side,Seconds})
|
||||
local addon = Seconds or self.BlockTime
|
||||
if Side == 1 or Side == 2 then
|
||||
self.BlockedSides[Side] = nil
|
||||
self.BlockedSides[Side] = false
|
||||
end
|
||||
return self
|
||||
end
|
||||
@@ -485,8 +498,11 @@ end
|
||||
-- @param Wrapper.Client#CLIENT Client The client
|
||||
-- @return #number PlayerID or nil
|
||||
function NET:GetPlayerIDFromClient(Client)
|
||||
self:T("GetPlayerIDFromClient")
|
||||
self:T({Client=Client})
|
||||
if Client then
|
||||
local name = Client:GetPlayerName()
|
||||
self:T({name=name})
|
||||
local id = self:GetPlayerIDByName(name)
|
||||
return id
|
||||
else
|
||||
@@ -528,6 +544,7 @@ function NET:SendChatToPlayer(Message, ToPlayer, FromPlayer)
|
||||
return self
|
||||
end
|
||||
|
||||
--[[ not in 2.97 MSE any longer
|
||||
--- Load a specific mission.
|
||||
-- @param #NET self
|
||||
-- @param #string Path and Mission
|
||||
@@ -550,6 +567,7 @@ function NET:LoadNextMission()
|
||||
outcome = net.load_next_mission()
|
||||
return outcome
|
||||
end
|
||||
--]]
|
||||
|
||||
--- Return a table of players currently connected to the server.
|
||||
-- @param #NET self
|
||||
@@ -680,16 +698,19 @@ end
|
||||
-- @return #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue
|
||||
-- @return #number SlotID
|
||||
function NET:GetSlot(Client)
|
||||
self:T("NET.GetSlot")
|
||||
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||
self:T("NET.GetSlot PlayerID = "..tostring(PlayerID))
|
||||
if PlayerID then
|
||||
local side,slot = net.get_slot(tonumber(PlayerID))
|
||||
self:T("NET.GetSlot side, slot = "..tostring(side)..","..tostring(slot))
|
||||
return side,slot
|
||||
else
|
||||
return nil,nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Force the slot for a specific client.
|
||||
--- Force the slot for a specific client. If this returns false, it didn't work via `net` (which is ALWAYS the case as of Nov 2024)!
|
||||
-- @param #NET self
|
||||
-- @param Wrapper.Client#CLIENT Client The client
|
||||
-- @param #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue
|
||||
@@ -697,19 +718,22 @@ end
|
||||
-- @return #boolean Success
|
||||
function NET:ForceSlot(Client,SideID,SlotID)
|
||||
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||
if PlayerID and tonumber(PlayerID) ~= 1 then
|
||||
return net.force_player_slot(tonumber(PlayerID), SideID, SlotID or '' )
|
||||
local SlotID = SlotID or Client:GetID()
|
||||
if PlayerID then -- and tonumber(PlayerID) ~= 1 then
|
||||
return net.force_player_slot(tonumber(PlayerID), SideID, SlotID )
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- Force a client back to spectators.
|
||||
--- Force a client back to spectators. If this returns false, it didn't work via `net` (which is ALWAYS the case as of Nov 2024)!
|
||||
-- @param #NET self
|
||||
-- @param Wrapper.Client#CLIENT Client The client
|
||||
-- @return #boolean Succes
|
||||
function NET:ReturnToSpectators(Client)
|
||||
local outcome = self:ForceSlot(Client,0)
|
||||
-- workaround
|
||||
local sched = TIMER:New(Client.Destroy,Client,1):Start(1)
|
||||
return outcome
|
||||
end
|
||||
|
||||
@@ -779,7 +803,7 @@ function NET:onafterStatus(From,Event,To)
|
||||
local function HouseHold(tavolo)
|
||||
local TNow = timer.getTime()
|
||||
for _,entry in pairs (tavolo) do
|
||||
if entry >= TNow then entry = nil end
|
||||
if type(entry) == "number" and entry >= TNow then entry = false end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -671,7 +671,7 @@ function POSITIONABLE:GetBoundingRadius( MinDist )
|
||||
return math.max( math.max( CX, CZ ), boxmin )
|
||||
end
|
||||
|
||||
BASE:E( { "Cannot GetBoundingRadius", Positionable = self, Alive = self:IsAlive() } )
|
||||
BASE:T( { "Cannot GetBoundingRadius", Positionable = self, Alive = self:IsAlive() } )
|
||||
|
||||
return nil
|
||||
end
|
||||
@@ -1853,6 +1853,7 @@ do -- Cargo
|
||||
["HL_KORD"] = 6*POSITIONABLE.DefaultInfantryWeight,
|
||||
["HL_DSHK"] = 6*POSITIONABLE.DefaultInfantryWeight,
|
||||
["CCKW_353"] = 16*POSITIONABLE.DefaultInfantryWeight, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers,
|
||||
["MaxxPro_MRAP"] = 7*POSITIONABLE.DefaultInfantryWeight,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,8 @@ function STATIC:Register( StaticName )
|
||||
if DCSStatic then
|
||||
local Life0 = DCSStatic:getLife() or 1
|
||||
self.Life0 = Life0
|
||||
else
|
||||
self:E(string.format("Static object %s does not exist!", tostring(self.StaticName)))
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
@@ -226,6 +226,26 @@ function STORAGE:NewFromStaticCargo(StaticCargoName)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new STORAGE object from an DCS static cargo object.
|
||||
-- @param #STORAGE self
|
||||
-- @param #string DynamicCargoName Unit name of the dynamic cargo.
|
||||
-- @return #STORAGE self
|
||||
function STORAGE:NewFromDynamicCargo(DynamicCargoName)
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
local self=BASE:Inherit(self, BASE:New()) -- #STORAGE
|
||||
|
||||
self.airbase=Unit.getByName(DynamicCargoName)
|
||||
|
||||
if Airbase.getWarehouse then
|
||||
self.warehouse=Warehouse.getCargoAsWarehouse(self.airbase)
|
||||
end
|
||||
|
||||
self.lid = string.format("STORAGE %s", DynamicCargoName)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Airbases only - Find a STORAGE in the **_DATABASE** using the name associated airbase.
|
||||
-- @param #STORAGE self
|
||||
|
||||
@@ -219,6 +219,7 @@ function UNIT:Name()
|
||||
return self.UnitName
|
||||
end
|
||||
|
||||
--[[
|
||||
--- Get the DCS unit object.
|
||||
-- @param #UNIT self
|
||||
-- @return DCS#Unit The DCS unit object.
|
||||
@@ -230,10 +231,36 @@ function UNIT:GetDCSObject()
|
||||
return DCSUnit
|
||||
end
|
||||
|
||||
--if self.DCSUnit then
|
||||
--return self.DCSUnit
|
||||
--end
|
||||
return nil
|
||||
end
|
||||
--]]
|
||||
|
||||
--- Returns the DCS Unit.
|
||||
-- @param #UNIT self
|
||||
-- @return DCS#Unit The DCS Group.
|
||||
function UNIT:GetDCSObject()
|
||||
|
||||
-- FF: Added checks that DCSObject exists because otherwise there were problems when respawning the unit right after it was initially spawned (e.g. teleport in OPSGROUP).
|
||||
-- Got "Unit does not exit" after coalition.addGroup() when trying to access unit data because LastCallDCSObject<=1.
|
||||
if (not self.LastCallDCSObject) or (self.LastCallDCSObject and timer.getTime()-self.LastCallDCSObject>1) or (self.DCSObject==nil) or (self.DCSObject:isExist()==false) then
|
||||
|
||||
-- Get DCS group.
|
||||
local DCSUnit = Unit.getByName( self.UnitName )
|
||||
|
||||
if DCSUnit then
|
||||
self.LastCallDCSObject = timer.getTime()
|
||||
self.DCSObject = DCSUnit
|
||||
return DCSUnit
|
||||
else
|
||||
self.DCSObject = nil
|
||||
self.LastCallDCSObject = nil
|
||||
end
|
||||
|
||||
else
|
||||
return self.DCSObject
|
||||
end
|
||||
|
||||
--self:E(string.format("ERROR: Could not get DCS group object of group %s because DCS object could not be found!", tostring(self.UnitName)))
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -243,7 +270,7 @@ end
|
||||
-- @return #number The height of the group or nil if is not existing or alive.
|
||||
function UNIT:GetAltitude(FromGround)
|
||||
|
||||
local DCSUnit = Unit.getByName( self.UnitName )
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
if DCSUnit then
|
||||
local altitude = 0
|
||||
@@ -273,12 +300,12 @@ end
|
||||
-- @param #number Heading The heading of the unit respawn.
|
||||
function UNIT:ReSpawnAt( Coordinate, Heading )
|
||||
|
||||
self:T( self:Name() )
|
||||
--self:T( self:Name() )
|
||||
local SpawnGroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplateFromUnitName( self:Name() ) )
|
||||
self:T( SpawnGroupTemplate )
|
||||
--self:T( SpawnGroupTemplate )
|
||||
|
||||
local SpawnGroup = self:GetGroup()
|
||||
self:T( { SpawnGroup = SpawnGroup } )
|
||||
--self:T( { SpawnGroup = SpawnGroup } )
|
||||
|
||||
if SpawnGroup then
|
||||
|
||||
@@ -286,10 +313,10 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
|
||||
SpawnGroupTemplate.x = Coordinate.x
|
||||
SpawnGroupTemplate.y = Coordinate.z
|
||||
|
||||
self:F( #SpawnGroupTemplate.units )
|
||||
--self:F( #SpawnGroupTemplate.units )
|
||||
for UnitID, UnitData in pairs( SpawnGroup:GetUnits() or {} ) do
|
||||
local GroupUnit = UnitData -- #UNIT
|
||||
self:F( GroupUnit:GetName() )
|
||||
--self:F( GroupUnit:GetName() )
|
||||
if GroupUnit:IsAlive() then
|
||||
local GroupUnitVec3 = GroupUnit:GetVec3()
|
||||
local GroupUnitHeading = GroupUnit:GetHeading()
|
||||
@@ -297,23 +324,23 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
|
||||
SpawnGroupTemplate.units[UnitID].x = GroupUnitVec3.x
|
||||
SpawnGroupTemplate.units[UnitID].y = GroupUnitVec3.z
|
||||
SpawnGroupTemplate.units[UnitID].heading = GroupUnitHeading
|
||||
self:F( { UnitID, SpawnGroupTemplate.units[UnitID], SpawnGroupTemplate.units[UnitID] } )
|
||||
--self:F( { UnitID, SpawnGroupTemplate.units[UnitID], SpawnGroupTemplate.units[UnitID] } )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for UnitTemplateID, UnitTemplateData in pairs( SpawnGroupTemplate.units ) do
|
||||
self:T( { UnitTemplateData.name, self:Name() } )
|
||||
--self:T( { UnitTemplateData.name, self:Name() } )
|
||||
SpawnGroupTemplate.units[UnitTemplateID].unitId = nil
|
||||
if UnitTemplateData.name == self:Name() then
|
||||
self:T("Adjusting")
|
||||
--self:T("Adjusting")
|
||||
SpawnGroupTemplate.units[UnitTemplateID].alt = Coordinate.y
|
||||
SpawnGroupTemplate.units[UnitTemplateID].x = Coordinate.x
|
||||
SpawnGroupTemplate.units[UnitTemplateID].y = Coordinate.z
|
||||
SpawnGroupTemplate.units[UnitTemplateID].heading = Heading
|
||||
self:F( { UnitTemplateID, SpawnGroupTemplate.units[UnitTemplateID], SpawnGroupTemplate.units[UnitTemplateID] } )
|
||||
--self:F( { UnitTemplateID, SpawnGroupTemplate.units[UnitTemplateID], SpawnGroupTemplate.units[UnitTemplateID] } )
|
||||
else
|
||||
self:F( SpawnGroupTemplate.units[UnitTemplateID].name )
|
||||
--self:F( SpawnGroupTemplate.units[UnitTemplateID].name )
|
||||
local GroupUnit = UNIT:FindByName( SpawnGroupTemplate.units[UnitTemplateID].name ) -- #UNIT
|
||||
if GroupUnit and GroupUnit:IsAlive() then
|
||||
local GroupUnitVec3 = GroupUnit:GetVec3()
|
||||
@@ -324,7 +351,7 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
|
||||
UnitTemplateData.heading = GroupUnitHeading
|
||||
else
|
||||
if SpawnGroupTemplate.units[UnitTemplateID].name ~= self:Name() then
|
||||
self:T("nilling")
|
||||
--self:T("nilling")
|
||||
SpawnGroupTemplate.units[UnitTemplateID].delete = true
|
||||
end
|
||||
end
|
||||
@@ -336,7 +363,7 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
|
||||
while i <= #SpawnGroupTemplate.units do
|
||||
|
||||
local UnitTemplateData = SpawnGroupTemplate.units[i]
|
||||
self:T( UnitTemplateData.name )
|
||||
--self:T( UnitTemplateData.name )
|
||||
|
||||
if UnitTemplateData.delete then
|
||||
table.remove( SpawnGroupTemplate.units, i )
|
||||
@@ -347,7 +374,7 @@ function UNIT:ReSpawnAt( Coordinate, Heading )
|
||||
|
||||
SpawnGroupTemplate.groupId = nil
|
||||
|
||||
self:T( SpawnGroupTemplate )
|
||||
--self:T( SpawnGroupTemplate )
|
||||
|
||||
_DATABASE:Spawn( SpawnGroupTemplate )
|
||||
end
|
||||
@@ -358,7 +385,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean `true` if Unit is activated. `nil` The DCS Unit is not existing or alive.
|
||||
function UNIT:IsActive()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -395,7 +422,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean Returns `true` if Unit is alive and active, `false` if it exists but is not active and `nil` if the object does not exist or DCS `isExist` function returns false.
|
||||
function UNIT:IsAlive()
|
||||
self:F3( self.UnitName )
|
||||
--self:F3( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject() -- DCS#Unit
|
||||
|
||||
@@ -418,7 +445,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #string The Callsign of the Unit.
|
||||
function UNIT:GetCallsign()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -430,7 +457,7 @@ function UNIT:GetCallsign()
|
||||
return UnitCallSign
|
||||
end
|
||||
|
||||
self:F( self.ClassName .. " " .. self.UnitName .. " not found!" )
|
||||
--self:F( self.ClassName .. " " .. self.UnitName .. " not found!" )
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -477,7 +504,7 @@ end
|
||||
-- @return #string Player Name
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetPlayerName()
|
||||
self:F( self.UnitName )
|
||||
--self:F( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject() -- DCS#Unit
|
||||
|
||||
@@ -551,7 +578,7 @@ end
|
||||
-- @return #number The Unit number.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetNumber()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -568,7 +595,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #number Speed in km/h.
|
||||
function UNIT:GetSpeedMax()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local Desc = self:GetDesc()
|
||||
|
||||
@@ -585,7 +612,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #number Range in meters.
|
||||
function UNIT:GetRange()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local Desc = self:GetDesc()
|
||||
|
||||
@@ -607,7 +634,7 @@ end
|
||||
-- @return #boolean If true, unit is refuelable (checks for the attribute "Refuelable").
|
||||
-- @return #number Refueling system (if any): 0=boom, 1=probe.
|
||||
function UNIT:IsRefuelable()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local refuelable=self:HasAttribute("Refuelable")
|
||||
|
||||
@@ -626,7 +653,7 @@ end
|
||||
-- @return #boolean If true, unit is a tanker (checks for the attribute "Tankers").
|
||||
-- @return #number Refueling system (if any): 0=boom, 1=probe.
|
||||
function UNIT:IsTanker()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local tanker=self:HasAttribute("Tankers")
|
||||
|
||||
@@ -725,7 +752,7 @@ end
|
||||
-- @param Wrapper.Unit#UNIT self
|
||||
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
||||
function UNIT:GetGroup()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
local UnitGroup = GROUP:FindByName(self.GroupName)
|
||||
if UnitGroup then
|
||||
return UnitGroup
|
||||
@@ -749,13 +776,13 @@ end
|
||||
-- @return #string The name of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetPrefix()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 )
|
||||
self:T3( UnitPrefix )
|
||||
--self:T3( UnitPrefix )
|
||||
return UnitPrefix
|
||||
end
|
||||
|
||||
@@ -766,7 +793,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return DCS#Unit.Ammo Table with ammuntion of the unit (or nil). This can be a complex table!
|
||||
function UNIT:GetAmmo()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
if DCSUnit then
|
||||
--local status, unitammo = pcall(
|
||||
@@ -958,7 +985,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return DCS#Unit.Sensors Table of sensors.
|
||||
function UNIT:GetSensors()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -977,7 +1004,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors.
|
||||
function UNIT:HasSensors( ... )
|
||||
self:F2( arg )
|
||||
--self:F2( arg )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -993,7 +1020,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean returns true if the unit is SEADable.
|
||||
function UNIT:HasSEAD()
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -1021,7 +1048,7 @@ end
|
||||
-- @return #boolean Indicates if at least one of the unit's radar(s) is on.
|
||||
-- @return DCS#Object The object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target.
|
||||
function UNIT:GetRadar()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -1037,7 +1064,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #number The relative amount of fuel (from 0.0 to 1.0) or *nil* if the DCS Unit is not existing or alive.
|
||||
function UNIT:GetFuel()
|
||||
self:F3( self.UnitName )
|
||||
--self:F3( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -1054,14 +1081,14 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #list<Wrapper.Unit#UNIT> A list of one @{Wrapper.Unit}.
|
||||
function UNIT:GetUnits()
|
||||
self:F3( { self.UnitName } )
|
||||
--self:F3( { self.UnitName } )
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
local Units = {}
|
||||
|
||||
if DCSUnit then
|
||||
Units[1] = UNIT:Find( DCSUnit )
|
||||
self:T3( Units )
|
||||
-self:T3( Units )
|
||||
return Units
|
||||
end
|
||||
|
||||
@@ -1073,7 +1100,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #number The Unit's health value or -1 if unit does not exist any more.
|
||||
function UNIT:GetLife()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -1089,7 +1116,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #number The Unit's initial health value or 0 if unit does not exist any more.
|
||||
function UNIT:GetLife0()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -1105,7 +1132,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #number The Unit's relative health value, i.e. a number in [0,1] or -1 if unit does not exist any more.
|
||||
function UNIT:GetLifeRelative()
|
||||
self:F2(self.UnitName)
|
||||
--self:F2(self.UnitName)
|
||||
|
||||
if self and self:IsAlive() then
|
||||
local life0=self:GetLife0()
|
||||
@@ -1120,7 +1147,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #number The Unit's relative health value, i.e. a number in [0,1] or 1 if unit does not exist any more.
|
||||
function UNIT:GetDamageRelative()
|
||||
self:F2(self.UnitName)
|
||||
--self:F2(self.UnitName)
|
||||
|
||||
if self and self:IsAlive() then
|
||||
return 1-self:GetLifeRelative()
|
||||
@@ -1158,7 +1185,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #number Unit category from `getDesc().category`.
|
||||
function UNIT:GetUnitCategory()
|
||||
self:F3( self.UnitName )
|
||||
--self:F3( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
if DCSUnit then
|
||||
@@ -1172,7 +1199,7 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #string Category name = Helicopter, Airplane, Ground Unit, Ship
|
||||
function UNIT:GetCategoryName()
|
||||
self:F3( self.UnitName )
|
||||
--self:F3( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
if DCSUnit then
|
||||
@@ -1184,7 +1211,7 @@ function UNIT:GetCategoryName()
|
||||
[Unit.Category.STRUCTURE] = "Structure",
|
||||
}
|
||||
local UnitCategory = DCSUnit:getDesc().category
|
||||
self:T3( UnitCategory )
|
||||
--self:T3( UnitCategory )
|
||||
|
||||
return CategoryNames[UnitCategory]
|
||||
end
|
||||
@@ -1412,7 +1439,7 @@ end
|
||||
-- @return true If the other DCS Unit is within the radius of the 2D point of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
|
||||
self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } )
|
||||
--self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@@ -1421,10 +1448,10 @@ function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
|
||||
local AwaitUnitVec3 = AwaitUnit:GetVec3()
|
||||
|
||||
if (((UnitVec3.x - AwaitUnitVec3.x)^2 + (UnitVec3.z - AwaitUnitVec3.z)^2)^0.5 <= Radius) then
|
||||
self:T3( "true" )
|
||||
--self:T3( "true" )
|
||||
return true
|
||||
else
|
||||
self:T3( "false" )
|
||||
--self:T3( "false" )
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -1442,17 +1469,17 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean IsFriendly evaluation result.
|
||||
function UNIT:IsFriendly( FriendlyCoalition )
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitCoalition = DCSUnit:getCoalition()
|
||||
self:T3( { UnitCoalition, FriendlyCoalition } )
|
||||
--self:T3( { UnitCoalition, FriendlyCoalition } )
|
||||
|
||||
local IsFriendlyResult = ( UnitCoalition == FriendlyCoalition )
|
||||
|
||||
self:F( IsFriendlyResult )
|
||||
--self:F( IsFriendlyResult )
|
||||
return IsFriendlyResult
|
||||
end
|
||||
|
||||
@@ -1464,17 +1491,17 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean Ship category evaluation result.
|
||||
function UNIT:IsShip()
|
||||
self:F2()
|
||||
--self:F2()
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitDescriptor = DCSUnit:getDesc()
|
||||
self:T3( { UnitDescriptor.category, Unit.Category.SHIP } )
|
||||
--self:T3( { UnitDescriptor.category, Unit.Category.SHIP } )
|
||||
|
||||
local IsShipResult = ( UnitDescriptor.category == Unit.Category.SHIP )
|
||||
|
||||
self:T3( IsShipResult )
|
||||
--self:T3( IsShipResult )
|
||||
return IsShipResult
|
||||
end
|
||||
|
||||
@@ -1486,7 +1513,7 @@ end
|
||||
-- @param #boolean NoHeloCheck If true, no additonal checks for helos are performed.
|
||||
-- @return #boolean Return true if in the air or #nil if the UNIT is not existing or alive.
|
||||
function UNIT:InAir(NoHeloCheck)
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
-- Get DCS unit object.
|
||||
local DCSUnit = self:GetDCSObject() --DCS#Unit
|
||||
@@ -1500,7 +1527,7 @@ function UNIT:InAir(NoHeloCheck)
|
||||
local UnitCategory = DCSUnit:getDesc().category
|
||||
|
||||
-- If DCS says that it is in air, check if this is really the case, since we might have landed on a building where inAir()=true but actually is not.
|
||||
-- This is a workaround since DCS currently does not acknoledge that helos land on buildings.
|
||||
-- This is a workaround since DCS currently does not acknowledge that helos land on buildings.
|
||||
-- Note however, that the velocity check will fail if the ground is moving, e.g. on an aircraft carrier!
|
||||
if UnitInAir==true and UnitCategory == Unit.Category.HELICOPTER and (not NoHeloCheck) then
|
||||
local VelocityVec3 = DCSUnit:getVelocity()
|
||||
@@ -1513,7 +1540,7 @@ function UNIT:InAir(NoHeloCheck)
|
||||
end
|
||||
end
|
||||
|
||||
self:T3( UnitInAir )
|
||||
--self:T3( UnitInAir )
|
||||
return UnitInAir
|
||||
end
|
||||
|
||||
@@ -1708,7 +1735,7 @@ end
|
||||
-- @param #boolean switch If true, emission is enabled. If false, emission is disabled.
|
||||
-- @return #UNIT self
|
||||
function UNIT:EnableEmission(switch)
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
|
||||
local switch = switch or false
|
||||
|
||||
@@ -1727,9 +1754,12 @@ end
|
||||
-- @param #UNIT self
|
||||
-- @return #string Skill String of skill name.
|
||||
function UNIT:GetSkill()
|
||||
self:F2( self.UnitName )
|
||||
--self:F2( self.UnitName )
|
||||
local name = self.UnitName
|
||||
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||
local skill = "Random"
|
||||
if _DATABASE.Templates.Units[name] and _DATABASE.Templates.Units[name].Template and _DATABASE.Templates.Units[name].Template.skill then
|
||||
skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||
end
|
||||
return skill
|
||||
end
|
||||
|
||||
@@ -1740,7 +1770,7 @@ end
|
||||
-- @return #string VCN Voice Callsign Number or nil if not set/capable.
|
||||
-- @return #string Lead If true, unit is Flight Lead, else false or nil.
|
||||
function UNIT:GetSTN()
|
||||
self:F2(self.UnitName)
|
||||
--self:F2(self.UnitName)
|
||||
local STN = nil -- STN/TN
|
||||
local VCL = nil -- VoiceCallsignLabel
|
||||
local VCN = nil -- VoiceCallsignNumber
|
||||
|
||||
@@ -385,24 +385,26 @@ function WEAPON:GetTarget()
|
||||
|
||||
--Target name
|
||||
local name=object:getName()
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Got Target Object %s, category=%d", object:getName(), category))
|
||||
|
||||
if category==Object.Category.UNIT then
|
||||
|
||||
target=UNIT:FindByName(name)
|
||||
|
||||
elseif category==Object.Category.STATIC then
|
||||
|
||||
target=STATIC:FindByName(name, false)
|
||||
|
||||
elseif category==Object.Category.SCENERY then
|
||||
self:E(self.lid..string.format("ERROR: Scenery target not implemented yet!"))
|
||||
else
|
||||
self:E(self.lid..string.format("ERROR: Object category=%d is not implemented yet!", category))
|
||||
|
||||
if name then
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Got Target Object %s, category=%d", name, category))
|
||||
|
||||
if category==Object.Category.UNIT then
|
||||
|
||||
target=UNIT:FindByName(name)
|
||||
|
||||
elseif category==Object.Category.STATIC then
|
||||
|
||||
target=STATIC:FindByName(name, false)
|
||||
|
||||
elseif category==Object.Category.SCENERY then
|
||||
self:E(self.lid..string.format("ERROR: Scenery target not implemented yet!"))
|
||||
else
|
||||
self:E(self.lid..string.format("ERROR: Object category=%d is not implemented yet!", category))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ Wrapper/Marker.lua
|
||||
Wrapper/Weapon.lua
|
||||
Wrapper/Net.lua
|
||||
Wrapper/Storage.lua
|
||||
Wrapper/DynamicCargo.lua
|
||||
|
||||
Cargo/Cargo.lua
|
||||
Cargo/CargoUnit.lua
|
||||
|
||||
Reference in New Issue
Block a user