mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2026-08-21 21:17:30 +00:00
Merge branch 'develop' into FF/Ops
This commit is contained in:
@@ -3940,11 +3940,7 @@ do
|
|||||||
--
|
--
|
||||||
-- # Demo Missions
|
-- # Demo Missions
|
||||||
--
|
--
|
||||||
-- ### [AI\_A2A\_GCICAP for Caucasus](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-200%20-%20AI_A2A%20-%20GCICAP%20Demonstration)
|
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching/AID-A2A%20-%20AI%20A2A%20Dispatching)
|
||||||
-- ### [AI\_A2A\_GCICAP for NTTR](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-210%20-%20NTTR%20AI_A2A_GCICAP%20Demonstration)
|
|
||||||
-- ### [AI\_A2A\_GCICAP for Normandy](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching/AID-220%20-%20NORMANDY%20AI_A2A_GCICAP%20Demonstration)
|
|
||||||
--
|
|
||||||
-- ### [AI\_A2A\_GCICAP for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/AID%20-%20AI%20Dispatching)
|
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -1224,7 +1224,7 @@ function EVENT:onEvent( Event )
|
|||||||
if Event.TgtObjectCategory == Object.Category.STATIC then
|
if Event.TgtObjectCategory == Object.Category.STATIC then
|
||||||
-- get base data
|
-- get base data
|
||||||
Event.TgtDCSUnit = Event.target
|
Event.TgtDCSUnit = Event.target
|
||||||
if Event.target:isExist() and Event.id ~= 33 then -- leave out ejected seat object
|
if Event.target:isExist() and Event.id ~= 33 and not Event.TgtObjectCategory == Object.Category.COORDINATE then -- leave out ejected seat object
|
||||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||||
Event.TgtUnitName = Event.TgtDCSUnitName
|
Event.TgtUnitName = Event.TgtDCSUnitName
|
||||||
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false )
|
Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false )
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ do -- SET_BASE
|
|||||||
return Names
|
return Names
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Return a table of the Objects in the Set.
|
--- Returns a table of the Objects in the Set.
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return #table Table of objects.
|
-- @return #table Table of objects.
|
||||||
function SET_BASE:GetSetObjects() -- R2.3
|
function SET_BASE:GetSetObjects() -- R2.3
|
||||||
@@ -388,7 +388,6 @@ do -- SET_BASE
|
|||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return Core.Base#BASE
|
-- @return Core.Base#BASE
|
||||||
function SET_BASE:GetFirst()
|
function SET_BASE:GetFirst()
|
||||||
|
|
||||||
local ObjectName = self.Index[1]
|
local ObjectName = self.Index[1]
|
||||||
local FirstObject = self.Set[ObjectName]
|
local FirstObject = self.Set[ObjectName]
|
||||||
self:T3( { FirstObject } )
|
self:T3( { FirstObject } )
|
||||||
@@ -399,8 +398,8 @@ do -- SET_BASE
|
|||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return Core.Base#BASE
|
-- @return Core.Base#BASE
|
||||||
function SET_BASE:GetLast()
|
function SET_BASE:GetLast()
|
||||||
|
local tablemax = table.maxn(self.Index)
|
||||||
local ObjectName = self.Index[#self.Index]
|
local ObjectName = self.Index[tablemax]
|
||||||
local LastObject = self.Set[ObjectName]
|
local LastObject = self.Set[ObjectName]
|
||||||
self:T3( { LastObject } )
|
self:T3( { LastObject } )
|
||||||
return LastObject
|
return LastObject
|
||||||
@@ -410,8 +409,8 @@ do -- SET_BASE
|
|||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return Core.Base#BASE
|
-- @return Core.Base#BASE
|
||||||
function SET_BASE:GetRandom()
|
function SET_BASE:GetRandom()
|
||||||
|
local tablemax = table.maxn(self.Index)
|
||||||
local RandomItem = self.Set[self.Index[math.random( #self.Index )]]
|
local RandomItem = self.Set[self.Index[math.random(1,tablemax)]]
|
||||||
self:T3( { RandomItem } )
|
self:T3( { RandomItem } )
|
||||||
return RandomItem
|
return RandomItem
|
||||||
end
|
end
|
||||||
@@ -420,8 +419,7 @@ do -- SET_BASE
|
|||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return #number Count
|
-- @return #number Count
|
||||||
function SET_BASE:Count()
|
function SET_BASE:Count()
|
||||||
|
return self.Index and table.maxn(self.Index) or 0
|
||||||
return self.Index and #self.Index or 0
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Copies the Filter criteria from a given Set (for rebuilding a new Set based on an existing Set).
|
--- Copies the Filter criteria from a given Set (for rebuilding a new Set based on an existing Set).
|
||||||
@@ -2007,12 +2005,7 @@ do -- SET_UNIT
|
|||||||
-- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT.
|
-- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT.
|
||||||
-- * @{#SET_UNIT.ForEachUnitInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence completely in a @{Core.Zone}, providing the UNIT object and optional parameters to the called function.
|
-- * @{#SET_UNIT.ForEachUnitInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence completely in a @{Core.Zone}, providing the UNIT object and optional parameters to the called function.
|
||||||
-- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence not in a @{Core.Zone}, providing the UNIT object and optional parameters to the called function.
|
-- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence not in a @{Core.Zone}, providing the UNIT object and optional parameters to the called function.
|
||||||
--
|
-- * @{#SET_UNIT:ForEachUnitPerThreatLevel}: Iterate the SET_UNIT **sorted *per Threat Level** and call an iterator function for each **alive** UNIT, providing the UNIT and optional parameters
|
||||||
-- Planned iterators methods in development are (so these are not yet available):
|
|
||||||
--
|
|
||||||
-- * @{#SET_UNIT.ForEachUnitInUnit}: Calls a function for each unit contained within the SET_UNIT.
|
|
||||||
-- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Core.Zone}, providing the UNIT and optional parameters to the called function.
|
|
||||||
-- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Core.Zone}, providing the UNIT and optional parameters to the called function.
|
|
||||||
--
|
--
|
||||||
-- ## 5) SET_UNIT atomic methods
|
-- ## 5) SET_UNIT atomic methods
|
||||||
--
|
--
|
||||||
@@ -3865,6 +3858,8 @@ do -- SET_CLIENT
|
|||||||
Countries = nil,
|
Countries = nil,
|
||||||
ClientPrefixes = nil,
|
ClientPrefixes = nil,
|
||||||
Zones = nil,
|
Zones = nil,
|
||||||
|
Playernames = nil,
|
||||||
|
Callsigns = nil,
|
||||||
},
|
},
|
||||||
FilterMeta = {
|
FilterMeta = {
|
||||||
Coalitions = {
|
Coalitions = {
|
||||||
@@ -3937,6 +3932,40 @@ do -- SET_CLIENT
|
|||||||
return ClientFound
|
return ClientFound
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Builds a set of clients of certain callsigns.
|
||||||
|
-- @param #SET_CLIENT self
|
||||||
|
-- @param #string Callsigns Can be a single string e.g. "Ford", or a table of strings e.g. {"Uzi","Enfield","Chevy"}. Refers to the callsigns as they can be set in the mission editor.
|
||||||
|
-- @return #SET_CLIENT self
|
||||||
|
function SET_CLIENT:FilterCallsigns( Callsigns )
|
||||||
|
if not self.Filter.Callsigns then
|
||||||
|
self.Filter.Callsigns = {}
|
||||||
|
end
|
||||||
|
if type( Callsigns ) ~= "table" then
|
||||||
|
Callsigns = { Callsigns }
|
||||||
|
end
|
||||||
|
for callsignID, callsign in pairs( Callsigns ) do
|
||||||
|
self.Filter.Callsigns[callsign] = callsign
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Builds a set of clients of certain playernames.
|
||||||
|
-- @param #SET_CLIENT self
|
||||||
|
-- @param #string Playernames Can be a single string e.g. "Apple", or a table of strings e.g. {"Walter","Hermann","Gonzo"}. Useful if you have e.g. a common squadron prefix.
|
||||||
|
-- @return #SET_CLIENT self
|
||||||
|
function SET_CLIENT:FilterPlayernames( Playernames )
|
||||||
|
if not self.Filter.Playernames then
|
||||||
|
self.Filter.Playernames = {}
|
||||||
|
end
|
||||||
|
if type( Playernames ) ~= "table" then
|
||||||
|
Playernames = { Playernames }
|
||||||
|
end
|
||||||
|
for PlayernameID, playername in pairs( Playernames ) do
|
||||||
|
self.Filter.Playernames[playername] = playername
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Builds a set of clients of coalitions.
|
--- Builds a set of clients of coalitions.
|
||||||
-- Possible current coalitions are red, blue and neutral.
|
-- Possible current coalitions are red, blue and neutral.
|
||||||
-- @param #SET_CLIENT self
|
-- @param #SET_CLIENT self
|
||||||
@@ -4224,9 +4253,10 @@ do -- SET_CLIENT
|
|||||||
|
|
||||||
if self.Filter.Active ~= nil then
|
if self.Filter.Active ~= nil then
|
||||||
local MClientActive = false
|
local MClientActive = false
|
||||||
if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true) then
|
if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true and MClient:IsAlive() == true) then
|
||||||
MClientActive = true
|
MClientActive = true
|
||||||
end
|
end
|
||||||
|
--self:I( { "Evaluated Active", MClientActive } )
|
||||||
MClientInclude = MClientInclude and MClientActive
|
MClientInclude = MClientInclude and MClientActive
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -4292,20 +4322,46 @@ do -- SET_CLIENT
|
|||||||
self:T( { "Evaluated Prefix", MClientPrefix } )
|
self:T( { "Evaluated Prefix", MClientPrefix } )
|
||||||
MClientInclude = MClientInclude and MClientPrefix
|
MClientInclude = MClientInclude and MClientPrefix
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if self.Filter.Zones then
|
if self.Filter.Zones then
|
||||||
local MClientZone = false
|
local MClientZone = false
|
||||||
for ZoneName, Zone in pairs( self.Filter.Zones ) do
|
for ZoneName, Zone in pairs( self.Filter.Zones ) do
|
||||||
self:T3( "Zone:", ZoneName )
|
self:T3( "Zone:", ZoneName )
|
||||||
local unit = MClient:GetClientGroupUnit()
|
local unit = MClient:GetClientGroupUnit()
|
||||||
if unit and unit:IsInZone(Zone) then
|
if unit and unit:IsInZone(Zone) then
|
||||||
MClientZone = true
|
MClientZone = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
MClientInclude = MClientInclude and MClientZone
|
MClientInclude = MClientInclude and MClientZone
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.Filter.Playernames then
|
||||||
|
local MClientPlayername = false
|
||||||
|
local playername = MClient:GetPlayerName() or "Unknown"
|
||||||
|
--self:I(playername)
|
||||||
|
for _,_Playername in pairs(self.Filter.Playernames) do
|
||||||
|
if playername and string.find(playername,_Playername) then
|
||||||
|
MClientPlayername = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:T( { "Evaluated Playername", MClientPlayername } )
|
||||||
|
MClientInclude = MClientInclude and MClientPlayername
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.Filter.Callsigns then
|
||||||
|
local MClientCallsigns = false
|
||||||
|
local callsign = MClient:GetCallsign()
|
||||||
|
--self:I(callsign)
|
||||||
|
for _,_Callsign in pairs(self.Filter.Callsigns) do
|
||||||
|
if callsign and string.find(callsign,_Callsign) then
|
||||||
|
MClientCallsigns = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:T( { "Evaluated Callsign", MClientCallsigns } )
|
||||||
|
MClientInclude = MClientInclude and MClientCallsigns
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
self:T2( MClientInclude )
|
self:T2( MClientInclude )
|
||||||
return MClientInclude
|
return MClientInclude
|
||||||
end
|
end
|
||||||
@@ -7334,7 +7390,7 @@ do -- SET_SCENERY
|
|||||||
|
|
||||||
if ZoneSet then
|
if ZoneSet then
|
||||||
for _,_zone in pairs(ZoneSet.Set) do
|
for _,_zone in pairs(ZoneSet.Set) do
|
||||||
--self:I("Zone type handed: "..tostring(_zone.ClassName))
|
self:T("Zone type handed: "..tostring(_zone.ClassName))
|
||||||
table.insert(zonenames,_zone:GetName())
|
table.insert(zonenames,_zone:GetName())
|
||||||
end
|
end
|
||||||
self:AddSceneryByName(zonenames)
|
self:AddSceneryByName(zonenames)
|
||||||
@@ -7348,7 +7404,7 @@ do -- SET_SCENERY
|
|||||||
-- @param Core.Zone#ZONE Zone The zone to be scanned. Can be a ZONE_RADIUS (round) or a ZONE_POLYGON (e.g. Quad-Point)
|
-- @param Core.Zone#ZONE Zone The zone to be scanned. Can be a ZONE_RADIUS (round) or a ZONE_POLYGON (e.g. Quad-Point)
|
||||||
-- @return #SET_SCENERY
|
-- @return #SET_SCENERY
|
||||||
function SET_SCENERY:NewFromZone(Zone)
|
function SET_SCENERY:NewFromZone(Zone)
|
||||||
local zone = Zone -- Core.Zone#ZONE_POLYGON
|
local zone = Zone -- Core.Zone#ZONE_RADIUS
|
||||||
if type(Zone) == "string" then
|
if type(Zone) == "string" then
|
||||||
zone = ZONE:FindByName(Zone)
|
zone = ZONE:FindByName(Zone)
|
||||||
end
|
end
|
||||||
@@ -7468,7 +7524,29 @@ do -- SET_SCENERY
|
|||||||
|
|
||||||
return CountU
|
return CountU
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a table of alive objects.
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @return #table Table of alive objects
|
||||||
|
-- @return Core.Set#SET_SCENERY SET of alive objects
|
||||||
|
function SET_SCENERY:GetAliveSet()
|
||||||
|
self:F2()
|
||||||
|
|
||||||
|
local AliveSet = SET_SCENERY:New()
|
||||||
|
|
||||||
|
-- Clean the Set before returning with only the alive Groups.
|
||||||
|
for GroupName, GroupObject in pairs( self.Set ) do
|
||||||
|
local GroupObject = GroupObject -- Wrapper.Group#GROUP
|
||||||
|
if GroupObject then
|
||||||
|
if GroupObject:IsAlive() then
|
||||||
|
AliveSet:Add( GroupName, GroupObject )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return AliveSet.Set or {}, AliveSet
|
||||||
|
end
|
||||||
|
|
||||||
--- Iterate the SET_SCENERY and call an iterator function for each **alive** SCENERY, providing the SCENERY and optional parameters.
|
--- Iterate the SET_SCENERY and call an iterator function for each **alive** SCENERY, providing the SCENERY and optional parameters.
|
||||||
-- @param #SET_SCENERY self
|
-- @param #SET_SCENERY self
|
||||||
-- @param #function IteratorFunction The function that will be called when there is an alive SCENERY in the SET_SCENERY. The function needs to accept a SCENERY parameter.
|
-- @param #function IteratorFunction The function that will be called when there is an alive SCENERY in the SET_SCENERY. The function needs to accept a SCENERY parameter.
|
||||||
|
|||||||
@@ -94,6 +94,7 @@
|
|||||||
-- @field #boolean ReportmBar Report mBar/hpa even if not metric, i.e. for Mirage flights
|
-- @field #boolean ReportmBar Report mBar/hpa even if not metric, i.e. for Mirage flights
|
||||||
-- @field #boolean TransmitOnlyWithPlayers For SRS - If true, only transmit if there are alive Players.
|
-- @field #boolean TransmitOnlyWithPlayers For SRS - If true, only transmit if there are alive Players.
|
||||||
-- @field #string SRSText Text of the complete SRS message (if done at least once, else nil)
|
-- @field #string SRSText Text of the complete SRS message (if done at least once, else nil)
|
||||||
|
-- @field #boolean ATISforFARPs Will be set to true if the base given is a FARP/Helipad
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
--- *It is a very sad thing that nowadays there is so little useless information.* - Oscar Wilde
|
||||||
@@ -309,6 +310,19 @@
|
|||||||
-- atis:Start()
|
-- atis:Start()
|
||||||
--
|
--
|
||||||
-- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Not that backslashes need to be escaped or simply use slashes (as in linux).
|
-- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Not that backslashes need to be escaped or simply use slashes (as in linux).
|
||||||
|
--
|
||||||
|
-- ## FARPS
|
||||||
|
--
|
||||||
|
-- ATIS is working with FARPS, but this requires the usage of SRS. The airbase name for the `New()-method` is the UNIT name of the FARP:
|
||||||
|
--
|
||||||
|
-- atis = ATIS:New("FARP Gold",119,radio.modulation.AM)
|
||||||
|
-- atis:SetMetricUnits()
|
||||||
|
-- atis:SetTransmitOnlyWithPlayers(true)
|
||||||
|
-- atis:SetReportmBar(true)
|
||||||
|
-- atis:SetTowerFrequencies(127.50)
|
||||||
|
-- atis:SetSRS("D:\\DCS\\_SRS\\", "male", "en-US",nil,5002)
|
||||||
|
-- atis:SetAdditionalInformation("Welcome to the Jungle!")
|
||||||
|
-- atis:__Start(3)
|
||||||
--
|
--
|
||||||
-- @field #ATIS
|
-- @field #ATIS
|
||||||
ATIS = {
|
ATIS = {
|
||||||
@@ -351,6 +365,7 @@ ATIS = {
|
|||||||
relHumidity = nil,
|
relHumidity = nil,
|
||||||
ReportmBar = false,
|
ReportmBar = false,
|
||||||
TransmitOnlyWithPlayers = false,
|
TransmitOnlyWithPlayers = false,
|
||||||
|
ATISforFARPs = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- NATO alphabet.
|
--- NATO alphabet.
|
||||||
@@ -593,7 +608,7 @@ _ATIS = {}
|
|||||||
|
|
||||||
--- ATIS class version.
|
--- ATIS class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ATIS.version = "0.9.12"
|
ATIS.version = "0.9.14"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@@ -1049,7 +1064,7 @@ end
|
|||||||
--
|
--
|
||||||
-- * 186° on the Caucaus map
|
-- * 186° on the Caucaus map
|
||||||
-- * 192° on the Nevada map
|
-- * 192° on the Nevada map
|
||||||
-- * 170° on the Normany map
|
-- * 170° on the Normandy map
|
||||||
-- * 182° on the Persian Gulf map
|
-- * 182° on the Persian Gulf map
|
||||||
--
|
--
|
||||||
-- Likewise, to convert *true* into *magnetic* heading, one has to substract easterly and add westerly variation.
|
-- Likewise, to convert *true* into *magnetic* heading, one has to substract easterly and add westerly variation.
|
||||||
@@ -1257,11 +1272,18 @@ end
|
|||||||
function ATIS:onafterStart( From, Event, To )
|
function ATIS:onafterStart( From, Event, To )
|
||||||
|
|
||||||
-- Check that this is an airdrome.
|
-- Check that this is an airdrome.
|
||||||
if self.airbase:GetAirbaseCategory() ~= Airbase.Category.AIRDROME then
|
if self.airbase:GetAirbaseCategory() == Airbase.Category.SHIP then
|
||||||
self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT FARPS or SHIPS.", self.airbasename ) )
|
self:E( self.lid .. string.format( "ERROR: Cannot start ATIS for airbase %s! Only AIRDROMES are supported but NOT SHIPS.", self.airbasename ) )
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Check that if is a Helipad.
|
||||||
|
if self.airbase:GetAirbaseCategory() == Airbase.Category.HELIPAD then
|
||||||
|
self:E( self.lid .. string.format( "EXPERIMENTAL: Starting ATIS for Helipad %s! SRS must be ON", self.airbasename ) )
|
||||||
|
self.ATISforFARPs = true
|
||||||
|
self.useSRS = true
|
||||||
|
end
|
||||||
|
|
||||||
-- Info.
|
-- Info.
|
||||||
self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) )
|
self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) )
|
||||||
|
|
||||||
@@ -1473,10 +1495,19 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
--------------
|
--------------
|
||||||
--- Runway ---
|
--- Runway ---
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
local runwayLanding, rwyLandingLeft=self:GetActiveRunway()
|
|
||||||
local runwayTakeoff, rwyTakeoffLeft=self:GetActiveRunway(true)
|
local runwayLanding, rwyLandingLeft
|
||||||
|
local runwayTakeoff, rwyTakeoffLeft
|
||||||
|
|
||||||
|
if self.airbase:GetAirbaseCategory() == Airbase.Category.HELIPAD then
|
||||||
|
runwayLanding, rwyLandingLeft="PAD 01",false
|
||||||
|
runwayTakeoff, rwyTakeoffLeft="PAD 02",false
|
||||||
|
else
|
||||||
|
runwayLanding, rwyLandingLeft=self:GetActiveRunway()
|
||||||
|
runwayTakeoff, rwyTakeoffLeft=self:GetActiveRunway(true)
|
||||||
|
end
|
||||||
|
|
||||||
------------
|
------------
|
||||||
--- Time ---
|
--- Time ---
|
||||||
------------
|
------------
|
||||||
@@ -1790,7 +1821,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
|
|
||||||
-- Airbase name
|
-- Airbase name
|
||||||
subtitle = string.format( "%s", self.airbasename )
|
subtitle = string.format( "%s", self.airbasename )
|
||||||
if self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil then
|
if (not self.ATISforFARPs) and self.airbasename:find( "AFB" ) == nil and self.airbasename:find( "Airport" ) == nil and self.airbasename:find( "Airstrip" ) == nil and self.airbasename:find( "airfield" ) == nil and self.airbasename:find( "AB" ) == nil then
|
||||||
subtitle = subtitle .. " Airport"
|
subtitle = subtitle .. " Airport"
|
||||||
end
|
end
|
||||||
if not self.useSRS then
|
if not self.useSRS then
|
||||||
@@ -1865,8 +1896,6 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
--self:I("Line 1811")
|
|
||||||
--self:I(alltext)
|
|
||||||
|
|
||||||
-- Visibility
|
-- Visibility
|
||||||
if self.metric then
|
if self.metric then
|
||||||
@@ -1884,8 +1913,6 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
--self:I("Line 1830")
|
|
||||||
--self:I(alltext)
|
|
||||||
|
|
||||||
subtitle = ""
|
subtitle = ""
|
||||||
-- Weather phenomena
|
-- Weather phenomena
|
||||||
@@ -1987,10 +2014,8 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--self:I("Line 1932")
|
|
||||||
|
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
--self:I(alltext)
|
|
||||||
subtitle = ""
|
subtitle = ""
|
||||||
-- Temperature
|
-- Temperature
|
||||||
if self.TDegF then
|
if self.TDegF then
|
||||||
@@ -2019,9 +2044,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 )
|
self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--self:I("Line 1962")
|
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
--self:I(alltext)
|
|
||||||
|
|
||||||
-- Dew point
|
-- Dew point
|
||||||
if self.TDegF then
|
if self.TDegF then
|
||||||
@@ -2050,8 +2073,6 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 )
|
self:Transmission( ATIS.Sound.DegreesCelsius, 0.2 )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--self:I("Line 1992")
|
|
||||||
--self:I(alltext)
|
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
|
|
||||||
-- Altimeter QNH/QFE.
|
-- Altimeter QNH/QFE.
|
||||||
@@ -2117,69 +2138,68 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
--self:I("Line 2049")
|
|
||||||
--self:I(alltext)
|
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
|
|
||||||
-- Active runway.
|
if not self.ATISforFARPs then
|
||||||
local subtitle=string.format("Active runway %s", runwayLanding)
|
-- Active runway.
|
||||||
if rwyLandingLeft==true then
|
local subtitle=string.format("Active runway %s", runwayLanding)
|
||||||
subtitle=subtitle.." Left"
|
|
||||||
elseif rwyLandingLeft==false then
|
|
||||||
subtitle=subtitle.." Right"
|
|
||||||
end
|
|
||||||
local _RUNACT = subtitle
|
|
||||||
if not self.useSRS then
|
|
||||||
self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle)
|
|
||||||
self.radioqueue:Number2Transmission(runwayLanding)
|
|
||||||
if rwyLandingLeft==true then
|
if rwyLandingLeft==true then
|
||||||
self:Transmission(ATIS.Sound.Left, 0.2)
|
subtitle=subtitle.." Left"
|
||||||
elseif rwyLandingLeft==false then
|
elseif rwyLandingLeft==false then
|
||||||
self:Transmission(ATIS.Sound.Right, 0.2)
|
subtitle=subtitle.." Right"
|
||||||
end
|
end
|
||||||
end
|
local _RUNACT = subtitle
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
|
||||||
|
|
||||||
-- Runway length.
|
|
||||||
if self.rwylength then
|
|
||||||
|
|
||||||
local runact = self.airbase:GetActiveRunway( self.runwaym2t )
|
|
||||||
local length = runact.length
|
|
||||||
if not self.metric then
|
|
||||||
length = UTILS.MetersToFeet( length )
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Length in thousands and hundrets of ft/meters.
|
|
||||||
local L1000, L0100 = self:_GetThousandsAndHundreds( length )
|
|
||||||
|
|
||||||
-- Subtitle.
|
|
||||||
local subtitle = string.format( "Runway length %d", length )
|
|
||||||
if self.metric then
|
|
||||||
subtitle = subtitle .. " meters"
|
|
||||||
else
|
|
||||||
subtitle = subtitle .. " feet"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Transmit.
|
|
||||||
if not self.useSRS then
|
if not self.useSRS then
|
||||||
self:Transmission( ATIS.Sound.RunwayLength, 1.0, subtitle )
|
self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle)
|
||||||
if tonumber( L1000 ) > 0 then
|
self.radioqueue:Number2Transmission(runwayLanding)
|
||||||
self.radioqueue:Number2Transmission( L1000 )
|
if rwyLandingLeft==true then
|
||||||
self:Transmission( ATIS.Sound.Thousand, 0.1 )
|
self:Transmission(ATIS.Sound.Left, 0.2)
|
||||||
end
|
elseif rwyLandingLeft==false then
|
||||||
if tonumber( L0100 ) > 0 then
|
self:Transmission(ATIS.Sound.Right, 0.2)
|
||||||
self.radioqueue:Number2Transmission( L0100 )
|
|
||||||
self:Transmission( ATIS.Sound.Hundred, 0.1 )
|
|
||||||
end
|
|
||||||
if self.metric then
|
|
||||||
self:Transmission( ATIS.Sound.Meters, 0.1 )
|
|
||||||
else
|
|
||||||
self:Transmission( ATIS.Sound.Feet, 0.1 )
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
|
|
||||||
|
-- Runway length.
|
||||||
|
if self.rwylength then
|
||||||
|
|
||||||
|
local runact = self.airbase:GetActiveRunway( self.runwaym2t )
|
||||||
|
local length = runact.length
|
||||||
|
if not self.metric then
|
||||||
|
length = UTILS.MetersToFeet( length )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Length in thousands and hundrets of ft/meters.
|
||||||
|
local L1000, L0100 = self:_GetThousandsAndHundreds( length )
|
||||||
|
|
||||||
|
-- Subtitle.
|
||||||
|
local subtitle = string.format( "Runway length %d", length )
|
||||||
|
if self.metric then
|
||||||
|
subtitle = subtitle .. " meters"
|
||||||
|
else
|
||||||
|
subtitle = subtitle .. " feet"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Transmit.
|
||||||
|
if not self.useSRS then
|
||||||
|
self:Transmission( ATIS.Sound.RunwayLength, 1.0, subtitle )
|
||||||
|
if tonumber( L1000 ) > 0 then
|
||||||
|
self.radioqueue:Number2Transmission( L1000 )
|
||||||
|
self:Transmission( ATIS.Sound.Thousand, 0.1 )
|
||||||
|
end
|
||||||
|
if tonumber( L0100 ) > 0 then
|
||||||
|
self.radioqueue:Number2Transmission( L0100 )
|
||||||
|
self:Transmission( ATIS.Sound.Hundred, 0.1 )
|
||||||
|
end
|
||||||
|
if self.metric then
|
||||||
|
self:Transmission( ATIS.Sound.Meters, 0.1 )
|
||||||
|
else
|
||||||
|
self:Transmission( ATIS.Sound.Feet, 0.1 )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Airfield elevation
|
-- Airfield elevation
|
||||||
if self.elevation then
|
if self.elevation then
|
||||||
|
|
||||||
@@ -2246,9 +2266,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- ILS
|
-- ILS
|
||||||
--self:I({ils=self.ils})
|
|
||||||
local ils=self:GetNavPoint(self.ils, runwayLanding, rwyLandingLeft)
|
local ils=self:GetNavPoint(self.ils, runwayLanding, rwyLandingLeft)
|
||||||
--self:I({ils=ils,runwayLanding=runwayLanding, rwyLandingLeft=rwyLandingLeft})
|
|
||||||
if ils then
|
if ils then
|
||||||
subtitle = string.format( "ILS frequency %.2f MHz", ils.frequency )
|
subtitle = string.format( "ILS frequency %.2f MHz", ils.frequency )
|
||||||
if not self.useSRS then
|
if not self.useSRS then
|
||||||
@@ -2263,7 +2281,6 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
self:Transmission( ATIS.Sound.MegaHertz, 0.2 )
|
self:Transmission( ATIS.Sound.MegaHertz, 0.2 )
|
||||||
end
|
end
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
--self:I(alltext)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Outer NDB
|
-- Outer NDB
|
||||||
@@ -2399,6 +2416,8 @@ function ATIS:onafterReport( From, Event, To, Text )
|
|||||||
local text = string.gsub( text, "mmHg", "millimeters of Mercury" )
|
local text = string.gsub( text, "mmHg", "millimeters of Mercury" )
|
||||||
local text = string.gsub( text, "hPa", "hectopascals" )
|
local text = string.gsub( text, "hPa", "hectopascals" )
|
||||||
local text = string.gsub( text, "m/s", "meters per second" )
|
local text = string.gsub( text, "m/s", "meters per second" )
|
||||||
|
local text = string.gsub( text, "TACAN", "tackan" )
|
||||||
|
local text = string.gsub( text, "FARP", "farp" )
|
||||||
|
|
||||||
-- Replace ";" by "."
|
-- Replace ";" by "."
|
||||||
local text = string.gsub( text, ";", " . " )
|
local text = string.gsub( text, ";", " . " )
|
||||||
|
|||||||
@@ -1317,6 +1317,62 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ
|
|||||||
return mission
|
return mission
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- **[AIR]** Create a CAP mission on a group.
|
||||||
|
-- @param #AUFTRAG self
|
||||||
|
-- @param Wrapper.Group#GROUP Grp.
|
||||||
|
-- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft.
|
||||||
|
-- @param #number Speed Orbit speed in knots. Default 250 KIAS.
|
||||||
|
-- @param #number RelHeading Relative heading [0, 360) of race-track pattern in degrees wrt heading of the carrier. Default is heading of the carrier.
|
||||||
|
-- @param #number Leg Length of race-track in NM. Default 14 NM.
|
||||||
|
-- @param #number OffsetDist Relative distance of the first race-track point wrt to the carrier. Default 6 NM.
|
||||||
|
-- @param #number OffsetAngle Relative angle of the first race-track point wrt. to the carrier. Default 180 (behind the boat).
|
||||||
|
-- @param #number UpdateDistance Threshold distance in NM before orbit pattern is updated. Default 5 NM.
|
||||||
|
-- @param #table TargetTypes (Optional) Table of target types. Default `{"Helicopters", "Ground Units", "Light armed ships"}`.
|
||||||
|
-- @param #number EngageRange Max range in nautical miles that the escort group(s) will engage enemies. Default 32 NM (60 km).
|
||||||
|
-- @return #AUFTRAG self
|
||||||
|
function AUFTRAG:NewCAPGROUP(Grp, Altitude, Speed, RelHeading, Leg, OffsetDist, OffsetAngle, UpdateDistance, TargetTypes, EngageRange)
|
||||||
|
|
||||||
|
-- Ensure given TargetTypes parameter is a table.
|
||||||
|
if TargetTypes then
|
||||||
|
if type(TargetTypes)~="table" then
|
||||||
|
TargetTypes={TargetTypes}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Six NM astern.
|
||||||
|
local OffsetVec2={r=OffsetDist or 6, phi=OffsetAngle or 180}
|
||||||
|
|
||||||
|
-- Default leg.
|
||||||
|
Leg=Leg or 14
|
||||||
|
|
||||||
|
local Heading=nil
|
||||||
|
if RelHeading then
|
||||||
|
Heading=-math.abs(RelHeading)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create orbit mission.
|
||||||
|
local mission=AUFTRAG:NewORBIT_GROUP(Grp, Altitude, Speed, Leg, Heading, OffsetVec2, UpdateDistance)
|
||||||
|
-- Mission type CAP.
|
||||||
|
mission.type=AUFTRAG.Type.CAP
|
||||||
|
mission:_SetLogID()
|
||||||
|
|
||||||
|
-- DCS task parameters:
|
||||||
|
local engage = EngageRange or 32
|
||||||
|
local zoneCAPGroup = ZONE_GROUP:New("CAPGroup", Grp, UTILS.NMToMeters(engage))
|
||||||
|
mission.engageZone=zoneCAPGroup
|
||||||
|
mission.engageTargetTypes=TargetTypes or {"Air"}
|
||||||
|
|
||||||
|
-- Mission options:
|
||||||
|
mission.missionTask=ENUMS.MissionTask.CAP
|
||||||
|
mission.optionROE=ENUMS.ROE.OpenFire
|
||||||
|
mission.optionROT=ENUMS.ROT.EvadeFire
|
||||||
|
|
||||||
|
mission.categories={AUFTRAG.Category.AIRCRAFT}
|
||||||
|
|
||||||
|
mission.DCStask=mission:GetDCSMissionTask()
|
||||||
|
|
||||||
|
return mission
|
||||||
|
end
|
||||||
|
|
||||||
--- **[AIR]** Create a CAS mission.
|
--- **[AIR]** Create a CAS mission.
|
||||||
-- @param #AUFTRAG self
|
-- @param #AUFTRAG self
|
||||||
-- @param Core.Zone#ZONE_RADIUS ZoneCAS Circular CAS zone. Detected targets in this zone will be engaged.
|
-- @param Core.Zone#ZONE_RADIUS ZoneCAS Circular CAS zone. Detected targets in this zone will be engaged.
|
||||||
@@ -6112,20 +6168,15 @@ function AUFTRAG:GetDCSMissionTask()
|
|||||||
-- Race-track vector.
|
-- Race-track vector.
|
||||||
orbitRaceTrack=UTILS.Vec2Translate(orbitVec2, self.orbitLeg, heading)
|
orbitRaceTrack=UTILS.Vec2Translate(orbitVec2, self.orbitLeg, heading)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Debug
|
local orbitRaceTrackCoord = nil
|
||||||
--UTILS.RemoveMark(self.orbitCenterMarkID)
|
if orbitRaceTrack then
|
||||||
--self.orbitCenterMarkID=COORDINATE:NewFromVec2(orbitVec2):MarkToAll("Orbit Center")
|
orbitRaceTrackCoord = COORDINATE:NewFromVec2(orbitRaceTrack)
|
||||||
|
end
|
||||||
-- Debug show arrow.
|
|
||||||
--if orbitRaceTrack then
|
|
||||||
--UTILS.RemoveMark(self.orbitArrowMarkID)
|
|
||||||
--self.orbitArrowMarkID=COORDINATE:NewFromVec2(orbitVec2):ArrowToAll(COORDINATE:NewFromVec2(orbitRaceTrack))
|
|
||||||
--end
|
|
||||||
|
|
||||||
-- Create orbit task.
|
-- Create orbit task.
|
||||||
local DCStask=CONTROLLABLE.TaskOrbit(nil, COORDINATE:NewFromVec2(orbitVec2), self.orbitAltitude, self.orbitSpeed, orbitRaceTrack)
|
local DCStask=CONTROLLABLE.TaskOrbit(nil, COORDINATE:NewFromVec2(orbitVec2), self.orbitAltitude, self.orbitSpeed, orbitRaceTrackCoord)
|
||||||
|
|
||||||
-- Add DCS task.
|
-- Add DCS task.
|
||||||
table.insert(DCStasks, DCStask)
|
table.insert(DCStasks, DCStask)
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ## AWACS
|
-- **AWACS** - MOOSE AI AWACS Operations using text-to-speech.
|
||||||
--
|
|
||||||
-- * MOOSE AI AWACS Operations using text-to-speech.
|
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -19,7 +17,7 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **applevangelist**
|
-- ### Author: **applevangelist**
|
||||||
-- @date Last Update November 2022
|
-- @date Last Update December 2022
|
||||||
-- @module Ops.AWACS
|
-- @module Ops.AWACS
|
||||||
-- @image OPS_AWACS.jpg
|
-- @image OPS_AWACS.jpg
|
||||||
|
|
||||||
@@ -106,7 +104,7 @@ do
|
|||||||
-- @field #boolean NoGroupTags Set to true if you don't want group tags.
|
-- @field #boolean NoGroupTags Set to true if you don't want group tags.
|
||||||
-- @field #boolean SuppressScreenOutput Set to true to suppress all screen output.
|
-- @field #boolean SuppressScreenOutput Set to true to suppress all screen output.
|
||||||
-- @field #boolean NoMissileCalls Suppress missile callouts
|
-- @field #boolean NoMissileCalls Suppress missile callouts
|
||||||
-- @field #boolean PlayerCapAssigment Assign players to CAP tasks when they are logged on
|
-- @field #boolean PlayerCapAssignment Assign players to CAP tasks when they are logged on
|
||||||
-- @field #number GoogleTTSPadding
|
-- @field #number GoogleTTSPadding
|
||||||
-- @field #number WindowsTTSPadding
|
-- @field #number WindowsTTSPadding
|
||||||
-- @field #boolean AllowMarkers
|
-- @field #boolean AllowMarkers
|
||||||
@@ -114,6 +112,7 @@ do
|
|||||||
-- @field #boolean GCI Act as GCI
|
-- @field #boolean GCI Act as GCI
|
||||||
-- @field Wrapper.Group#GROUP GCIGroup EWR group object for GCI ops
|
-- @field Wrapper.Group#GROUP GCIGroup EWR group object for GCI ops
|
||||||
-- @field #string locale Localization
|
-- @field #string locale Localization
|
||||||
|
-- @field #boolean IncludeHelicopters
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
@@ -358,7 +357,7 @@ do
|
|||||||
-- testawacs.maxassigndistance = 100 -- Don't assign targets further out than this, in NM.
|
-- testawacs.maxassigndistance = 100 -- Don't assign targets further out than this, in NM.
|
||||||
-- testawacs.debug = false -- set to true to produce more log output.
|
-- testawacs.debug = false -- set to true to produce more log output.
|
||||||
-- testawacs.NoMissileCalls = true -- suppress missile callouts
|
-- testawacs.NoMissileCalls = true -- suppress missile callouts
|
||||||
-- testawacs.PlayerCapAssigment = true -- no intercept task assignments for players
|
-- testawacs.PlayerCapAssignment = true -- no intercept task assignments for players
|
||||||
-- testawacs.invisible = false -- set AWACS to be invisible to hostiles
|
-- testawacs.invisible = false -- set AWACS to be invisible to hostiles
|
||||||
-- testawacs.immortal = false -- set AWACS to be immortal
|
-- testawacs.immortal = false -- set AWACS to be immortal
|
||||||
-- -- By default, the radio queue is checked every 10 secs. This is altered by the calculated length of the sentence to speak
|
-- -- By default, the radio queue is checked every 10 secs. This is altered by the calculated length of the sentence to speak
|
||||||
@@ -367,6 +366,7 @@ do
|
|||||||
-- testawacs.GoogleTTSPadding = 1 -- seconds
|
-- testawacs.GoogleTTSPadding = 1 -- seconds
|
||||||
-- testawacs.WindowsTTSPadding = 2.5 -- seconds
|
-- testawacs.WindowsTTSPadding = 2.5 -- seconds
|
||||||
-- testawacs.PikesSpecialSwitch = false -- if set to true, AWACS will omit the "doing xy knots" on the station assignement callout
|
-- testawacs.PikesSpecialSwitch = false -- if set to true, AWACS will omit the "doing xy knots" on the station assignement callout
|
||||||
|
-- testawacs.IncludeHelicopters = false -- if set to true, Helicopter pilots will also get the AWACS Menu and options
|
||||||
--
|
--
|
||||||
-- ## 9.2 Bespoke random voices for AI CAP (Google TTS only)
|
-- ## 9.2 Bespoke random voices for AI CAP (Google TTS only)
|
||||||
--
|
--
|
||||||
@@ -499,7 +499,7 @@ do
|
|||||||
-- @field #AWACS
|
-- @field #AWACS
|
||||||
AWACS = {
|
AWACS = {
|
||||||
ClassName = "AWACS", -- #string
|
ClassName = "AWACS", -- #string
|
||||||
version = "0.2.49", -- #string
|
version = "0.2.51", -- #string
|
||||||
lid = "", -- #string
|
lid = "", -- #string
|
||||||
coalition = coalition.side.BLUE, -- #number
|
coalition = coalition.side.BLUE, -- #number
|
||||||
coalitiontxt = "blue", -- #string
|
coalitiontxt = "blue", -- #string
|
||||||
@@ -580,12 +580,13 @@ AWACS = {
|
|||||||
NoMissileCalls = true,
|
NoMissileCalls = true,
|
||||||
GoogleTTSPadding = 1,
|
GoogleTTSPadding = 1,
|
||||||
WindowsTTSPadding = 2.5,
|
WindowsTTSPadding = 2.5,
|
||||||
PlayerCapAssigment = true,
|
PlayerCapAssignment = true,
|
||||||
AllowMarkers = false,
|
AllowMarkers = false,
|
||||||
PlayerStationName = nil,
|
PlayerStationName = nil,
|
||||||
GCI = false,
|
GCI = false,
|
||||||
GCIGroup = nil,
|
GCIGroup = nil,
|
||||||
locale = "en",
|
locale = "en",
|
||||||
|
IncludeHelicopters = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -916,13 +917,13 @@ AWACS.TaskStatus = {
|
|||||||
--@field #boolean FromAI
|
--@field #boolean FromAI
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO-List 0.2.42
|
-- TODO-List 0.2.51
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
--
|
--
|
||||||
-- DONE - WIP - Player tasking, VID
|
-- DONE - WIP - Player tasking, VID
|
||||||
-- DONE - Localization (sensible?)
|
-- DONE - Localization (sensible?)
|
||||||
-- TODO - (LOW) LotATC
|
-- TODO - (LOW) LotATC
|
||||||
-- TODO - SW Optimization
|
-- DONE - SW Optimization
|
||||||
-- WONTDO - Maybe check in AI only when airborne
|
-- WONTDO - Maybe check in AI only when airborne
|
||||||
-- DONE - remove SSML tag when not on google (currently sometimes spoken)
|
-- DONE - remove SSML tag when not on google (currently sometimes spoken)
|
||||||
-- DONE - Maybe - Assign specific number of AI CAP to a station
|
-- DONE - Maybe - Assign specific number of AI CAP to a station
|
||||||
@@ -953,6 +954,7 @@ AWACS.TaskStatus = {
|
|||||||
-- DONE - Shift Length AWACS/AI
|
-- DONE - Shift Length AWACS/AI
|
||||||
-- DONE - (WIP) Reporting
|
-- DONE - (WIP) Reporting
|
||||||
-- DONE - Do not report non-airborne groups
|
-- DONE - Do not report non-airborne groups
|
||||||
|
-- DONE - Added option for helos
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- Constructor
|
-- Constructor
|
||||||
@@ -1125,7 +1127,7 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
|
|||||||
self.MenuStrict = true
|
self.MenuStrict = true
|
||||||
self.maxassigndistance = 100 --nm
|
self.maxassigndistance = 100 --nm
|
||||||
self.NoMissileCalls = true
|
self.NoMissileCalls = true
|
||||||
self.PlayerCapAssigment = true
|
self.PlayerCapAssignment = true
|
||||||
|
|
||||||
-- managed groups
|
-- managed groups
|
||||||
self.ManagedGrps = {} -- #table of #AWACS.ManagedGroup entries
|
self.ManagedGrps = {} -- #table of #AWACS.ManagedGroup entries
|
||||||
@@ -3470,7 +3472,7 @@ function AWACS:_CheckInAI(FlightGroup,Group,AuftragsNr)
|
|||||||
local CAPVoice = self.CAPVoice
|
local CAPVoice = self.CAPVoice
|
||||||
|
|
||||||
if self.PathToGoogleKey then
|
if self.PathToGoogleKey then
|
||||||
CAPVoice = AWACS.CapVoices[math.floor(math.random(1,10))]
|
CAPVoice = self.CapVoices[math.floor(math.random(1,10))]
|
||||||
end
|
end
|
||||||
|
|
||||||
FlightGroup:SetSRS(self.PathToSRS,self.CAPGender,self.CAPCulture,CAPVoice,self.Port,self.PathToGoogleKey,"FLIGHT")
|
FlightGroup:SetSRS(self.PathToSRS,self.CAPGender,self.CAPCulture,CAPVoice,self.Port,self.PathToGoogleKey,"FLIGHT")
|
||||||
@@ -3589,13 +3591,15 @@ function AWACS:_SetClientMenus()
|
|||||||
local bogeydope = MENU_GROUP_COMMAND:New(cgrp,"Bogey Dope",basemenu,self._BogeyDope,self,cgrp)
|
local bogeydope = MENU_GROUP_COMMAND:New(cgrp,"Bogey Dope",basemenu,self._BogeyDope,self,cgrp)
|
||||||
local picture = MENU_GROUP_COMMAND:New(cgrp,"Picture",basemenu,self._Picture,self,cgrp)
|
local picture = MENU_GROUP_COMMAND:New(cgrp,"Picture",basemenu,self._Picture,self,cgrp)
|
||||||
local declare = MENU_GROUP_COMMAND:New(cgrp,"Declare",basemenu,self._Declare,self,cgrp)
|
local declare = MENU_GROUP_COMMAND:New(cgrp,"Declare",basemenu,self._Declare,self,cgrp)
|
||||||
|
|
||||||
local tasking = MENU_GROUP:New(cgrp,"Tasking",basemenu)
|
local tasking = MENU_GROUP:New(cgrp,"Tasking",basemenu)
|
||||||
local showtask = MENU_GROUP_COMMAND:New(cgrp,"Showtask",tasking,self._Showtask,self,cgrp)
|
local showtask = MENU_GROUP_COMMAND:New(cgrp,"Showtask",tasking,self._Showtask,self,cgrp)
|
||||||
local commit = MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp)
|
|
||||||
local unable = MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp)
|
if self.PlayerCapAssignment then
|
||||||
local abort = MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp)
|
local commit = MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp)
|
||||||
--local judy = MENU_GROUP_COMMAND:New(cgrp,"Judy",tasking,self._Judy,self,cgrp)
|
local unable = MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp)
|
||||||
|
local abort = MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp)
|
||||||
|
--local judy = MENU_GROUP_COMMAND:New(cgrp,"Judy",tasking,self._Judy,self,cgrp)
|
||||||
|
end
|
||||||
|
|
||||||
if self.AwacsROE == AWACS.ROE.POLICE or self.AwacsROE == AWACS.ROE.VID then
|
if self.AwacsROE == AWACS.ROE.POLICE or self.AwacsROE == AWACS.ROE.VID then
|
||||||
local vid = MENU_GROUP:New(cgrp,"VID as",tasking)
|
local vid = MENU_GROUP:New(cgrp,"VID as",tasking)
|
||||||
@@ -5534,6 +5538,20 @@ end
|
|||||||
-- FSM Functions
|
-- FSM Functions
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- [Internal] onbeforeStart
|
||||||
|
-- @param #AWACS self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #AWACS self
|
||||||
|
function AWACS:onbeforeStart(From,Event,to)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
if self.IncludeHelicopters then
|
||||||
|
self.clientset:FilterCategories("helicopter")
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- [Internal] onafterStart
|
--- [Internal] onafterStart
|
||||||
-- @param #AWACS self
|
-- @param #AWACS self
|
||||||
-- @param #string From
|
-- @param #string From
|
||||||
@@ -5959,7 +5977,7 @@ function AWACS:onafterStatus(From, Event, To)
|
|||||||
local AI, Humans = self:_GetIdlePilots()
|
local AI, Humans = self:_GetIdlePilots()
|
||||||
-- assign Pilot if there are targets and available Pilots, prefer Humans to AI
|
-- assign Pilot if there are targets and available Pilots, prefer Humans to AI
|
||||||
-- DONE - Implemented AI First, Humans laters - need to work out how to loop the targets to assign a pilot
|
-- DONE - Implemented AI First, Humans laters - need to work out how to loop the targets to assign a pilot
|
||||||
if outcome and #Humans > 0 and self.PlayerCapAssigment then
|
if outcome and #Humans > 0 and self.PlayerCapAssignment then
|
||||||
-- add a task for AI
|
-- add a task for AI
|
||||||
self:_AssignPilotToTarget(Humans,targets)
|
self:_AssignPilotToTarget(Humans,targets)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -711,6 +711,7 @@ do
|
|||||||
-- my_ctld.droppedbeacontimeout = 600 -- dropped beacon lasts 10 minutes
|
-- my_ctld.droppedbeacontimeout = 600 -- dropped beacon lasts 10 minutes
|
||||||
-- my_ctld.usesubcats = false -- use sub-category names for crates, adds an extra menu layer in "Get Crates", useful if you have > 10 crate types.
|
-- my_ctld.usesubcats = false -- use sub-category names for crates, adds an extra menu layer in "Get Crates", useful if you have > 10 crate types.
|
||||||
-- my_ctld.placeCratesAhead = false -- place crates straight ahead of the helicopter, in a random way. If true, crates are more neatly sorted.
|
-- my_ctld.placeCratesAhead = false -- place crates straight ahead of the helicopter, in a random way. If true, crates are more neatly sorted.
|
||||||
|
-- my_ctld.nobuildinloadzones = true -- forbid players to build stuff in LOAD zones if set to `true`
|
||||||
--
|
--
|
||||||
-- ## 2.1 User functions
|
-- ## 2.1 User functions
|
||||||
--
|
--
|
||||||
@@ -1015,7 +1016,16 @@ CTLD = {
|
|||||||
-- @type CTLD.ZoneBeacon
|
-- @type CTLD.ZoneBeacon
|
||||||
-- @field #string name -- Name of zone for the coordinate
|
-- @field #string name -- Name of zone for the coordinate
|
||||||
-- @field #number frequency -- in mHz
|
-- @field #number frequency -- in mHz
|
||||||
-- @field #number modulation -- i.e.radio.modulation.FM or radio.modulation.AM
|
-- @field #number modulation -- i.e.CTLD.RadioModulation.FM or CTLD.RadioModulation.AM
|
||||||
|
|
||||||
|
--- Radio Modulation
|
||||||
|
-- @type CTLD.RadioModulation
|
||||||
|
-- @field #number AM
|
||||||
|
-- @field #number FM
|
||||||
|
CTLD.RadioModulation = {
|
||||||
|
AM = 0,
|
||||||
|
FM = 1,
|
||||||
|
}
|
||||||
|
|
||||||
--- Zone Info.
|
--- Zone Info.
|
||||||
-- @type CTLD.CargoZone
|
-- @type CTLD.CargoZone
|
||||||
@@ -1078,7 +1088,7 @@ CTLD.UnitTypes = {
|
|||||||
|
|
||||||
--- CTLD class version.
|
--- CTLD class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
CTLD.version="1.0.20"
|
CTLD.version="1.0.24"
|
||||||
|
|
||||||
--- Instantiate a new CTLD.
|
--- Instantiate a new CTLD.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
@@ -1161,6 +1171,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
|||||||
|
|
||||||
-- radio beacons
|
-- radio beacons
|
||||||
self.RadioSound = "beacon.ogg"
|
self.RadioSound = "beacon.ogg"
|
||||||
|
self.RadioPath = "l10n/DEFAULT/"
|
||||||
|
|
||||||
-- zones stuff
|
-- zones stuff
|
||||||
self.pickupZones = {}
|
self.pickupZones = {}
|
||||||
@@ -1243,6 +1254,9 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
|||||||
self.usesubcats = false
|
self.usesubcats = false
|
||||||
self.subcats = {}
|
self.subcats = {}
|
||||||
|
|
||||||
|
-- disallow building in loadzones
|
||||||
|
self.nobuildinloadzones = true
|
||||||
|
|
||||||
local AliaS = string.gsub(self.alias," ","_")
|
local AliaS = string.gsub(self.alias," ","_")
|
||||||
self.filename = string.format("CTLD_%s_Persist.csv",AliaS)
|
self.filename = string.format("CTLD_%s_Persist.csv",AliaS)
|
||||||
|
|
||||||
@@ -1635,12 +1649,54 @@ function CTLD:_SendMessage(Text, Time, Clearscreen, Group)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- (Internal) Find a troops CTLD_CARGO object in stock
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string Name of the object
|
||||||
|
-- @return #CTLD_CARGO Cargo object, nil if it cannot be found
|
||||||
|
function CTLD:_FindTroopsCargoObject(Name)
|
||||||
|
self:T(self.lid .. " _FindTroopsCargoObject")
|
||||||
|
local cargo = nil
|
||||||
|
for _,_cargo in pairs(self.Cargo_Troops)do
|
||||||
|
local cargo = _cargo -- #CTLD_CARGO
|
||||||
|
if cargo.Name == Name then
|
||||||
|
return cargo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (User) Pre-load troops into a helo, e.g. for airstart. Unit **must** be alive in-game, i.e. player has taken the slot!
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object
|
||||||
|
-- @param #string Troopname The name of the Troops to be loaded. Must be created prior in the CTLD setup!
|
||||||
|
-- @return #CTLD self
|
||||||
|
-- @usage
|
||||||
|
-- local client = UNIT:FindByName("Helo-1-1")
|
||||||
|
-- if client and client:IsAlive() then
|
||||||
|
-- myctld:PreloadTroops(client,"Infantry")
|
||||||
|
-- end
|
||||||
|
function CTLD:PreloadTroops(Unit,Troopname)
|
||||||
|
self:T(self.lid .. " PreloadTroops")
|
||||||
|
local name = Troopname or "Unknown"
|
||||||
|
if Unit and Unit:IsAlive() then
|
||||||
|
local cargo = self:_FindTroopsCargoObject(name)
|
||||||
|
local group = Unit:GetGroup()
|
||||||
|
if cargo then
|
||||||
|
self:_LoadTroops(group,Unit,cargo,true)
|
||||||
|
else
|
||||||
|
self:E(self.lid.." Troops preload - Cargo Object "..name.." not found!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Function to load troops into a heli.
|
--- (Internal) Function to load troops into a heli.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
-- @param Wrapper.Group#GROUP Group
|
-- @param Wrapper.Group#GROUP Group
|
||||||
-- @param Wrapper.Unit#UNIT Unit
|
-- @param Wrapper.Unit#UNIT Unit
|
||||||
-- @param #CTLD_CARGO Cargotype
|
-- @param #CTLD_CARGO Cargotype
|
||||||
function CTLD:_LoadTroops(Group, Unit, Cargotype)
|
-- @param #boolean Inject
|
||||||
|
function CTLD:_LoadTroops(Group, Unit, Cargotype, Inject)
|
||||||
self:T(self.lid .. " _LoadTroops")
|
self:T(self.lid .. " _LoadTroops")
|
||||||
-- check if we have stock
|
-- check if we have stock
|
||||||
local instock = Cargotype:GetStock()
|
local instock = Cargotype:GetStock()
|
||||||
@@ -1648,7 +1704,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype)
|
|||||||
local cgotype = Cargotype:GetType()
|
local cgotype = Cargotype:GetType()
|
||||||
local cgonetmass = Cargotype:GetNetMass()
|
local cgonetmass = Cargotype:GetNetMass()
|
||||||
local maxloadable = self:_GetMaxLoadableMass(Unit)
|
local maxloadable = self:_GetMaxLoadableMass(Unit)
|
||||||
if type(instock) == "number" and tonumber(instock) <= 0 and tonumber(instock) ~= -1 then
|
if type(instock) == "number" and tonumber(instock) <= 0 and tonumber(instock) ~= -1 and not Inject then
|
||||||
-- nothing left over
|
-- nothing left over
|
||||||
self:_SendMessage(string.format("Sorry, all %s are gone!", cgoname), 10, false, Group)
|
self:_SendMessage(string.format("Sorry, all %s are gone!", cgoname), 10, false, Group)
|
||||||
return self
|
return self
|
||||||
@@ -1656,21 +1712,22 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype)
|
|||||||
-- landed or hovering over load zone?
|
-- landed or hovering over load zone?
|
||||||
local grounded = not self:IsUnitInAir(Unit)
|
local grounded = not self:IsUnitInAir(Unit)
|
||||||
local hoverload = self:CanHoverLoad(Unit)
|
local hoverload = self:CanHoverLoad(Unit)
|
||||||
--local dooropen = UTILS.IsLoadingDoorOpen(Unit:GetName()) and self.pilotmustopendoors
|
|
||||||
-- check if we are in LOAD zone
|
-- check if we are in LOAD zone
|
||||||
local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
|
local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
|
||||||
if not inzone then
|
if not inzone then
|
||||||
inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
|
inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
|
||||||
end
|
end
|
||||||
if not inzone then
|
if not Inject then
|
||||||
self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group)
|
if not inzone then
|
||||||
if not self.debug then return self end
|
self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group)
|
||||||
elseif not grounded and not hoverload then
|
if not self.debug then return self end
|
||||||
self:_SendMessage("You need to land or hover in position to load!", 10, false, Group)
|
elseif not grounded and not hoverload then
|
||||||
if not self.debug then return self end
|
self:_SendMessage("You need to land or hover in position to load!", 10, false, Group)
|
||||||
elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
|
if not self.debug then return self end
|
||||||
self:_SendMessage("You need to open the door(s) to load troops!", 10, false, Group)
|
elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
|
||||||
if not self.debug then return self end
|
self:_SendMessage("You need to open the door(s) to load troops!", 10, false, Group)
|
||||||
|
if not self.debug then return self end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
-- load troops into heli
|
-- load troops into heli
|
||||||
local group = Group -- Wrapper.Group#GROUP
|
local group = Group -- Wrapper.Group#GROUP
|
||||||
@@ -2093,10 +2150,11 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
|
|||||||
self.CargoCounter = self.CargoCounter + 1
|
self.CargoCounter = self.CargoCounter + 1
|
||||||
local realcargo = nil
|
local realcargo = nil
|
||||||
if drop then
|
if drop then
|
||||||
realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,subcat)
|
--CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory)
|
||||||
|
realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,true,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,nil,subcat)
|
||||||
table.insert(droppedcargo,realcargo)
|
table.insert(droppedcargo,realcargo)
|
||||||
else
|
else
|
||||||
realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],true,cargotype.PerCrateMass,subcat)
|
realcargo = CTLD_CARGO:New(self.CargoCounter,cratename,templ,sorte,false,false,cratesneeded,self.Spawned_Crates[self.CrateCounter],false,cargotype.PerCrateMass,nil,subcat)
|
||||||
Cargo:RemoveStock()
|
Cargo:RemoveStock()
|
||||||
end
|
end
|
||||||
table.insert(self.Spawned_Cargo, realcargo)
|
table.insert(self.Spawned_Cargo, realcargo)
|
||||||
@@ -2824,6 +2882,14 @@ function CTLD:_BuildCrates(Group, Unit,Engineering)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if not Engineering and self.nobuildinloadzones then
|
||||||
|
-- are we in a load zone?
|
||||||
|
local inloadzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
|
||||||
|
if inloadzone then
|
||||||
|
self:_SendMessage("You cannot build in a loading area, Pilot!", 10, false, Group)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
end
|
||||||
-- get nearby crates
|
-- get nearby crates
|
||||||
local finddist = self.CrateDistance or 35
|
local finddist = self.CrateDistance or 35
|
||||||
local crates,number = self:_FindCratesNearby(Group,Unit, finddist,true) -- #table
|
local crates,number = self:_FindCratesNearby(Group,Unit, finddist,true) -- #table
|
||||||
@@ -3440,7 +3506,7 @@ function CTLD:_GetFMBeacon(Name)
|
|||||||
table.insert(self.UsedFMFrequencies, FM)
|
table.insert(self.UsedFMFrequencies, FM)
|
||||||
beacon.name = Name
|
beacon.name = Name
|
||||||
beacon.frequency = FM / 1000000
|
beacon.frequency = FM / 1000000
|
||||||
beacon.modulation = radio.modulation.FM
|
beacon.modulation = CTLD.RadioModulation.FM
|
||||||
return beacon
|
return beacon
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3460,7 +3526,7 @@ function CTLD:_GetUHFBeacon(Name)
|
|||||||
table.insert(self.UsedUHFFrequencies, UHF)
|
table.insert(self.UsedUHFFrequencies, UHF)
|
||||||
beacon.name = Name
|
beacon.name = Name
|
||||||
beacon.frequency = UHF / 1000000
|
beacon.frequency = UHF / 1000000
|
||||||
beacon.modulation = radio.modulation.AM
|
beacon.modulation = CTLD.RadioModulation.AM
|
||||||
|
|
||||||
return beacon
|
return beacon
|
||||||
end
|
end
|
||||||
@@ -3481,7 +3547,7 @@ function CTLD:_GetVHFBeacon(Name)
|
|||||||
table.insert(self.UsedVHFFrequencies, VHF)
|
table.insert(self.UsedVHFFrequencies, VHF)
|
||||||
beacon.name = Name
|
beacon.name = Name
|
||||||
beacon.frequency = VHF / 1000000
|
beacon.frequency = VHF / 1000000
|
||||||
beacon.modulation = radio.modulation.FM
|
beacon.modulation = CTLD.RadioModulation.FM
|
||||||
return beacon
|
return beacon
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3690,24 +3756,45 @@ function CTLD:_AddRadioBeacon(Name, Sound, Mhz, Modulation, IsShip, IsDropped)
|
|||||||
local Sound = Sound or "beacon.ogg"
|
local Sound = Sound or "beacon.ogg"
|
||||||
if IsDropped and Zone then
|
if IsDropped and Zone then
|
||||||
local ZoneCoord = Zone
|
local ZoneCoord = Zone
|
||||||
local ZoneVec3 = ZoneCoord:GetVec3()
|
local ZoneVec3 = ZoneCoord:GetVec3(1)
|
||||||
local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz
|
local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz
|
||||||
local Sound = "l10n/DEFAULT/"..Sound
|
local Sound = self.RadioPath..Sound
|
||||||
trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight
|
trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, tonumber(Frequency), 1000) -- Beacon in MP only runs for 30secs straight
|
||||||
--local status = string.format("***** Beacon added Freq %s Mod %s", Mhz, UTILS.GetModulationName(Modulation))
|
|
||||||
--MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug)
|
|
||||||
elseif Zone then
|
elseif Zone then
|
||||||
local ZoneCoord = Zone:GetCoordinate(2)
|
local ZoneCoord = Zone:GetCoordinate(1)
|
||||||
local ZoneVec3 = ZoneCoord:GetVec3()
|
local ZoneVec3 = ZoneCoord:GetVec3()
|
||||||
local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz
|
local Frequency = string.format("%09d",Mhz * 1000000) -- Freq in Hertz
|
||||||
local Sound = "l10n/DEFAULT/"..Sound
|
local Sound = self.RadioPath..Sound
|
||||||
trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, Frequency, 1000) -- Beacon in MP only runs for 30secs straight
|
trigger.action.radioTransmission(Sound, ZoneVec3, Modulation, false, tonumber(Frequency), 1000) -- Beacon in MP only runs for 30secs straight
|
||||||
--local status = string.format("***** Beacon added Freq %s Mod %s", Mhz, UTILS.GetModulationName(Modulation))
|
|
||||||
--MESSAGE:New(status,10,"Debug"):ToLogIf(self.debug)
|
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set folder path where the CTLD sound files are located **within you mission (miz) file**.
|
||||||
|
-- The default path is "l10n/DEFAULT/" but sound files simply copied there will be removed by DCS the next time you save the mission.
|
||||||
|
-- However, if you create a new folder inside the miz file, which contains the sounds, it will not be deleted and can be used.
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string FolderPath The path to the sound files, e.g. "CTLD_Soundfiles/".
|
||||||
|
-- @return #CTLD self
|
||||||
|
function CTLD:SetSoundfilesFolder( FolderPath )
|
||||||
|
self:T(self.lid .. " SetSoundfilesFolder")
|
||||||
|
-- Check that it ends with /
|
||||||
|
if FolderPath then
|
||||||
|
local lastchar = string.sub( FolderPath, -1 )
|
||||||
|
if lastchar ~= "/" then
|
||||||
|
FolderPath = FolderPath .. "/"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Folderpath.
|
||||||
|
self.RadioPath = FolderPath
|
||||||
|
|
||||||
|
-- Info message.
|
||||||
|
self:I( self.lid .. string.format( "Setting sound files folder to: %s", self.RadioPath ) )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Function to refresh radio beacons
|
--- (Internal) Function to refresh radio beacons
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
function CTLD:_RefreshRadioBeacons()
|
function CTLD:_RefreshRadioBeacons()
|
||||||
@@ -3730,10 +3817,14 @@ function CTLD:_RefreshRadioBeacons()
|
|||||||
local Name = czone.name
|
local Name = czone.name
|
||||||
local FM = FMbeacon.frequency -- MHz
|
local FM = FMbeacon.frequency -- MHz
|
||||||
local VHF = VHFbeacon.frequency -- KHz
|
local VHF = VHFbeacon.frequency -- KHz
|
||||||
local UHF = UHFbeacon.frequency -- MHz
|
local UHF = UHFbeacon.frequency -- MHz
|
||||||
self:_AddRadioBeacon(Name,Sound,FM,radio.modulation.FM, IsShip, IsDropped)
|
-- local co = coroutine.create(self._AddRadioBeacon)
|
||||||
self:_AddRadioBeacon(Name,Sound,VHF,radio.modulation.FM, IsShip, IsDropped)
|
--coroutine.resume(co, self, Name,Sound,FM,CTLD.RadioModulation.FM, IsShip, IsDropped)
|
||||||
self:_AddRadioBeacon(Name,Sound,UHF,radio.modulation.AM, IsShip, IsDropped)
|
--coroutine.resume(co, self, Name,Sound,VHF,CTLD.RadioModulation.FM, IsShip, IsDropped)
|
||||||
|
--coroutine.resume(co, self, Name,Sound,UHF,CTLD.RadioModulation.AM, IsShip, IsDropped)
|
||||||
|
self:_AddRadioBeacon(Name,Sound,FM, CTLD.RadioModulation.FM, IsShip, IsDropped)
|
||||||
|
self:_AddRadioBeacon(Name,Sound,VHF,CTLD.RadioModulation.FM, IsShip, IsDropped)
|
||||||
|
self:_AddRadioBeacon(Name,Sound,UHF,CTLD.RadioModulation.AM, IsShip, IsDropped)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4747,7 +4838,7 @@ end
|
|||||||
for _,_cargo in pairs (stcstable) do
|
for _,_cargo in pairs (stcstable) do
|
||||||
local cargo = _cargo -- #CTLD_CARGO
|
local cargo = _cargo -- #CTLD_CARGO
|
||||||
local object = cargo:GetPositionable() -- Wrapper.Static#STATIC
|
local object = cargo:GetPositionable() -- Wrapper.Static#STATIC
|
||||||
if object and object:IsAlive() and cargo:WasDropped() then
|
if object and object:IsAlive() and (cargo:WasDropped() or not cargo:HasMoved()) then
|
||||||
statics[#statics+1] = cargo
|
statics[#statics+1] = cargo
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -712,7 +712,7 @@ end
|
|||||||
-- @param #FLIGHTGROUP self
|
-- @param #FLIGHTGROUP self
|
||||||
-- @return #boolean If true, has landed somewhere.
|
-- @return #boolean If true, has landed somewhere.
|
||||||
function FLIGHTGROUP:IsLandedAt()
|
function FLIGHTGROUP:IsLandedAt()
|
||||||
is=self:Is("LandedAt")
|
local is=self:Is("LandedAt")
|
||||||
return is
|
return is
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -908,7 +908,7 @@ function FLIGHTGROUP:Status()
|
|||||||
if mission and mission.updateDCSTask then
|
if mission and mission.updateDCSTask then
|
||||||
|
|
||||||
-- Orbit missions might need updates.
|
-- Orbit missions might need updates.
|
||||||
if (mission:GetType()==AUFTRAG.Type.ORBIT or mission:GetType()==AUFTRAG.Type.RECOVERYTANKER) and mission.orbitVec2 then
|
if (mission:GetType()==AUFTRAG.Type.ORBIT or mission:GetType()==AUFTRAG.Type.RECOVERYTANKER or mission:GetType()==AUFTRAG.Type.CAP) and mission.orbitVec2 then
|
||||||
|
|
||||||
-- Get 2D vector of orbit target.
|
-- Get 2D vector of orbit target.
|
||||||
local vec2=mission:GetTargetVec2()
|
local vec2=mission:GetTargetVec2()
|
||||||
|
|||||||
@@ -366,7 +366,10 @@ function PLAYERRECCE:SetReferencePoint(Coordinate,Name)
|
|||||||
if self.RPMarker then
|
if self.RPMarker then
|
||||||
self.RPMarker:Remove()
|
self.RPMarker:Remove()
|
||||||
end
|
end
|
||||||
local text = string.format("%s RP %s\n%s\n%s\n%s",self.Name,Name,Coordinate:ToStringLLDDM(),Coordinate:ToStringLLDMS(),Coordinate:ToStringMGRS())
|
local llddm = Coordinate:ToStringLLDDM()
|
||||||
|
local lldms = Coordinate:ToStringLLDMS()
|
||||||
|
local mgrs = Coordinate:ToStringMGRS()
|
||||||
|
local text = string.format("%s RP %s\n%s\n%s\n%s",self.Name,Name,llddm,lldms,mgrs)
|
||||||
self.RPMarker = MARKER:New(Coordinate,text)
|
self.RPMarker = MARKER:New(Coordinate,text)
|
||||||
self.RPMarker:ReadOnly()
|
self.RPMarker:ReadOnly()
|
||||||
self.RPMarker:ToCoalition(self.Coalition)
|
self.RPMarker:ToCoalition(self.Coalition)
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ do
|
|||||||
-- @field #table NextTaskSuccess
|
-- @field #table NextTaskSuccess
|
||||||
-- @field #table NextTaskFailure
|
-- @field #table NextTaskFailure
|
||||||
-- @field #string FinalState
|
-- @field #string FinalState
|
||||||
|
-- @field #string TypeName
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ PLAYERTASK = {
|
|||||||
|
|
||||||
--- PLAYERTASK class version.
|
--- PLAYERTASK class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
PLAYERTASK.version="0.1.10"
|
PLAYERTASK.version="0.1.11"
|
||||||
|
|
||||||
--- Generic task condition.
|
--- Generic task condition.
|
||||||
-- @type PLAYERTASK.Condition
|
-- @type PLAYERTASK.Condition
|
||||||
@@ -284,6 +285,14 @@ function PLAYERTASK:GetCoalition()
|
|||||||
return self.coalition
|
return self.coalition
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [User] Get the Ops.Target#TARGET object for this task
|
||||||
|
-- @param #PLAYERTASK self
|
||||||
|
-- @return Ops.Target#TARGET Target
|
||||||
|
function PLAYERTASK:GetTarget()
|
||||||
|
self:T(self.lid.."GetTarget")
|
||||||
|
return self.Target
|
||||||
|
end
|
||||||
|
|
||||||
--- [USER] Add a free text description to this task.
|
--- [USER] Add a free text description to this task.
|
||||||
-- @param #PLAYERTASK self
|
-- @param #PLAYERTASK self
|
||||||
-- @param #string Text
|
-- @param #string Text
|
||||||
@@ -770,7 +779,7 @@ function PLAYERTASK:onafterClientAdded(From, Event, To, Client)
|
|||||||
self:T({From, Event, To})
|
self:T({From, Event, To})
|
||||||
if Client and self.verbose then
|
if Client and self.verbose then
|
||||||
local text = string.format("Player %s joined task %03d!",Client:GetPlayerName() or "Generic",self.PlayerTaskNr)
|
local text = string.format("Player %s joined task %03d!",Client:GetPlayerName() or "Generic",self.PlayerTaskNr)
|
||||||
self:I(self.lid..text)
|
self:T(self.lid..text)
|
||||||
end
|
end
|
||||||
self.timestamp = timer.getAbsTime()
|
self.timestamp = timer.getAbsTime()
|
||||||
return self
|
return self
|
||||||
@@ -925,6 +934,8 @@ do
|
|||||||
-- @field #boolean ShowMagnetic Also show magnetic angles
|
-- @field #boolean ShowMagnetic Also show magnetic angles
|
||||||
-- @field #boolean InfoHasCoordinate
|
-- @field #boolean InfoHasCoordinate
|
||||||
-- @field #boolean InfoHasLLDDM
|
-- @field #boolean InfoHasLLDDM
|
||||||
|
-- @field #table PlayerMenuTag
|
||||||
|
-- @field #boolean UseTypeNames
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1111,6 +1122,13 @@ do
|
|||||||
-- BRIEFING = "Briefing",
|
-- BRIEFING = "Briefing",
|
||||||
-- TARGETLOCATION ="Target location",
|
-- TARGETLOCATION ="Target location",
|
||||||
-- COORDINATE = "Coordinate",
|
-- COORDINATE = "Coordinate",
|
||||||
|
-- INFANTRY = "Infantry",
|
||||||
|
-- TECHNICAL = "Technical",
|
||||||
|
-- ARTILLERY = "Artillery",
|
||||||
|
-- TANKS = "Tanks",
|
||||||
|
-- AIRDEFENSE = "Airdefense",
|
||||||
|
-- SAM = "SAM",
|
||||||
|
-- GROUP = "Group",
|
||||||
-- },
|
-- },
|
||||||
--
|
--
|
||||||
-- e.g.
|
-- e.g.
|
||||||
@@ -1232,6 +1250,7 @@ PLAYERTASKCONTROLLER = {
|
|||||||
PlayerFlashMenu = {},
|
PlayerFlashMenu = {},
|
||||||
PlayerJoinMenu = {},
|
PlayerJoinMenu = {},
|
||||||
PlayerInfoMenu = {},
|
PlayerInfoMenu = {},
|
||||||
|
PlayerMenuTag = {},
|
||||||
noflaresmokemenu = false,
|
noflaresmokemenu = false,
|
||||||
TransmitOnlyWithPlayers = true,
|
TransmitOnlyWithPlayers = true,
|
||||||
buddylasing = false,
|
buddylasing = false,
|
||||||
@@ -1241,6 +1260,7 @@ PLAYERTASKCONTROLLER = {
|
|||||||
ShowMagnetic = true,
|
ShowMagnetic = true,
|
||||||
InfoHasLLDDM = false,
|
InfoHasLLDDM = false,
|
||||||
InfoHasCoordinate = false,
|
InfoHasCoordinate = false,
|
||||||
|
UseTypeNames = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1339,6 +1359,13 @@ PLAYERTASKCONTROLLER.Messages = {
|
|||||||
BRIEFING = "Briefing",
|
BRIEFING = "Briefing",
|
||||||
TARGETLOCATION ="Target location",
|
TARGETLOCATION ="Target location",
|
||||||
COORDINATE = "Coordinate",
|
COORDINATE = "Coordinate",
|
||||||
|
INFANTRY = "Infantry",
|
||||||
|
TECHNICAL = "Technical",
|
||||||
|
ARTILLERY = "Artillery",
|
||||||
|
TANKS = "Tanks",
|
||||||
|
AIRDEFENSE = "Airdefense",
|
||||||
|
SAM = "SAM",
|
||||||
|
GROUP = "Group",
|
||||||
},
|
},
|
||||||
DE = {
|
DE = {
|
||||||
TASKABORT = "Auftrag abgebrochen!",
|
TASKABORT = "Auftrag abgebrochen!",
|
||||||
@@ -1404,19 +1431,26 @@ PLAYERTASKCONTROLLER.Messages = {
|
|||||||
BRIEFING = "Briefing",
|
BRIEFING = "Briefing",
|
||||||
TARGETLOCATION ="Zielposition",
|
TARGETLOCATION ="Zielposition",
|
||||||
COORDINATE = "Koordinate",
|
COORDINATE = "Koordinate",
|
||||||
|
INFANTRY = "Infantrie",
|
||||||
|
TECHNICAL = "Technische",
|
||||||
|
ARTILLERY = "Artillerie",
|
||||||
|
TANKS = "Panzer",
|
||||||
|
AIRDEFENSE = "Flak",
|
||||||
|
SAM = "Luftabwehr",
|
||||||
|
GROUP = "Einheit",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
--- PLAYERTASK class version.
|
--- PLAYERTASK class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
PLAYERTASKCONTROLLER.version="0.1.51"
|
PLAYERTASKCONTROLLER.version="0.1.54"
|
||||||
|
|
||||||
--- Create and run a new TASKCONTROLLER instance.
|
--- Create and run a new TASKCONTROLLER instance.
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @param #string Name Name of this controller
|
-- @param #string Name Name of this controller
|
||||||
-- @param #number Coalition of this controller, e.g. coalition.side.BLUE
|
-- @param #number Coalition of this controller, e.g. coalition.side.BLUE
|
||||||
-- @param #string Type Type of the tasks controlled, defaults to PLAYERTASKCONTROLLER.Type.A2G
|
-- @param #string Type Type of the tasks controlled, defaults to PLAYERTASKCONTROLLER.Type.A2G
|
||||||
-- @param #string ClientFilter (optional) Additional prefix filter for the SET_CLIENT
|
-- @param #string ClientFilter (optional) Additional prefix filter for the SET_CLIENT. Can be handed as @{Core.Set#SET_CLIENT} also.
|
||||||
-- @return #PLAYERTASKCONTROLLER self
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
|
function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
|
||||||
|
|
||||||
@@ -1467,10 +1501,20 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
|
|||||||
self.noflaresmokemenu = false
|
self.noflaresmokemenu = false
|
||||||
|
|
||||||
self.ShowMagnetic = true
|
self.ShowMagnetic = true
|
||||||
|
|
||||||
|
self.UseTypeNames = false
|
||||||
|
|
||||||
|
local IsClientSet = false
|
||||||
|
|
||||||
|
if ClientFilter and type(ClientFilter) == "table" and ClientFilter.ClassName and ClientFilter.ClassName == "SET_CLIENT" then
|
||||||
|
-- we have a predefined SET_CLIENT
|
||||||
|
self.ClientSet = ClientFilter
|
||||||
|
IsClientSet = true
|
||||||
|
end
|
||||||
|
|
||||||
if ClientFilter then
|
if ClientFilter and not IsClientSet then
|
||||||
self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart()
|
self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart()
|
||||||
else
|
elseif not IsClientSet then
|
||||||
self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterStart()
|
self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterStart()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1605,6 +1649,24 @@ function PLAYERTASKCONTROLLER:_InitLocalization()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [User] Show target menu entries of type names for GROUND targets (off by default!), e.g. "Tank Group..."
|
||||||
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
|
function PLAYERTASKCONTROLLER:SetEnableUseTypeNames()
|
||||||
|
self:T(self.lid.."SetEnableUseTypeNames")
|
||||||
|
self.UseTypeNames = true
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [User] Do not show target menu entries of type names for GROUND targets
|
||||||
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
|
function PLAYERTASKCONTROLLER:SetDisableUseTypeNames()
|
||||||
|
self:T(self.lid.."SetDisableUseTypeNames")
|
||||||
|
self.UseTypeNames = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- [User] Set flash directions option for player (player based info)
|
--- [User] Set flash directions option for player (player based info)
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @param #boolean OnOff Set to `true` to switch on and `false` to switch off. Default is OFF.
|
-- @param #boolean OnOff Set to `true` to switch on and `false` to switch off. Default is OFF.
|
||||||
@@ -1961,6 +2023,9 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
|||||||
end
|
end
|
||||||
elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then
|
elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then
|
||||||
if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then
|
if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then
|
||||||
|
if self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then
|
||||||
|
return self
|
||||||
|
end
|
||||||
self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
|
self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
|
||||||
local frequency = self.Frequency
|
local frequency = self.Frequency
|
||||||
local freqtext = ""
|
local freqtext = ""
|
||||||
@@ -2099,7 +2164,7 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType()
|
|||||||
self:T(self.lid.."_GetTasksPerType")
|
self:T(self.lid.."_GetTasksPerType")
|
||||||
local tasktypes = self:_GetAvailableTaskTypes()
|
local tasktypes = self:_GetAvailableTaskTypes()
|
||||||
|
|
||||||
self:T({tasktypes})
|
--self:T({tasktypes})
|
||||||
|
|
||||||
-- Sort tasks per threat level first
|
-- Sort tasks per threat level first
|
||||||
local datatable = self.TaskQueue:GetDataTable()
|
local datatable = self.TaskQueue:GetDataTable()
|
||||||
@@ -2132,7 +2197,7 @@ end
|
|||||||
function PLAYERTASKCONTROLLER:_CheckTargetQueue()
|
function PLAYERTASKCONTROLLER:_CheckTargetQueue()
|
||||||
self:T(self.lid.."_CheckTargetQueue")
|
self:T(self.lid.."_CheckTargetQueue")
|
||||||
if self.TargetQueue:Count() > 0 then
|
if self.TargetQueue:Count() > 0 then
|
||||||
local object = self.TargetQueue:Pull()
|
local object = self.TargetQueue:Pull() -- Wrapper.Positionable#POSITIONABLE
|
||||||
local target = TARGET:New(object)
|
local target = TARGET:New(object)
|
||||||
if object.menuname then
|
if object.menuname then
|
||||||
target.menuname = object.menuname
|
target.menuname = object.menuname
|
||||||
@@ -2140,6 +2205,38 @@ function PLAYERTASKCONTROLLER:_CheckTargetQueue()
|
|||||||
target.freetext = object.freetext
|
target.freetext = object.freetext
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.UseTypeNames and object:IsGround() then
|
||||||
|
-- * Threat level 0: Unit is unarmed.
|
||||||
|
-- * Threat level 1: Unit is infantry.
|
||||||
|
-- * Threat level 2: Unit is an infantry vehicle.
|
||||||
|
-- * Threat level 3: Unit is ground artillery.
|
||||||
|
-- * Threat level 4: Unit is a tank.
|
||||||
|
-- * Threat level 5: Unit is a modern tank or ifv with ATGM.
|
||||||
|
-- * Threat level 6: Unit is a AAA.
|
||||||
|
-- * Threat level 7: Unit is a SAM or manpad, IR guided.
|
||||||
|
-- * Threat level 8: Unit is a Short Range SAM, radar guided.
|
||||||
|
-- * Threat level 9: Unit is a Medium Range SAM, radar guided.
|
||||||
|
-- * Threat level 10: Unit is a Long Range SAM, radar guided.
|
||||||
|
local threat = object:GetThreatLevel()
|
||||||
|
local typekey = "INFANTRY"
|
||||||
|
if threat == 0 or threat == 2 then
|
||||||
|
typekey = "TECHNICAL"
|
||||||
|
elseif threat == 3 then
|
||||||
|
typekey = "ARTILLERY"
|
||||||
|
elseif threat == 4 or threat == 5 then
|
||||||
|
typekey = "TANKS"
|
||||||
|
elseif threat == 6 or threat == 7 then
|
||||||
|
typekey = "AIRDEFENSE"
|
||||||
|
elseif threat >= 8 then
|
||||||
|
typekey = "SAM"
|
||||||
|
end
|
||||||
|
local typename = self.gettext:GetEntry(typekey,self.locale)
|
||||||
|
local gname = self.gettext:GetEntry("GROUP",self.locale)
|
||||||
|
target.TypeName = string.format("%s %s",typename,gname)
|
||||||
|
--self:T(self.lid.."Target TypeName = "..target.TypeName)
|
||||||
|
end
|
||||||
|
|
||||||
self:_AddTask(target)
|
self:_AddTask(target)
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
@@ -2614,6 +2711,7 @@ function PLAYERTASKCONTROLLER:_AddTask(Target)
|
|||||||
end
|
end
|
||||||
|
|
||||||
task.coalition = self.Coalition
|
task.coalition = self.Coalition
|
||||||
|
task.TypeName = Target.TypeName
|
||||||
|
|
||||||
if type == AUFTRAG.Type.BOMBRUNWAY then
|
if type == AUFTRAG.Type.BOMBRUNWAY then
|
||||||
-- task to handle event shot
|
-- task to handle event shot
|
||||||
@@ -2652,6 +2750,7 @@ end
|
|||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @param Ops.PlayerTask#PLAYERTASK PlayerTask
|
-- @param Ops.PlayerTask#PLAYERTASK PlayerTask
|
||||||
-- @param #boolean Silent If true, make no "has new task" announcement
|
-- @param #boolean Silent If true, make no "has new task" announcement
|
||||||
|
-- @param #boolen TaskFilter If true, apply the white/black-list task filters here, also
|
||||||
-- @return #PLAYERTASKCONTROLLER self
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
-- @usage
|
-- @usage
|
||||||
-- Example to create a PLAYERTASK of type CTLD and give Players 10 minutes to complete:
|
-- Example to create a PLAYERTASK of type CTLD and give Players 10 minutes to complete:
|
||||||
@@ -2672,9 +2771,17 @@ end
|
|||||||
-- )
|
-- )
|
||||||
--
|
--
|
||||||
-- taskmanager:AddPlayerTaskToQueue(PlayerTask)
|
-- taskmanager:AddPlayerTaskToQueue(PlayerTask)
|
||||||
function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent)
|
function PLAYERTASKCONTROLLER:AddPlayerTaskToQueue(PlayerTask,Silent,TaskFilter)
|
||||||
self:T(self.lid.."AddPlayerTaskToQueue")
|
self:T(self.lid.."AddPlayerTaskToQueue")
|
||||||
if PlayerTask and PlayerTask.ClassName and PlayerTask.ClassName == "PLAYERTASK" then
|
if PlayerTask and PlayerTask.ClassName and PlayerTask.ClassName == "PLAYERTASK" then
|
||||||
|
if TaskFilter then
|
||||||
|
if self.UseWhiteList and (not self:_CheckTaskTypeAllowed(PlayerTask.Type)) then
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
if self.UseBlackList and self:_CheckTaskTypeDisallowed(PlayerTask.Type) then
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
end
|
||||||
PlayerTask:_SetController(self)
|
PlayerTask:_SetController(self)
|
||||||
PlayerTask:SetCoalition(self.Coalition)
|
PlayerTask:SetCoalition(self.Coalition)
|
||||||
self.TaskQueue:Push(PlayerTask)
|
self.TaskQueue:Push(PlayerTask)
|
||||||
@@ -3120,25 +3227,26 @@ end
|
|||||||
-- @param Core.Menu#MENU_BASE topmenu
|
-- @param Core.Menu#MENU_BASE topmenu
|
||||||
-- @param #table tasktypes
|
-- @param #table tasktypes
|
||||||
-- @param #table taskpertype
|
-- @param #table taskpertype
|
||||||
|
-- @param #string newtag
|
||||||
-- @return #table taskinfomenu
|
-- @return #table taskinfomenu
|
||||||
function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype)
|
function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag)
|
||||||
self:T(self.lid.."_BuildTaskInfoMenu")
|
self:T(self.lid.."_BuildTaskInfoMenu")
|
||||||
local taskinfomenu = nil
|
local taskinfomenu = nil
|
||||||
if self.taskinfomenu then
|
if self.taskinfomenu then
|
||||||
local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale)
|
local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale)
|
||||||
local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu)
|
local taskinfomenu = MENU_GROUP_DELAYED:New(group,menutaskinfo,topmenu):SetTag(newtag)
|
||||||
local ittypes = {}
|
local ittypes = {}
|
||||||
local itaskmenu = {}
|
local itaskmenu = {}
|
||||||
|
local tnow = timer.getTime()
|
||||||
|
|
||||||
for _tasktype,_data in pairs(tasktypes) do
|
for _tasktype,_data in pairs(tasktypes) do
|
||||||
ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu)
|
ittypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,taskinfomenu):SetTag(newtag)
|
||||||
local tasks = taskpertype[_tasktype] or {}
|
local tasks = taskpertype[_tasktype] or {}
|
||||||
local n = 0
|
local n = 0
|
||||||
for _,_task in pairs(tasks) do
|
for _,_task in pairs(tasks) do
|
||||||
_task = _task -- Ops.PlayerTask#PLAYERTASK
|
_task = _task -- Ops.PlayerTask#PLAYERTASK
|
||||||
local pilotcount = _task:CountClients()
|
local pilotcount = _task:CountClients()
|
||||||
local newtext = "]"
|
local newtext = "]"
|
||||||
local tnow = timer.getTime()
|
|
||||||
-- marker for new tasks
|
-- marker for new tasks
|
||||||
if tnow - _task.timestamp < 60 then
|
if tnow - _task.timestamp < 60 then
|
||||||
newtext = "*]"
|
newtext = "*]"
|
||||||
@@ -3151,7 +3259,14 @@ function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu
|
|||||||
text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext)
|
text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task)
|
if self.UseTypeNames then
|
||||||
|
if _task.TypeName then
|
||||||
|
--local name = self.gettext:GetEntry(_task.TypeName,self.locale)
|
||||||
|
text = string.format("%s (%03d) [%d%s",_task.TypeName,_task.PlayerTaskNr,pilotcount,newtext)
|
||||||
|
--self:T(self.lid.."Menu text = "..text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task):SetTag(newtag)
|
||||||
--taskentry:SetTag(playername)
|
--taskentry:SetTag(playername)
|
||||||
itaskmenu[#itaskmenu+1] = taskentry
|
itaskmenu[#itaskmenu+1] = taskentry
|
||||||
-- keep max items limit
|
-- keep max items limit
|
||||||
@@ -3186,11 +3301,16 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
|
|||||||
end
|
end
|
||||||
|
|
||||||
for _,_client in pairs(clients) do
|
for _,_client in pairs(clients) do
|
||||||
if _client then
|
if _client and _client:IsAlive() then
|
||||||
local client = _client -- Wrapper.Client#CLIENT
|
local client = _client -- Wrapper.Client#CLIENT
|
||||||
local group = client:GetGroup()
|
local group = client:GetGroup()
|
||||||
local unknown = self.gettext:GetEntry("UNKNOWN",self.locale)
|
local unknown = self.gettext:GetEntry("UNKNOWN",self.locale)
|
||||||
local playername = client:GetPlayerName() or unknown
|
local playername = client:GetPlayerName() or unknown
|
||||||
|
|
||||||
|
local oldtag = self.PlayerMenuTag[playername]
|
||||||
|
local newtag = playername..timer.getAbsTime()
|
||||||
|
self.PlayerMenuTag[playername] = newtag
|
||||||
|
|
||||||
if group and client then
|
if group and client then
|
||||||
---
|
---
|
||||||
-- TOPMENU
|
-- TOPMENU
|
||||||
@@ -3202,6 +3322,8 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
|
|||||||
|
|
||||||
if self:_CheckPlayerHasTask(playername) and not fromsuccess then playerhastask = true end
|
if self:_CheckPlayerHasTask(playername) and not fromsuccess then playerhastask = true end
|
||||||
local topmenu = nil
|
local topmenu = nil
|
||||||
|
--local oldmenu = nil
|
||||||
|
local rebuilddone = false
|
||||||
|
|
||||||
self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort))
|
self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort))
|
||||||
|
|
||||||
@@ -3215,16 +3337,20 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
|
|||||||
-- 2)+3) Join or abort?
|
-- 2)+3) Join or abort?
|
||||||
if joinorabort then
|
if joinorabort then
|
||||||
self.PlayerMenu[playername]:RemoveSubMenus()
|
self.PlayerMenu[playername]:RemoveSubMenus()
|
||||||
self.PlayerMenu[playername]:SetTag(timer.getAbsTime())
|
self.PlayerMenu[playername]:SetTag(newtag)
|
||||||
topmenu = self.PlayerMenu[playername]
|
topmenu = self.PlayerMenu[playername]
|
||||||
elseif (not playerhastask) or enforced then
|
elseif (not playerhastask) or enforced then
|
||||||
-- 4) last build > 30 secs?
|
-- 4) last build > 30 secs?
|
||||||
local T0 = timer.getAbsTime()
|
local T0 = timer.getAbsTime()
|
||||||
local TDiff = T0-self.PlayerMenu[playername].MenuTag
|
local TDiff = T0-self.PlayerMenu[playername].PTTimeStamp
|
||||||
self:T("TDiff = "..string.format("%.2d",TDiff))
|
self:T("TDiff = "..string.format("%.2d",TDiff))
|
||||||
if TDiff >= self.holdmenutime then
|
if TDiff >= self.holdmenutime then
|
||||||
self.PlayerMenu[playername]:RemoveSubMenus()
|
--self.PlayerMenu[playername]:RemoveSubMenus()
|
||||||
self.PlayerMenu[playername]:SetTag(timer.getAbsTime())
|
--oldmenu = self.PlayerMenu[playername]
|
||||||
|
--self.PlayerMenu[playername] = nil
|
||||||
|
self.PlayerMenu[playername] = MENU_GROUP_DELAYED:New(group,menuname,self.MenuParent)
|
||||||
|
self.PlayerMenu[playername]:SetTag(newtag)
|
||||||
|
self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime()
|
||||||
timedbuild = true
|
timedbuild = true
|
||||||
end
|
end
|
||||||
topmenu = self.PlayerMenu[playername]
|
topmenu = self.PlayerMenu[playername]
|
||||||
@@ -3233,14 +3359,17 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
|
|||||||
-- 1) new player#
|
-- 1) new player#
|
||||||
topmenu = MENU_GROUP_DELAYED:New(group,menuname,self.MenuParent)
|
topmenu = MENU_GROUP_DELAYED:New(group,menuname,self.MenuParent)
|
||||||
self.PlayerMenu[playername] = topmenu
|
self.PlayerMenu[playername] = topmenu
|
||||||
self.PlayerMenu[playername]:SetTag(timer.getAbsTime())
|
self.PlayerMenu[playername]:SetTag(newtag)
|
||||||
|
self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime()
|
||||||
|
enforced = true
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
-- ACTIVE TASK MENU
|
-- ACTIVE TASK MENU
|
||||||
---
|
---
|
||||||
if playerhastask and enforced then
|
if playerhastask and enforced then
|
||||||
--self:T("Building Active Task Menus for "..playername)
|
self:T("Building Active Task Menus for "..playername)
|
||||||
|
rebuilddone = true
|
||||||
local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale)
|
local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale)
|
||||||
local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale)
|
local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale)
|
||||||
local menumark = self.gettext:GetEntry("MENUMARK",self.locale)
|
local menumark = self.gettext:GetEntry("MENUMARK",self.locale)
|
||||||
@@ -3248,44 +3377,45 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
|
|||||||
local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale)
|
local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale)
|
||||||
local menuabort = self.gettext:GetEntry("MENUABORT",self.locale)
|
local menuabort = self.gettext:GetEntry("MENUABORT",self.locale)
|
||||||
|
|
||||||
local active = MENU_GROUP_DELAYED:New(group,menuactive,topmenu)
|
local active = MENU_GROUP_DELAYED:New(group,menuactive,topmenu):SetTag(newtag)
|
||||||
local info = MENU_GROUP_COMMAND_DELAYED:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client)
|
local info = MENU_GROUP_COMMAND_DELAYED:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client):SetTag(newtag)
|
||||||
local mark = MENU_GROUP_COMMAND_DELAYED:New(group,menumark,active,self._MarkTask,self,group,client)
|
local mark = MENU_GROUP_COMMAND_DELAYED:New(group,menumark,active,self._MarkTask,self,group,client):SetTag(newtag)
|
||||||
if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then
|
if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then
|
||||||
if self.noflaresmokemenu ~= true then
|
if self.noflaresmokemenu ~= true then
|
||||||
-- no smoking/flaring here if A2A or designer has set noflaresmokemenu to true
|
-- no smoking/flaring here if A2A or designer has set noflaresmokemenu to true
|
||||||
local smoke = MENU_GROUP_COMMAND_DELAYED:New(group,menusmoke,active,self._SmokeTask,self,group,client)
|
local smoke = MENU_GROUP_COMMAND_DELAYED:New(group,menusmoke,active,self._SmokeTask,self,group,client):SetTag(newtag)
|
||||||
local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client)
|
local flare = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._FlareTask,self,group,client):SetTag(newtag)
|
||||||
local IsNight = client:GetCoordinate():IsNight()
|
local IsNight = client:GetCoordinate():IsNight()
|
||||||
if IsNight then
|
if IsNight then
|
||||||
local light = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._IlluminateTask,self,group,client)
|
local light = MENU_GROUP_COMMAND_DELAYED:New(group,menuflare,active,self._IlluminateTask,self,group,client):SetTag(newtag)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client)
|
local abort = MENU_GROUP_COMMAND_DELAYED:New(group,menuabort,active,self._AbortTask,self,group,client):SetTag(newtag)
|
||||||
if self.activehasinfomenu and self.taskinfomenu then
|
if self.activehasinfomenu and self.taskinfomenu then
|
||||||
--self:T("Building Active-Info Menus for "..playername)
|
self:T("Building Active-Info Menus for "..playername)
|
||||||
local tasktypes = self:_GetAvailableTaskTypes()
|
local tasktypes = self:_GetAvailableTaskTypes()
|
||||||
local taskpertype = self:_GetTasksPerType()
|
local taskpertype = self:_GetTasksPerType()
|
||||||
if self.PlayerInfoMenu[playername] then
|
if self.PlayerInfoMenu[playername] then
|
||||||
self.PlayerInfoMenu[playername]:RemoveSubMenus()
|
self.PlayerInfoMenu[playername]:RemoveSubMenus(nil,oldtag)
|
||||||
end
|
end
|
||||||
self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype)
|
self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag)
|
||||||
end
|
end
|
||||||
elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask and (timedbuild or joinorabort)) then
|
elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask and (timedbuild or joinorabort)) then
|
||||||
--self:T("Building Join Menus for "..playername)
|
self:T("Building Join Menus for "..playername)
|
||||||
|
rebuilddone = true
|
||||||
---
|
---
|
||||||
-- JOIN TASK MENU
|
-- JOIN TASK MENU
|
||||||
---
|
---
|
||||||
local tasktypes = self:_GetAvailableTaskTypes()
|
local tasktypes = self:_GetAvailableTaskTypes()
|
||||||
local taskpertype = self:_GetTasksPerType()
|
local taskpertype = self:_GetTasksPerType()
|
||||||
local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale)
|
local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale)
|
||||||
local joinmenu = MENU_GROUP_DELAYED:New(group,menujoin,topmenu)
|
local joinmenu = MENU_GROUP_DELAYED:New(group,menujoin,topmenu):SetTag(newtag)
|
||||||
|
|
||||||
local ttypes = {}
|
local ttypes = {}
|
||||||
local taskmenu = {}
|
local taskmenu = {}
|
||||||
for _tasktype,_data in pairs(tasktypes) do
|
for _tasktype,_data in pairs(tasktypes) do
|
||||||
ttypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,joinmenu)
|
ttypes[_tasktype] = MENU_GROUP_DELAYED:New(group,_tasktype,joinmenu):SetTag(newtag)
|
||||||
local tasks = taskpertype[_tasktype] or {}
|
local tasks = taskpertype[_tasktype] or {}
|
||||||
local n = 0
|
local n = 0
|
||||||
for _,_task in pairs(tasks) do
|
for _,_task in pairs(tasks) do
|
||||||
@@ -3305,7 +3435,7 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
|
|||||||
text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext)
|
text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task)
|
local taskentry = MENU_GROUP_COMMAND_DELAYED:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task):SetTag(newtag)
|
||||||
--taskentry:SetTag(playername)
|
--taskentry:SetTag(playername)
|
||||||
taskmenu[#taskmenu+1] = taskentry
|
taskmenu[#taskmenu+1] = taskentry
|
||||||
n = n + 1
|
n = n + 1
|
||||||
@@ -3315,25 +3445,30 @@ function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if self.taskinfomenu then
|
if self.taskinfomenu then
|
||||||
--self:T("Building Join-Info Menus for "..playername)
|
self:T("Building Join-Info Menus for "..playername)
|
||||||
if self.PlayerInfoMenu[playername] then
|
if self.PlayerInfoMenu[playername] then
|
||||||
self.PlayerInfoMenu[playername]:RemoveSubMenus()
|
self.PlayerInfoMenu[playername]:RemoveSubMenus(nil,oldtag)
|
||||||
end
|
end
|
||||||
self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype)
|
self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag)
|
||||||
end
|
end
|
||||||
elseif self.TaskQueue:Count() == 0 then
|
end
|
||||||
-- no tasks (yet)
|
if self.AllowFlash then
|
||||||
|
local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale)
|
||||||
|
local flashmenu = MENU_GROUP_COMMAND_DELAYED:New(group,flashtext,topmenu,self._SwitchFlashing,self,group,client):SetTag(newtag)
|
||||||
|
end
|
||||||
|
if self.TaskQueue:Count() == 0 then
|
||||||
|
self:T("No open tasks info")
|
||||||
local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale)
|
local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale)
|
||||||
local joinmenu = MENU_GROUP_DELAYED:New(group,menunotasks,topmenu)
|
local joinmenu = MENU_GROUP_DELAYED:New(group,menunotasks,self.PlayerMenu[playername]):SetTag(newtag)
|
||||||
|
rebuilddone = true
|
||||||
end
|
end
|
||||||
---
|
---
|
||||||
-- REFRESH MENU
|
-- REFRESH MENU
|
||||||
---
|
---
|
||||||
if self.AllowFlash then
|
if rebuilddone then
|
||||||
local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale)
|
self.PlayerMenu[playername]:RemoveSubMenus(nil,oldtag)
|
||||||
local flashmenu = MENU_GROUP_COMMAND_DELAYED:New(group,flashtext,self.PlayerMenu[playername],self._SwitchFlashing,self,group,client)
|
self.PlayerMenu[playername]:Refresh()
|
||||||
end
|
end
|
||||||
self.PlayerMenu[playername]:Set()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -3652,7 +3787,7 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To)
|
|||||||
self:_BuildMenus(nil,enforcedmenu)
|
self:_BuildMenus(nil,enforcedmenu)
|
||||||
|
|
||||||
if self.verbose then
|
if self.verbose then
|
||||||
local text = string.format("New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",targetcount,taskcount,playercount,assignedtasks)
|
local text = string.format("%s | New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",self.MenuName, targetcount,taskcount,playercount,assignedtasks)
|
||||||
self:I(text)
|
self:I(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -625,7 +625,7 @@ function TARGET:onafterStatus(From, Event, To)
|
|||||||
-- Log output verbose=1.
|
-- Log output verbose=1.
|
||||||
if self.verbose>=1 then
|
if self.verbose>=1 then
|
||||||
local text=string.format("%s: Targets=%d/%d Life=%.1f/%.1f Damage=%.1f", fsmstate, self:CountTargets(), self.N0, self:GetLife(), self:GetLife0(), self:GetDamage())
|
local text=string.format("%s: Targets=%d/%d Life=%.1f/%.1f Damage=%.1f", fsmstate, self:CountTargets(), self.N0, self:GetLife(), self:GetLife0(), self:GetDamage())
|
||||||
if self:CountTargets() == 0 then
|
if self:CountTargets() == 0 or self:GetDamage() >= 100 then
|
||||||
text=text.." Dead!"
|
text=text.." Dead!"
|
||||||
elseif damaged then
|
elseif damaged then
|
||||||
text=text.." Damaged!"
|
text=text.." Damaged!"
|
||||||
@@ -644,7 +644,7 @@ function TARGET:onafterStatus(From, Event, To)
|
|||||||
self:I(self.lid..text)
|
self:I(self.lid..text)
|
||||||
end
|
end
|
||||||
|
|
||||||
if self:CountTargets() == 0 then
|
if self:CountTargets() == 0 or self:GetDamage() >= 100 then
|
||||||
self:Dead()
|
self:Dead()
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -943,6 +943,9 @@ function TARGET:_AddObject(Object)
|
|||||||
target.Coordinate=scenery:GetCoordinate()
|
target.Coordinate=scenery:GetCoordinate()
|
||||||
|
|
||||||
target.Life0=scenery:GetLife0()
|
target.Life0=scenery:GetLife0()
|
||||||
|
|
||||||
|
if target.Life0==0 then target.Life0 = 1 end
|
||||||
|
|
||||||
target.Life=scenery:GetLife()
|
target.Life=scenery:GetLife()
|
||||||
|
|
||||||
target.N0=target.N0+1
|
target.N0=target.N0+1
|
||||||
@@ -1095,7 +1098,9 @@ function TARGET:GetTargetLife(Target)
|
|||||||
elseif Target.Type==TARGET.ObjectType.STATIC then
|
elseif Target.Type==TARGET.ObjectType.STATIC then
|
||||||
|
|
||||||
if Target.Object and Target.Object:IsAlive() then
|
if Target.Object and Target.Object:IsAlive() then
|
||||||
return 1
|
local life=Target.Object:GetLife()
|
||||||
|
return life
|
||||||
|
--return 1
|
||||||
else
|
else
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2201,10 +2201,29 @@ function UTILS.CheckFileExists(Path,Filename)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Function to obtain a table of typenames from the group given with the number of units of the same type in the group.
|
||||||
|
-- @param Wrapper.Group#GROUP Group The group to list
|
||||||
|
-- @return #table Table of typnames and typename counts, e.g. `{["KAMAZ Truck"]=3,["ATZ-5"]=1}`
|
||||||
|
function UTILS.GetCountPerTypeName(Group)
|
||||||
|
local units = Group:GetUnits()
|
||||||
|
local TypeNameTable = {}
|
||||||
|
for _,_unt in pairs (units) do
|
||||||
|
local unit = _unt -- Wrapper.Unit#UNIT
|
||||||
|
local typen = unit:GetTypeName()
|
||||||
|
if not TypeNameTable[typen] then
|
||||||
|
TypeNameTable[typen] = 1
|
||||||
|
else
|
||||||
|
TypeNameTable[typen] = TypeNameTable[typen] + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return TypeNameTable
|
||||||
|
end
|
||||||
|
|
||||||
--- Function to save the state of a list of groups found by name
|
--- Function to save the state of a list of groups found by name
|
||||||
-- @param #table List Table of strings with groupnames
|
-- @param #table List Table of strings with groupnames
|
||||||
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
-- @param #string Filename The name of the file.
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @param #boolean Structured Append the data with a list of typenames in the group plus their count.
|
||||||
-- @return #boolean outcome True if saving is successful, else false.
|
-- @return #boolean outcome True if saving is successful, else false.
|
||||||
-- @usage
|
-- @usage
|
||||||
-- We will go through the list and find the corresponding group and save the current group size (0 when dead).
|
-- We will go through the list and find the corresponding group and save the current group size (0 when dead).
|
||||||
@@ -2212,7 +2231,7 @@ end
|
|||||||
-- Position is still saved for your usage.
|
-- Position is still saved for your usage.
|
||||||
-- The idea is to reduce the number of units when reloading the data again to restart the saved mission.
|
-- The idea is to reduce the number of units when reloading the data again to restart the saved mission.
|
||||||
-- The data will be a simple comma separated list of groupname and size, with one header line.
|
-- The data will be a simple comma separated list of groupname and size, with one header line.
|
||||||
function UTILS.SaveStationaryListOfGroups(List,Path,Filename)
|
function UTILS.SaveStationaryListOfGroups(List,Path,Filename,Structured)
|
||||||
local filename = Filename or "StateListofGroups"
|
local filename = Filename or "StateListofGroups"
|
||||||
local data = "--Save Stationary List of Groups: "..Filename .."\n"
|
local data = "--Save Stationary List of Groups: "..Filename .."\n"
|
||||||
for _,_group in pairs (List) do
|
for _,_group in pairs (List) do
|
||||||
@@ -2220,7 +2239,16 @@ function UTILS.SaveStationaryListOfGroups(List,Path,Filename)
|
|||||||
if group and group:IsAlive() then
|
if group and group:IsAlive() then
|
||||||
local units = group:CountAliveUnits()
|
local units = group:CountAliveUnits()
|
||||||
local position = group:GetVec3()
|
local position = group:GetVec3()
|
||||||
data = string.format("%s%s,%d,%d,%d,%d\n",data,_group,units,position.x,position.y,position.z)
|
if Structured then
|
||||||
|
local structure = UTILS.GetCountPerTypeName(group)
|
||||||
|
local strucdata = ""
|
||||||
|
for typen,anzahl in pairs (structure) do
|
||||||
|
strucdata = strucdata .. typen .. "=="..anzahl..";"
|
||||||
|
end
|
||||||
|
data = string.format("%s%s,%d,%d,%d,%d,%s\n",data,_group,units,position.x,position.y,position.z,strucdata)
|
||||||
|
else
|
||||||
|
data = string.format("%s%s,%d,%d,%d,%d\n",data,_group,units,position.x,position.y,position.z)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
data = string.format("%s%s,0,0,0,0\n",data,_group)
|
data = string.format("%s%s,0,0,0,0\n",data,_group)
|
||||||
end
|
end
|
||||||
@@ -2234,6 +2262,7 @@ end
|
|||||||
-- @param Core.Set#SET_BASE Set of objects to save
|
-- @param Core.Set#SET_BASE Set of objects to save
|
||||||
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
-- @param #string Filename The name of the file.
|
-- @param #string Filename The name of the file.
|
||||||
|
-- @param #boolean Structured Append the data with a list of typenames in the group plus their count.
|
||||||
-- @return #boolean outcome True if saving is successful, else false.
|
-- @return #boolean outcome True if saving is successful, else false.
|
||||||
-- @usage
|
-- @usage
|
||||||
-- We will go through the set and find the corresponding group and save the current group size and current position.
|
-- We will go through the set and find the corresponding group and save the current group size and current position.
|
||||||
@@ -2243,7 +2272,7 @@ end
|
|||||||
-- **Note** Do NOT use dashes or hashes in group template names (-,#)!
|
-- **Note** Do NOT use dashes or hashes in group template names (-,#)!
|
||||||
-- The data will be a simple comma separated list of groupname and size, with one header line.
|
-- The data will be a simple comma separated list of groupname and size, with one header line.
|
||||||
-- The current task/waypoint/etc cannot be restored.
|
-- The current task/waypoint/etc cannot be restored.
|
||||||
function UTILS.SaveSetOfGroups(Set,Path,Filename)
|
function UTILS.SaveSetOfGroups(Set,Path,Filename,Structured)
|
||||||
local filename = Filename or "SetOfGroups"
|
local filename = Filename or "SetOfGroups"
|
||||||
local data = "--Save SET of groups: "..Filename .."\n"
|
local data = "--Save SET of groups: "..Filename .."\n"
|
||||||
local List = Set:GetSetObjects()
|
local List = Set:GetSetObjects()
|
||||||
@@ -2257,7 +2286,16 @@ function UTILS.SaveSetOfGroups(Set,Path,Filename)
|
|||||||
end
|
end
|
||||||
local units = group:CountAliveUnits()
|
local units = group:CountAliveUnits()
|
||||||
local position = group:GetVec3()
|
local position = group:GetVec3()
|
||||||
data = string.format("%s%s,%s,%d,%d,%d,%d\n",data,name,template,units,position.x,position.y,position.z)
|
if Structured then
|
||||||
|
local structure = UTILS.GetCountPerTypeName(group)
|
||||||
|
local strucdata = ""
|
||||||
|
for typen,anzahl in pairs (structure) do
|
||||||
|
strucdata = strucdata .. typen .. "=="..anzahl..";"
|
||||||
|
end
|
||||||
|
data = string.format("%s%s,%s,%d,%d,%d,%d,%s\n",data,name,template,units,position.x,position.y,position.z,strucdata)
|
||||||
|
else
|
||||||
|
data = string.format("%s%s,%s,%d,%d,%d,%d\n",data,name,template,units,position.x,position.y,position.z)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- save the data
|
-- save the data
|
||||||
@@ -2321,8 +2359,41 @@ end
|
|||||||
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
-- @param #string Filename The name of the file.
|
-- @param #string Filename The name of the file.
|
||||||
-- @param #boolean Reduce If false, existing loaded groups will not be reduced to fit the saved number.
|
-- @param #boolean Reduce If false, existing loaded groups will not be reduced to fit the saved number.
|
||||||
|
-- @param #boolean Structured (Optional, needs Reduce = true) If true, and the data has been saved as structure before, remove the correct unit types as per the saved list.
|
||||||
|
-- @param #boolean Cinematic (Optional, needs Structured = true) If true, place a fire/smoke effect on the dead static position.
|
||||||
|
-- @param #number Effect (Optional for Cinematic) What effect to use. Defaults to a random effect. Smoke presets are: 1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke.
|
||||||
|
-- @param #number Density (Optional for Cinematic) What smoke density to use, can be 0 to 1. Defaults to 0.5.
|
||||||
-- @return #table Table of data objects (tables) containing groupname, coordinate and group object. Returns nil when file cannot be read.
|
-- @return #table Table of data objects (tables) containing groupname, coordinate and group object. Returns nil when file cannot be read.
|
||||||
function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce)
|
-- @return #table When using Cinematic: table of names of smoke and fire objects, so they can be extinguished with `COORDINATE.StopBigSmokeAndFire( name )`
|
||||||
|
function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce,Structured,Cinematic,Effect,Density)
|
||||||
|
|
||||||
|
local fires = {}
|
||||||
|
|
||||||
|
local function Smokers(name,coord,effect,density)
|
||||||
|
local eff = math.random(8)
|
||||||
|
if type(effect) == "number" then eff = effect end
|
||||||
|
coord:BigSmokeAndFire(eff,density,name)
|
||||||
|
table.insert(fires,name)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Cruncher(group,typename,anzahl)
|
||||||
|
local units = group:GetUnits()
|
||||||
|
local reduced = 0
|
||||||
|
for _,_unit in pairs (units) do
|
||||||
|
local typo = _unit:GetTypeName()
|
||||||
|
if typename == typo then
|
||||||
|
if Cinematic then
|
||||||
|
local coordinate = _unit:GetCoordinate()
|
||||||
|
local name = _unit:GetName()
|
||||||
|
Smokers(name,coordinate,Effect,Density)
|
||||||
|
end
|
||||||
|
_unit:Destroy(false)
|
||||||
|
reduced = reduced + 1
|
||||||
|
if reduced == anzahl then break end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local reduce = true
|
local reduce = true
|
||||||
if Reduce == false then reduce = false end
|
if Reduce == false then reduce = false end
|
||||||
local filename = Filename or "StateListofGroups"
|
local filename = Filename or "StateListofGroups"
|
||||||
@@ -2339,18 +2410,48 @@ function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce)
|
|||||||
local posx = tonumber(dataset[3])
|
local posx = tonumber(dataset[3])
|
||||||
local posy = tonumber(dataset[4])
|
local posy = tonumber(dataset[4])
|
||||||
local posz = tonumber(dataset[5])
|
local posz = tonumber(dataset[5])
|
||||||
|
local structure = dataset[6]
|
||||||
|
--BASE:I({structure})
|
||||||
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
||||||
local data = { groupname=groupname, size=size, coordinate=coordinate, group=GROUP:FindByName(groupname) }
|
local data = { groupname=groupname, size=size, coordinate=coordinate, group=GROUP:FindByName(groupname) }
|
||||||
if reduce then
|
if reduce then
|
||||||
local actualgroup = GROUP:FindByName(groupname)
|
local actualgroup = GROUP:FindByName(groupname)
|
||||||
if actualgroup and actualgroup:IsAlive() and actualgroup:CountAliveUnits() > size then
|
if actualgroup and actualgroup:IsAlive() and actualgroup:CountAliveUnits() > size then
|
||||||
local reduction = actualgroup:CountAliveUnits() - size
|
if Structured and structure then
|
||||||
BASE:I("Reducing groupsize by ".. reduction .. " units!")
|
--BASE:I("Reducing group structure!")
|
||||||
-- reduce existing group
|
local loadedstructure = {}
|
||||||
local units = actualgroup:GetUnits()
|
local strcset = UTILS.Split(structure,";")
|
||||||
local units2 = UTILS.ShuffleTable(units) -- randomize table
|
for _,_data in pairs(strcset) do
|
||||||
for i=1,reduction do
|
local datasplit = UTILS.Split(_data,"==")
|
||||||
units2[i]:Destroy(false)
|
loadedstructure[datasplit[1]] = tonumber(datasplit[2])
|
||||||
|
end
|
||||||
|
--BASE:I({loadedstructure})
|
||||||
|
local originalstructure = UTILS.GetCountPerTypeName(actualgroup)
|
||||||
|
--BASE:I({originalstructure})
|
||||||
|
for _name,_number in pairs(originalstructure) do
|
||||||
|
local loadednumber = 0
|
||||||
|
if loadedstructure[_name] then
|
||||||
|
loadednumber = loadedstructure[_name]
|
||||||
|
end
|
||||||
|
local reduce = false
|
||||||
|
if loadednumber < _number then reduce = true end
|
||||||
|
|
||||||
|
--BASE:I(string.format("Looking at: %s | Original number: %d | Loaded number: %d | Reduce: %s",_name,_number,loadednumber,tostring(reduce)))
|
||||||
|
|
||||||
|
if reduce then
|
||||||
|
Cruncher(actualgroup,_name,_number-loadednumber)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local reduction = actualgroup:CountAliveUnits() - size
|
||||||
|
--BASE:I("Reducing groupsize by ".. reduction .. " units!")
|
||||||
|
-- reduce existing group
|
||||||
|
local units = actualgroup:GetUnits()
|
||||||
|
local units2 = UTILS.ShuffleTable(units) -- randomize table
|
||||||
|
for i=1,reduction do
|
||||||
|
units2[i]:Destroy(false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2359,19 +2460,52 @@ function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce)
|
|||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return datatable
|
return datatable,fires
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Load back a SET of groups from file.
|
--- Load back a SET of groups from file.
|
||||||
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
-- @param #string Filename The name of the file.
|
-- @param #string Filename The name of the file.
|
||||||
-- @param #boolean Spawn If set to false, do not re-spawn the groups loaded in location and reduce to size.
|
-- @param #boolean Spawn If set to false, do not re-spawn the groups loaded in location and reduce to size.
|
||||||
|
-- @param #boolean Structured (Optional, needs Spawn=true)If true, and the data has been saved as structure before, remove the correct unit types as per the saved list.
|
||||||
|
-- @param #boolean Cinematic (Optional, needs Structured=true) If true, place a fire/smoke effect on the dead static position.
|
||||||
|
-- @param #number Effect (Optional for Cinematic) What effect to use. Defaults to a random effect. Smoke presets are: 1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke.
|
||||||
|
-- @param #number Density (Optional for Cinematic) What smoke density to use, can be 0 to 1. Defaults to 0.5.
|
||||||
-- @return Core.Set#SET_GROUP Set of GROUP objects.
|
-- @return Core.Set#SET_GROUP Set of GROUP objects.
|
||||||
-- Returns nil when file cannot be read. Returns a table of data entries if Spawn is false: `{ groupname=groupname, size=size, coordinate=coordinate, template=template }`
|
-- Returns nil when file cannot be read. Returns a table of data entries if Spawn is false: `{ groupname=groupname, size=size, coordinate=coordinate, template=template }`
|
||||||
function UTILS.LoadSetOfGroups(Path,Filename,Spawn)
|
-- @return #table When using Cinematic: table of names of smoke and fire objects, so they can be extinguished with `COORDINATE.StopBigSmokeAndFire( name )`
|
||||||
|
function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,Density)
|
||||||
|
|
||||||
|
local fires = {}
|
||||||
|
|
||||||
|
local function Smokers(name,coord,effect,density)
|
||||||
|
local eff = math.random(8)
|
||||||
|
if type(effect) == "number" then eff = effect end
|
||||||
|
coord:BigSmokeAndFire(eff,density,name)
|
||||||
|
table.insert(fires,name)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function Cruncher(group,typename,anzahl)
|
||||||
|
local units = group:GetUnits()
|
||||||
|
local reduced = 0
|
||||||
|
for _,_unit in pairs (units) do
|
||||||
|
local typo = _unit:GetTypeName()
|
||||||
|
if typename == typo then
|
||||||
|
if Cinematic then
|
||||||
|
local coordinate = _unit:GetCoordinate()
|
||||||
|
local name = _unit:GetName()
|
||||||
|
Smokers(name,coordinate,Effect,Density)
|
||||||
|
end
|
||||||
|
_unit:Destroy(false)
|
||||||
|
reduced = reduced + 1
|
||||||
|
if reduced == anzahl then break end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local spawn = true
|
local spawn = true
|
||||||
if Spawn == false then spawn = false end
|
if Spawn == false then spawn = false end
|
||||||
BASE:I("Spawn = "..tostring(spawn))
|
--BASE:I("Spawn = "..tostring(spawn))
|
||||||
local filename = Filename or "SetOfGroups"
|
local filename = Filename or "SetOfGroups"
|
||||||
local setdata = SET_GROUP:New()
|
local setdata = SET_GROUP:New()
|
||||||
local datatable = {}
|
local datatable = {}
|
||||||
@@ -2388,6 +2522,7 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn)
|
|||||||
local posx = tonumber(dataset[4])
|
local posx = tonumber(dataset[4])
|
||||||
local posy = tonumber(dataset[5])
|
local posy = tonumber(dataset[5])
|
||||||
local posz = tonumber(dataset[6])
|
local posz = tonumber(dataset[6])
|
||||||
|
local structure = dataset[7]
|
||||||
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
||||||
local group=nil
|
local group=nil
|
||||||
local data = { groupname=groupname, size=size, coordinate=coordinate, template=template }
|
local data = { groupname=groupname, size=size, coordinate=coordinate, template=template }
|
||||||
@@ -2400,12 +2535,40 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn)
|
|||||||
setdata:AddObject(spwndgrp)
|
setdata:AddObject(spwndgrp)
|
||||||
local actualsize = spwndgrp:CountAliveUnits()
|
local actualsize = spwndgrp:CountAliveUnits()
|
||||||
if actualsize > size then
|
if actualsize > size then
|
||||||
local reduction = actualsize-size
|
if Structured and structure then
|
||||||
-- reduce existing group
|
--BASE:I("Reducing group structure!")
|
||||||
local units = spwndgrp:GetUnits()
|
local loadedstructure = {}
|
||||||
local units2 = UTILS.ShuffleTable(units) -- randomize table
|
local strcset = UTILS.Split(structure,";")
|
||||||
for i=1,reduction do
|
for _,_data in pairs(strcset) do
|
||||||
units2[i]:Destroy(false)
|
local datasplit = UTILS.Split(_data,"==")
|
||||||
|
loadedstructure[datasplit[1]] = tonumber(datasplit[2])
|
||||||
|
end
|
||||||
|
--BASE:I({loadedstructure})
|
||||||
|
local originalstructure = UTILS.GetCountPerTypeName(spwndgrp)
|
||||||
|
--BASE:I({originalstructure})
|
||||||
|
for _name,_number in pairs(originalstructure) do
|
||||||
|
local loadednumber = 0
|
||||||
|
if loadedstructure[_name] then
|
||||||
|
loadednumber = loadedstructure[_name]
|
||||||
|
end
|
||||||
|
local reduce = false
|
||||||
|
if loadednumber < _number then reduce = true end
|
||||||
|
|
||||||
|
--BASE:I(string.format("Looking at: %s | Original number: %d | Loaded number: %d | Reduce: %s",_name,_number,loadednumber,tostring(reduce)))
|
||||||
|
|
||||||
|
if reduce then
|
||||||
|
Cruncher(spwndgrp,_name,_number-loadednumber)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local reduction = actualsize-size
|
||||||
|
-- reduce existing group
|
||||||
|
local units = spwndgrp:GetUnits()
|
||||||
|
local units2 = UTILS.ShuffleTable(units) -- randomize table
|
||||||
|
for i=1,reduction do
|
||||||
|
units2[i]:Destroy(false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2417,7 +2580,7 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
if spawn then
|
if spawn then
|
||||||
return setdata
|
return setdata,fires
|
||||||
else
|
else
|
||||||
return datatable
|
return datatable
|
||||||
end
|
end
|
||||||
@@ -2436,13 +2599,11 @@ function UTILS.LoadSetOfStatics(Path,Filename)
|
|||||||
table.remove(loadeddata, 1)
|
table.remove(loadeddata, 1)
|
||||||
for _id,_entry in pairs (loadeddata) do
|
for _id,_entry in pairs (loadeddata) do
|
||||||
local dataset = UTILS.Split(_entry,",")
|
local dataset = UTILS.Split(_entry,",")
|
||||||
-- staticname,position.x,position.y,position.z
|
|
||||||
local staticname = dataset[1]
|
local staticname = dataset[1]
|
||||||
local posx = tonumber(dataset[2])
|
local StaticObject = STATIC:FindByName(staticname,false)
|
||||||
local posy = tonumber(dataset[3])
|
if StaticObject then
|
||||||
local posz = tonumber(dataset[4])
|
datatable:AddObject(StaticObject)
|
||||||
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
|
end
|
||||||
datatable:AddObject(STATIC:FindByName(staticname,false))
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
@@ -2454,9 +2615,15 @@ end
|
|||||||
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
|
||||||
-- @param #string Filename The name of the file.
|
-- @param #string Filename The name of the file.
|
||||||
-- @param #boolean Reduce If false, do not destroy the units with size=0.
|
-- @param #boolean Reduce If false, do not destroy the units with size=0.
|
||||||
-- @return #table Table of data objects (tables) containing staticname, size (0=dead else 1), coordinate and the static object.
|
-- @param #boolean Dead (Optional, needs Reduce = true) If Dead is true, re-spawn the dead object as dead and do not just delete it.
|
||||||
|
-- @param #boolean Cinematic (Optional, needs Dead = true) If true, place a fire/smoke effect on the dead static position.
|
||||||
|
-- @param #number Effect (Optional for Cinematic) What effect to use. Defaults to a random effect. Smoke presets are: 1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke.
|
||||||
|
-- @param #number Density (Optional for Cinematic) What smoke density to use, can be 0 to 1. Defaults to 0.5.
|
||||||
|
-- @return #table Table of data objects (tables) containing staticname, size (0=dead else 1), coordinate and the static object. Dead objects will have coordinate points `{x=0,y=0,z=0}`
|
||||||
|
-- @return #table When using Cinematic: table of names of smoke and fire objects, so they can be extinguished with `COORDINATE.StopBigSmokeAndFire( name )`
|
||||||
-- Returns nil when file cannot be read.
|
-- Returns nil when file cannot be read.
|
||||||
function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce)
|
function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce,Dead,Cinematic,Effect,Density)
|
||||||
|
local fires = {}
|
||||||
local reduce = true
|
local reduce = true
|
||||||
if Reduce == false then reduce = false end
|
if Reduce == false then reduce = false end
|
||||||
local filename = Filename or "StateListofStatics"
|
local filename = Filename or "StateListofStatics"
|
||||||
@@ -2479,14 +2646,31 @@ function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce)
|
|||||||
if size==0 and reduce then
|
if size==0 and reduce then
|
||||||
local static = STATIC:FindByName(staticname,false)
|
local static = STATIC:FindByName(staticname,false)
|
||||||
if static then
|
if static then
|
||||||
static:Destroy(false)
|
if Dead then
|
||||||
|
local deadobject = SPAWNSTATIC:NewFromStatic(staticname,static:GetCountry())
|
||||||
|
deadobject:InitDead(true)
|
||||||
|
local heading = static:GetHeading()
|
||||||
|
local coord = static:GetCoordinate()
|
||||||
|
static:Destroy(false)
|
||||||
|
deadobject:SpawnFromCoordinate(coord,heading,staticname)
|
||||||
|
if Cinematic then
|
||||||
|
local effect = math.random(8)
|
||||||
|
if type(Effect) == "number" then
|
||||||
|
effect = Effect
|
||||||
|
end
|
||||||
|
coord:BigSmokeAndFire(effect,Density,staticname)
|
||||||
|
table.insert(fires,staticname)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
static:Destroy(false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return datatable
|
return datatable,fires
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Heading Degrees (0-360) to Cardinal
|
--- Heading Degrees (0-360) to Cardinal
|
||||||
|
|||||||
@@ -510,6 +510,7 @@ AIRBASE.MarianaIslands = {
|
|||||||
-- * AIRBASE.SouthAtlantic.Porvenir_Airfield
|
-- * AIRBASE.SouthAtlantic.Porvenir_Airfield
|
||||||
-- * AIRBASE.SouthAtlantic.Almirante_Schroeders
|
-- * AIRBASE.SouthAtlantic.Almirante_Schroeders
|
||||||
-- * AIRBASE.SouthAtlantic.Rio_Turbio
|
-- * AIRBASE.SouthAtlantic.Rio_Turbio
|
||||||
|
-- * AIRBASE.SouthAtlantic.Rio_Chico_Airfield
|
||||||
--
|
--
|
||||||
--@field MarianaIslands
|
--@field MarianaIslands
|
||||||
AIRBASE.SouthAtlantic={
|
AIRBASE.SouthAtlantic={
|
||||||
@@ -532,6 +533,7 @@ AIRBASE.SouthAtlantic={
|
|||||||
["Porvenir_Airfield"]="Porvenir Airfield",
|
["Porvenir_Airfield"]="Porvenir Airfield",
|
||||||
["Almirante_Schroeders"]="Almirante Schroeders",
|
["Almirante_Schroeders"]="Almirante Schroeders",
|
||||||
["Rio_Turbio"]="Rio Turbio",
|
["Rio_Turbio"]="Rio Turbio",
|
||||||
|
["Rio_Chico"] = "Rio Chico",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||||
@@ -839,7 +841,7 @@ end
|
|||||||
-- Black listed spots overrule white listed spots.
|
-- Black listed spots overrule white listed spots.
|
||||||
-- **NOTE** that terminal IDs are not necessarily the same as those displayed in the mission editor!
|
-- **NOTE** that terminal IDs are not necessarily the same as those displayed in the mission editor!
|
||||||
-- @param #AIRBASE self
|
-- @param #AIRBASE self
|
||||||
-- @param #table TerminalIdBlacklist Table of white listed terminal IDs.
|
-- @param #table TerminalIdWhitelist Table of white listed terminal IDs.
|
||||||
-- @return #AIRBASE self
|
-- @return #AIRBASE self
|
||||||
-- @usage AIRBASE:FindByName("Batumi"):SetParkingSpotWhitelist({2, 3, 4}) --Only allow terminal IDs 2, 3, 4
|
-- @usage AIRBASE:FindByName("Batumi"):SetParkingSpotWhitelist({2, 3, 4}) --Only allow terminal IDs 2, 3, 4
|
||||||
function AIRBASE:SetParkingSpotWhitelist(TerminalIdWhitelist)
|
function AIRBASE:SetParkingSpotWhitelist(TerminalIdWhitelist)
|
||||||
|
|||||||
@@ -630,21 +630,23 @@ end
|
|||||||
-- @param Wrapper.Unit#UNIT self
|
-- @param Wrapper.Unit#UNIT self
|
||||||
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
||||||
function UNIT:GetGroup()
|
function UNIT:GetGroup()
|
||||||
self:F2( self.UnitName )
|
self:F2( self.UnitName )
|
||||||
|
local UnitGroup = GROUP:FindByName(self.GroupName)
|
||||||
local DCSUnit = self:GetDCSObject()
|
if UnitGroup then
|
||||||
|
|
||||||
if DCSUnit then
|
|
||||||
local UnitGroup = GROUP:FindByName( DCSUnit:getGroup():getName() )
|
|
||||||
return UnitGroup
|
return UnitGroup
|
||||||
|
else
|
||||||
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
if DCSUnit then
|
||||||
|
local grp = DCSUnit:getGroup()
|
||||||
|
if grp then
|
||||||
|
local UnitGroup = GROUP:FindByName( grp:getName() )
|
||||||
|
return UnitGroup
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Need to add here functions to check if radar is on and which object etc.
|
|
||||||
|
|
||||||
--- Returns the prefix name of the DCS Unit. A prefix name is a part of the name before a '#'-sign.
|
--- Returns the prefix name of the DCS Unit. A prefix name is a part of the name before a '#'-sign.
|
||||||
-- DCS Units spawned with the @{Core.Spawn#SPAWN} class contain a '#'-sign to indicate the end of the (base) DCS Unit name.
|
-- DCS Units spawned with the @{Core.Spawn#SPAWN} class contain a '#'-sign to indicate the end of the (base) DCS Unit name.
|
||||||
-- The spawn sequence number and unit number are contained within the name after the '#' sign.
|
-- The spawn sequence number and unit number are contained within the name after the '#' sign.
|
||||||
|
|||||||
Reference in New Issue
Block a user