mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'FF/Ops' into FF/OpsDev
This commit is contained in:
commit
fa8e1d2a56
1
.github/workflows/build-includes.yml
vendored
1
.github/workflows/build-includes.yml
vendored
@ -47,6 +47,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Update apt-get (needed for act docker image)
|
- name: Update apt-get (needed for act docker image)
|
||||||
run: |
|
run: |
|
||||||
|
sudo rm /etc/apt/sources.list.d/microsoft-prod.list
|
||||||
sudo apt-get -qq update
|
sudo apt-get -qq update
|
||||||
|
|
||||||
- name: Install tree
|
- name: Install tree
|
||||||
|
|||||||
@ -38,11 +38,13 @@
|
|||||||
-- @type BEACON
|
-- @type BEACON
|
||||||
-- @field #string ClassName Name of the class "BEACON".
|
-- @field #string ClassName Name of the class "BEACON".
|
||||||
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{Wrapper.Controllable#CONTROLLABLE} that will receive radio capabilities.
|
-- @field Wrapper.Controllable#CONTROLLABLE Positionable The @{Wrapper.Controllable#CONTROLLABLE} that will receive radio capabilities.
|
||||||
|
-- @field #number UniqueName Counter to make the unique naming work.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
BEACON = {
|
BEACON = {
|
||||||
ClassName = "BEACON",
|
ClassName = "BEACON",
|
||||||
Positionable = nil,
|
Positionable = nil,
|
||||||
name = nil,
|
name = nil,
|
||||||
|
UniqueName = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Beacon types supported by DCS.
|
--- Beacon types supported by DCS.
|
||||||
@ -384,7 +386,9 @@ end
|
|||||||
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
|
function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDuration)
|
||||||
self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
|
self:F({FileName, Frequency, Modulation, Power, BeaconDuration})
|
||||||
local IsValid = false
|
local IsValid = false
|
||||||
|
|
||||||
|
Modulation = Modulation or radio.modulation.AM
|
||||||
|
|
||||||
-- Check the filename
|
-- Check the filename
|
||||||
if type(FileName) == "string" then
|
if type(FileName) == "string" then
|
||||||
if FileName:find(".ogg") or FileName:find(".wav") then
|
if FileName:find(".ogg") or FileName:find(".wav") then
|
||||||
@ -395,7 +399,7 @@ function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDurati
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not IsValid then
|
if not IsValid then
|
||||||
self:E({"File name invalid. Maybe something wrong with the extension ? ", FileName})
|
self:E({"File name invalid. Maybe something wrong with the extension? ", FileName})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check the Frequency
|
-- Check the Frequency
|
||||||
@ -421,7 +425,9 @@ function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDurati
|
|||||||
if IsValid then
|
if IsValid then
|
||||||
self:T2({"Activating Beacon on ", Frequency, Modulation})
|
self:T2({"Activating Beacon on ", Frequency, Modulation})
|
||||||
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
|
-- Note that this is looped. I have to give this transmission a unique name, I use the class ID
|
||||||
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, tostring(self.ID))
|
BEACON.UniqueName = BEACON.UniqueName + 1
|
||||||
|
self.BeaconName = "MooseBeacon"..tostring(BEACON.UniqueName)
|
||||||
|
trigger.action.radioTransmission(FileName, self.Positionable:GetPositionVec3(), Modulation, true, Frequency, Power, self.BeaconName)
|
||||||
|
|
||||||
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
if BeaconDuration then -- Schedule the stop of the BEACON if asked by the MD
|
||||||
SCHEDULER:New( nil,
|
SCHEDULER:New( nil,
|
||||||
@ -429,7 +435,8 @@ function BEACON:RadioBeacon(FileName, Frequency, Modulation, Power, BeaconDurati
|
|||||||
self:StopRadioBeacon()
|
self:StopRadioBeacon()
|
||||||
end, {}, BeaconDuration)
|
end, {}, BeaconDuration)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Stops the Radio Beacon
|
--- Stops the Radio Beacon
|
||||||
@ -438,7 +445,7 @@ end
|
|||||||
function BEACON:StopRadioBeacon()
|
function BEACON:StopRadioBeacon()
|
||||||
self:F()
|
self:F()
|
||||||
-- The unique name of the transmission is the class ID
|
-- The unique name of the transmission is the class ID
|
||||||
trigger.action.stopRadioTransmission(tostring(self.ID))
|
trigger.action.stopRadioTransmission(self.BeaconName)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
--
|
--
|
||||||
-- @module Core.ClientMenu
|
-- @module Core.ClientMenu
|
||||||
-- @image Core_Menu.JPG
|
-- @image Core_Menu.JPG
|
||||||
-- last change: Apr 2024
|
-- last change: May 2024
|
||||||
|
|
||||||
-- TODO
|
-- TODO
|
||||||
----------------------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------------------
|
||||||
@ -691,7 +691,7 @@ function CLIENTMENUMANAGER:Propagate(Client)
|
|||||||
local client = _client -- Wrapper.Client#CLIENT
|
local client = _client -- Wrapper.Client#CLIENT
|
||||||
if client and client:IsAlive() then
|
if client and client:IsAlive() then
|
||||||
local playerunit = client:GetName()
|
local playerunit = client:GetName()
|
||||||
local playergroup = client:GetGroup()
|
--local playergroup = client:GetGroup()
|
||||||
local playername = client:GetPlayerName() or "none"
|
local playername = client:GetPlayerName() or "none"
|
||||||
if not knownunits[playerunit] then
|
if not knownunits[playerunit] then
|
||||||
knownunits[playerunit] = true
|
knownunits[playerunit] = true
|
||||||
|
|||||||
@ -1147,10 +1147,13 @@ end
|
|||||||
-- @param #string GroupName Group name.
|
-- @param #string GroupName Group name.
|
||||||
-- @return #table Group template table.
|
-- @return #table Group template table.
|
||||||
function DATABASE:GetGroupTemplate( GroupName )
|
function DATABASE:GetGroupTemplate( GroupName )
|
||||||
local GroupTemplate = self.Templates.Groups[GroupName].Template
|
local GroupTemplate=nil
|
||||||
GroupTemplate.SpawnCoalitionID = self.Templates.Groups[GroupName].CoalitionID
|
if self.Templates.Groups[GroupName] then
|
||||||
GroupTemplate.SpawnCategoryID = self.Templates.Groups[GroupName].CategoryID
|
GroupTemplate = self.Templates.Groups[GroupName].Template
|
||||||
GroupTemplate.SpawnCountryID = self.Templates.Groups[GroupName].CountryID
|
GroupTemplate.SpawnCoalitionID = self.Templates.Groups[GroupName].CoalitionID
|
||||||
|
GroupTemplate.SpawnCategoryID = self.Templates.Groups[GroupName].CategoryID
|
||||||
|
GroupTemplate.SpawnCountryID = self.Templates.Groups[GroupName].CountryID
|
||||||
|
end
|
||||||
return GroupTemplate
|
return GroupTemplate
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1301,7 +1301,7 @@ function EVENT:onEvent( Event )
|
|||||||
-- STATIC
|
-- STATIC
|
||||||
---
|
---
|
||||||
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.target:isExist() and Event.id ~= 33 then -- leave out ejected seat object, check that isExist exists (Kiowa Hellfire issue, Special K)
|
||||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||||
-- Workaround for borked target info on cruise missiles
|
-- Workaround for borked target info on cruise missiles
|
||||||
if Event.TgtDCSUnitName and Event.TgtDCSUnitName ~= "" then
|
if Event.TgtDCSUnitName and Event.TgtDCSUnitName ~= "" then
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -516,10 +516,10 @@ function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,G
|
|||||||
end
|
end
|
||||||
|
|
||||||
_MESSAGESRS.label = Label or MSRS.Label or "MESSAGE"
|
_MESSAGESRS.label = Label or MSRS.Label or "MESSAGE"
|
||||||
_MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE")
|
_MESSAGESRS.MSRS:SetLabel(_MESSAGESRS.label)
|
||||||
|
|
||||||
_MESSAGESRS.port = Port or MSRS.port or 5002
|
_MESSAGESRS.port = Port or MSRS.port or 5002
|
||||||
_MESSAGESRS.MSRS:SetPort(Port or 5002)
|
_MESSAGESRS.MSRS:SetPort(_MESSAGESRS.port)
|
||||||
|
|
||||||
_MESSAGESRS.volume = Volume or MSRS.volume or 1
|
_MESSAGESRS.volume = Volume or MSRS.volume or 1
|
||||||
_MESSAGESRS.MSRS:SetVolume(_MESSAGESRS.volume)
|
_MESSAGESRS.MSRS:SetVolume(_MESSAGESRS.volume)
|
||||||
|
|||||||
@ -1516,6 +1516,7 @@ do
|
|||||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||||
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
||||||
|
self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash )
|
||||||
if self.Filter.Zones then
|
if self.Filter.Zones then
|
||||||
self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self)
|
self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self)
|
||||||
local timing = self.ZoneTimerInterval or 30
|
local timing = self.ZoneTimerInterval or 30
|
||||||
@ -3477,7 +3478,7 @@ do -- SET_STATIC
|
|||||||
|
|
||||||
--- Add STATIC(s) to SET_STATIC.
|
--- Add STATIC(s) to SET_STATIC.
|
||||||
-- @param #SET_STATIC self
|
-- @param #SET_STATIC self
|
||||||
-- @param #string AddStatic A single STATIC.
|
-- @param Wrapper.Static#STATIC AddStatic A single STATIC.
|
||||||
-- @return #SET_STATIC self
|
-- @return #SET_STATIC self
|
||||||
function SET_STATIC:AddStatic( AddStatic )
|
function SET_STATIC:AddStatic( AddStatic )
|
||||||
self:F2( AddStatic:GetName() )
|
self:F2( AddStatic:GetName() )
|
||||||
|
|||||||
@ -202,19 +202,19 @@
|
|||||||
--
|
--
|
||||||
-- ### Link-16 Datalink STN and SADL IDs (limited at the moment to F15/16/18/AWACS/Tanker/B1B, but not the F15E for clients, SADL A10CII only)
|
-- ### Link-16 Datalink STN and SADL IDs (limited at the moment to F15/16/18/AWACS/Tanker/B1B, but not the F15E for clients, SADL A10CII only)
|
||||||
--
|
--
|
||||||
-- *{#SPAWN.InitSTN}(): Set the STN of the first unit in the group. All other units will have consecutive STNs, provided they have not been used yet.
|
-- * @{#SPAWN.InitSTN}(): Set the STN of the first unit in the group. All other units will have consecutive STNs, provided they have not been used yet.
|
||||||
-- *{#SPAWN.InitSADL}(): Set the SADL of the first unit in the group. All other units will have consecutive SADLs, provided they have not been used yet.
|
-- * @{#SPAWN.InitSADL}(): Set the SADL of the first unit in the group. All other units will have consecutive SADLs, provided they have not been used yet.
|
||||||
--
|
--
|
||||||
-- ### Callsigns
|
-- ### Callsigns
|
||||||
--
|
--
|
||||||
-- *{#SPAWN.InitRandomizeCallsign}(): Set a random callsign name per spawn.
|
-- * @{#SPAWN.InitRandomizeCallsign}(): Set a random callsign name per spawn.
|
||||||
-- *{#SPAWN.SpawnInitCallSign}(): Set a specific callsign for a spawned group.
|
-- * @{#SPAWN.SpawnInitCallSign}(): Set a specific callsign for a spawned group.
|
||||||
--
|
--
|
||||||
-- ### Speed
|
-- ### Speed
|
||||||
--
|
--
|
||||||
-- *{#SPAWN.InitSpeedMps}(): Set the initial speed on spawning in meters per second.
|
-- * @{#SPAWN.InitSpeedMps}(): Set the initial speed on spawning in meters per second.
|
||||||
-- *{#SPAWN.InitSpeedKph}(): Set the initial speed on spawning in kilometers per hour.
|
-- * @{#SPAWN.InitSpeedKph}(): Set the initial speed on spawning in kilometers per hour.
|
||||||
-- *{#SPAWN.InitSpeedKnots}(): Set the initial speed on spawning in knots.
|
-- * @{#SPAWN.InitSpeedKnots}(): Set the initial speed on spawning in knots.
|
||||||
--
|
--
|
||||||
-- ## SPAWN **Spawn** methods
|
-- ## SPAWN **Spawn** methods
|
||||||
--
|
--
|
||||||
@ -620,12 +620,14 @@ end
|
|||||||
-- and any spaces before and after the resulting name are removed.
|
-- and any spaces before and after the resulting name are removed.
|
||||||
-- IMPORTANT! This method MUST be the first used after :New !!!
|
-- IMPORTANT! This method MUST be the first used after :New !!!
|
||||||
-- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @param #boolean KeepUnitNames (optional) If true, the unit names are kept, false or not provided to make new unit names.
|
-- @param #boolean KeepUnitNames (optional) If true, the unit names are kept, false or not provided create new unit names.
|
||||||
-- @return #SPAWN self
|
-- @return #SPAWN self
|
||||||
function SPAWN:InitKeepUnitNames( KeepUnitNames )
|
function SPAWN:InitKeepUnitNames( KeepUnitNames )
|
||||||
self:F()
|
self:F()
|
||||||
|
|
||||||
self.SpawnInitKeepUnitNames = KeepUnitNames or true
|
self.SpawnInitKeepUnitNames = false
|
||||||
|
|
||||||
|
if KeepUnitNames == true then self.SpawnInitKeepUnitNames = true end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -1209,11 +1211,12 @@ end
|
|||||||
-- @param #number Major Major number, i.e. the group number of this name, e.g. 1 - resulting in e.g. Texaco-2-1
|
-- @param #number Major Major number, i.e. the group number of this name, e.g. 1 - resulting in e.g. Texaco-2-1
|
||||||
-- @return #SPAWN self
|
-- @return #SPAWN self
|
||||||
function SPAWN:InitCallSign(ID,Name,Minor,Major)
|
function SPAWN:InitCallSign(ID,Name,Minor,Major)
|
||||||
|
local Name = Name or "Enfield"
|
||||||
self.SpawnInitCallSign = true
|
self.SpawnInitCallSign = true
|
||||||
self.SpawnInitCallSignID = ID or 1
|
self.SpawnInitCallSignID = ID or 1
|
||||||
self.SpawnInitCallSignMinor = Minor or 1
|
self.SpawnInitCallSignMinor = Minor or 1
|
||||||
self.SpawnInitCallSignMajor = Major or 1
|
self.SpawnInitCallSignMajor = Major or 1
|
||||||
self.SpawnInitCallSignName = string.lower(Name) or "enfield"
|
self.SpawnInitCallSignName=string.lower(Name):gsub("^%l", string.upper)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1609,8 +1612,8 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
|||||||
RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnOuterRadius, self.SpawnInnerRadius )
|
RandomVec2 = PointVec3:GetRandomVec2InRadius( self.SpawnOuterRadius, self.SpawnInnerRadius )
|
||||||
numTries = numTries + 1
|
numTries = numTries + 1
|
||||||
inZone = SpawnZone:IsVec2InZone(RandomVec2)
|
inZone = SpawnZone:IsVec2InZone(RandomVec2)
|
||||||
self:I("Retrying " .. numTries .. "spawn " .. SpawnTemplate.name .. " in Zone " .. SpawnZone:GetName() .. "!")
|
--self:I("Retrying " .. numTries .. "spawn " .. SpawnTemplate.name .. " in Zone " .. SpawnZone:GetName() .. "!")
|
||||||
self:I(SpawnZone)
|
--self:I(SpawnZone)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if (not inZone) then
|
if (not inZone) then
|
||||||
@ -3436,24 +3439,28 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.SpawnInitKeepUnitNames == false then
|
if self.SpawnInitKeepUnitNames == false then
|
||||||
for UnitID = 1, #SpawnTemplate.units do
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID )
|
if not string.find(SpawnTemplate.units[UnitID].name,"#IFF_",1,true) then --Razbam IFF hack for F15E etc
|
||||||
|
SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID )
|
||||||
|
end
|
||||||
SpawnTemplate.units[UnitID].unitId = nil
|
SpawnTemplate.units[UnitID].unitId = nil
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for UnitID = 1, #SpawnTemplate.units do
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
local SpawnInitKeepUnitIFF = false
|
local SpawnInitKeepUnitIFF = false
|
||||||
if string.find(SpawnTemplate.units[UnitID].name,"#IFF_",1,true) then --Razbam IFF hack for F15E etc
|
if string.find(SpawnTemplate.units[UnitID].name,"#IFF_",1,true) then --Razbam IFF hack for F15E etc
|
||||||
SpawnInitKeepUnitIFF = true
|
SpawnInitKeepUnitIFF = true
|
||||||
end
|
|
||||||
local UnitPrefix, Rest
|
|
||||||
if SpawnInitKeepUnitIFF == false then
|
|
||||||
UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" )
|
|
||||||
self:T( { UnitPrefix, Rest } )
|
|
||||||
else
|
|
||||||
UnitPrefix=SpawnTemplate.units[UnitID].name
|
|
||||||
end
|
end
|
||||||
SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID )
|
local UnitPrefix, Rest
|
||||||
|
if SpawnInitKeepUnitIFF == false then
|
||||||
|
UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" )
|
||||||
|
SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID )
|
||||||
|
self:T( { UnitPrefix, Rest } )
|
||||||
|
--else
|
||||||
|
--UnitPrefix=SpawnTemplate.units[UnitID].name
|
||||||
|
end
|
||||||
|
--SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID )
|
||||||
|
|
||||||
SpawnTemplate.units[UnitID].unitId = nil
|
SpawnTemplate.units[UnitID].unitId = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -3090,9 +3090,25 @@ function ZONE_POLYGON:NewFromDrawing(DrawingName)
|
|||||||
-- points for the drawings are saved in local space, so add the object's map x and y coordinates to get
|
-- points for the drawings are saved in local space, so add the object's map x and y coordinates to get
|
||||||
-- world space points we can use
|
-- world space points we can use
|
||||||
for _, point in UTILS.spairs(object["points"]) do
|
for _, point in UTILS.spairs(object["points"]) do
|
||||||
|
-- check if we want to skip adding a point
|
||||||
|
local skip = false
|
||||||
local p = {x = object["mapX"] + point["x"],
|
local p = {x = object["mapX"] + point["x"],
|
||||||
y = object["mapY"] + point["y"] }
|
y = object["mapY"] + point["y"] }
|
||||||
table.add(points, p)
|
|
||||||
|
-- Check if the same coordinates already exist in the list, skip if they do
|
||||||
|
-- This can happen when drawing a Polygon in Free mode, DCS adds points on
|
||||||
|
-- top of each other that are in the `mission` file, but not visible in the
|
||||||
|
-- Mission Editor
|
||||||
|
for _, pt in pairs(points) do
|
||||||
|
if pt.x == p.x and pt.y == p.y then
|
||||||
|
skip = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if it's a unique point, add it
|
||||||
|
if not skip then
|
||||||
|
table.add(points, p)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
elseif object["polygonMode"] == "rect" then
|
elseif object["polygonMode"] == "rect" then
|
||||||
-- the points for a rect are saved as local coordinates with an angle. To get the world space points from this
|
-- the points for a rect are saved as local coordinates with an angle. To get the world space points from this
|
||||||
@ -3110,6 +3126,7 @@ function ZONE_POLYGON:NewFromDrawing(DrawingName)
|
|||||||
|
|
||||||
points = {p1, p2, p3, p4}
|
points = {p1, p2, p3, p4}
|
||||||
else
|
else
|
||||||
|
-- bring the Arrow code over from Shape/Polygon
|
||||||
-- something else that might be added in the future
|
-- something else that might be added in the future
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -74,7 +74,7 @@
|
|||||||
-- @image Designation.JPG
|
-- @image Designation.JPG
|
||||||
--
|
--
|
||||||
-- Date: 24 Oct 2021
|
-- Date: 24 Oct 2021
|
||||||
-- Last Update: Jan 2024
|
-- Last Update: May 2024
|
||||||
--
|
--
|
||||||
--- Class AUTOLASE
|
--- Class AUTOLASE
|
||||||
-- @type AUTOLASE
|
-- @type AUTOLASE
|
||||||
@ -88,6 +88,7 @@
|
|||||||
-- @field #table LaserCodes
|
-- @field #table LaserCodes
|
||||||
-- @field #table playermenus
|
-- @field #table playermenus
|
||||||
-- @field #boolean smokemenu
|
-- @field #boolean smokemenu
|
||||||
|
-- @field #boolean threatmenu
|
||||||
-- @extends Ops.Intel#INTEL
|
-- @extends Ops.Intel#INTEL
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -117,7 +118,7 @@ AUTOLASE = {
|
|||||||
|
|
||||||
--- AUTOLASE class version.
|
--- AUTOLASE class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
AUTOLASE.version = "0.1.23"
|
AUTOLASE.version = "0.1.25"
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
-- Begin Functional.Autolase.lua
|
-- Begin Functional.Autolase.lua
|
||||||
@ -205,6 +206,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
|
|||||||
self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes
|
self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes
|
||||||
self.playermenus = {}
|
self.playermenus = {}
|
||||||
self.smokemenu = true
|
self.smokemenu = true
|
||||||
|
self.threatmenu = true
|
||||||
|
|
||||||
-- Set some string id for output to DCS.log file.
|
-- Set some string id for output to DCS.log file.
|
||||||
self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown")
|
self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown")
|
||||||
@ -323,39 +325,51 @@ end
|
|||||||
function AUTOLASE:SetPilotMenu()
|
function AUTOLASE:SetPilotMenu()
|
||||||
if self.usepilotset then
|
if self.usepilotset then
|
||||||
local pilottable = self.pilotset:GetSetObjects() or {}
|
local pilottable = self.pilotset:GetSetObjects() or {}
|
||||||
|
local grouptable = {}
|
||||||
for _,_unit in pairs (pilottable) do
|
for _,_unit in pairs (pilottable) do
|
||||||
local Unit = _unit -- Wrapper.Unit#UNIT
|
local Unit = _unit -- Wrapper.Unit#UNIT
|
||||||
if Unit and Unit:IsAlive() then
|
if Unit and Unit:IsAlive() then
|
||||||
local Group = Unit:GetGroup()
|
local Group = Unit:GetGroup()
|
||||||
|
local GroupName = Group:GetName() or "none"
|
||||||
local unitname = Unit:GetName()
|
local unitname = Unit:GetName()
|
||||||
if self.playermenus[unitname] then self.playermenus[unitname]:Remove() end
|
if not grouptable[GroupName] == true then
|
||||||
local lasetopm = MENU_GROUP:New(Group,"Autolase",nil)
|
if self.playermenus[unitname] then self.playermenus[unitname]:Remove() end -- menus
|
||||||
self.playermenus[unitname] = lasetopm
|
local lasetopm = MENU_GROUP:New(Group,"Autolase",nil)
|
||||||
local lasemenu = MENU_GROUP_COMMAND:New(Group,"Status",lasetopm,self.ShowStatus,self,Group,Unit)
|
self.playermenus[unitname] = lasetopm
|
||||||
if self.smokemenu then
|
local lasemenu = MENU_GROUP_COMMAND:New(Group,"Status",lasetopm,self.ShowStatus,self,Group,Unit)
|
||||||
local smoke = (self.smoketargets == true) and "off" or "on"
|
if self.smokemenu then
|
||||||
local smoketext = string.format("Switch smoke targets to %s",smoke)
|
local smoke = (self.smoketargets == true) and "off" or "on"
|
||||||
local smokemenu = MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets))
|
local smoketext = string.format("Switch smoke targets to %s",smoke)
|
||||||
end
|
local smokemenu = MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets))
|
||||||
for _,_grp in pairs(self.RecceSet.Set) do
|
end -- smokement
|
||||||
local grp = _grp -- Wrapper.Group#GROUP
|
if self.threatmenu then
|
||||||
local unit = grp:GetUnit(1)
|
local threatmenutop = MENU_GROUP:New(Group,"Set min lasing threat",lasetopm)
|
||||||
--local name = grp:GetName()
|
for i=0,10,2 do
|
||||||
if unit and unit:IsAlive() then
|
local text = "Threatlevel "..tostring(i)
|
||||||
local name = unit:GetName()
|
local threatmenu = MENU_GROUP_COMMAND:New(Group,text,threatmenutop,self.SetMinThreatLevel,self,i)
|
||||||
local mname = string.gsub(name,".%d+.%d+$","")
|
end -- threatlevel
|
||||||
local code = self:GetLaserCode(name)
|
end -- threatmenu
|
||||||
local unittop = MENU_GROUP:New(Group,"Change laser code for "..mname,lasetopm)
|
for _,_grp in pairs(self.RecceSet.Set) do
|
||||||
for _,_code in pairs(self.LaserCodes) do
|
local grp = _grp -- Wrapper.Group#GROUP
|
||||||
local text = tostring(_code)
|
local unit = grp:GetUnit(1)
|
||||||
if _code == code then text = text.."(*)" end
|
--local name = grp:GetName()
|
||||||
local changemenu = MENU_GROUP_COMMAND:New(Group,text,unittop,self.SetRecceLaserCode,self,name,_code,true)
|
if unit and unit:IsAlive() then
|
||||||
end
|
local name = unit:GetName()
|
||||||
end
|
local mname = string.gsub(name,".%d+.%d+$","")
|
||||||
end
|
local code = self:GetLaserCode(name)
|
||||||
|
local unittop = MENU_GROUP:New(Group,"Change laser code for "..mname,lasetopm)
|
||||||
|
for _,_code in pairs(self.LaserCodes) do
|
||||||
|
local text = tostring(_code)
|
||||||
|
if _code == code then text = text.."(*)" end
|
||||||
|
local changemenu = MENU_GROUP_COMMAND:New(Group,text,unittop,self.SetRecceLaserCode,self,name,_code,true)
|
||||||
|
end -- Codes
|
||||||
|
end -- unit alive
|
||||||
|
end -- Recceset
|
||||||
|
grouptable[GroupName] = true
|
||||||
|
end -- grouptable[GroupName]
|
||||||
--lasemenu:Refresh()
|
--lasemenu:Refresh()
|
||||||
end
|
end -- unit alive
|
||||||
end
|
end -- pilot loop
|
||||||
else
|
else
|
||||||
if not self.NoMenus then
|
if not self.NoMenus then
|
||||||
self.Menu = MENU_COALITION_COMMAND:New(self.coalition,"Autolase",nil,self.ShowStatus,self)
|
self.Menu = MENU_COALITION_COMMAND:New(self.coalition,"Autolase",nil,self.ShowStatus,self)
|
||||||
@ -602,6 +616,21 @@ function AUTOLASE:DisableSmokeMenu()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- (User) Show the "Switch min threat lasing..." menu entry for pilots. On by default.
|
||||||
|
-- @param #AUTOLASE self
|
||||||
|
-- @return #AUTOLASE self
|
||||||
|
function AUTOLASE:EnableThreatLevelMenu()
|
||||||
|
self.threatmenu = true
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (User) Do not show the "Switch min threat lasing..." menu entry for pilots.
|
||||||
|
-- @param #AUTOLASE self
|
||||||
|
-- @return #AUTOLASE self
|
||||||
|
function AUTOLASE:DisableThreatLevelMenu()
|
||||||
|
self.threatmenu = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Function to calculate line of sight.
|
--- (Internal) Function to calculate line of sight.
|
||||||
-- @param #AUTOLASE self
|
-- @param #AUTOLASE self
|
||||||
@ -730,6 +759,7 @@ function AUTOLASE:ShowStatus(Group,Unit)
|
|||||||
report:Add(string.format("Recce %s has code %d",name,code))
|
report:Add(string.format("Recce %s has code %d",name,code))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
report:Add(string.format("Lasing min threat level %d",self.minthreatlevel))
|
||||||
local lines = 0
|
local lines = 0
|
||||||
for _ind,_entry in pairs(self.CurrentLasing) do
|
for _ind,_entry in pairs(self.CurrentLasing) do
|
||||||
local entry = _entry -- #AUTOLASE.LaserSpot
|
local entry = _entry -- #AUTOLASE.LaserSpot
|
||||||
|
|||||||
@ -184,7 +184,7 @@
|
|||||||
|
|
||||||
do -- DESIGNATE
|
do -- DESIGNATE
|
||||||
|
|
||||||
--- @type DESIGNATE
|
-- @type DESIGNATE
|
||||||
-- @extends Core.Fsm#FSM_PROCESS
|
-- @extends Core.Fsm#FSM_PROCESS
|
||||||
|
|
||||||
--- Manage the designation of detected targets.
|
--- Manage the designation of detected targets.
|
||||||
@ -525,7 +525,7 @@ do -- DESIGNATE
|
|||||||
|
|
||||||
self.AttackSet:ForEachGroupAlive(
|
self.AttackSet:ForEachGroupAlive(
|
||||||
|
|
||||||
--- @param Wrapper.Group#GROUP AttackGroup
|
-- @param Wrapper.Group#GROUP AttackGroup
|
||||||
function( AttackGroup )
|
function( AttackGroup )
|
||||||
self.FlashStatusMenu[AttackGroup] = FlashMenu
|
self.FlashStatusMenu[AttackGroup] = FlashMenu
|
||||||
end
|
end
|
||||||
@ -554,7 +554,7 @@ do -- DESIGNATE
|
|||||||
|
|
||||||
self.AttackSet:ForEachGroupAlive(
|
self.AttackSet:ForEachGroupAlive(
|
||||||
|
|
||||||
--- @param Wrapper.Group#GROUP AttackGroup
|
-- @param Wrapper.Group#GROUP AttackGroup
|
||||||
function( AttackGroup )
|
function( AttackGroup )
|
||||||
self.FlashDetectionMessage[AttackGroup] = FlashDetectionMessage
|
self.FlashDetectionMessage[AttackGroup] = FlashDetectionMessage
|
||||||
end
|
end
|
||||||
@ -826,7 +826,7 @@ do -- DESIGNATE
|
|||||||
-- This Detection is obsolete, remove from the designate scope
|
-- This Detection is obsolete, remove from the designate scope
|
||||||
self.Designating[DesignateIndex] = nil
|
self.Designating[DesignateIndex] = nil
|
||||||
self.AttackSet:ForEachGroupAlive(
|
self.AttackSet:ForEachGroupAlive(
|
||||||
--- @param Wrapper.Group#GROUP AttackGroup
|
-- @param Wrapper.Group#GROUP AttackGroup
|
||||||
function( AttackGroup )
|
function( AttackGroup )
|
||||||
if AttackGroup:IsAlive() == true then
|
if AttackGroup:IsAlive() == true then
|
||||||
local DetectionText = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup ):Text( ", " )
|
local DetectionText = self.Detection:DetectedItemReportSummary( DetectedItem, AttackGroup ):Text( ", " )
|
||||||
@ -903,7 +903,7 @@ do -- DESIGNATE
|
|||||||
|
|
||||||
self.AttackSet:ForEachGroupAlive(
|
self.AttackSet:ForEachGroupAlive(
|
||||||
|
|
||||||
--- @param Wrapper.Group#GROUP GroupReport
|
-- @param Wrapper.Group#GROUP GroupReport
|
||||||
function( AttackGroup )
|
function( AttackGroup )
|
||||||
|
|
||||||
if self.FlashStatusMenu[AttackGroup] or ( MenuAttackGroup and ( AttackGroup:GetName() == MenuAttackGroup:GetName() ) ) then
|
if self.FlashStatusMenu[AttackGroup] or ( MenuAttackGroup and ( AttackGroup:GetName() == MenuAttackGroup:GetName() ) ) then
|
||||||
@ -1060,7 +1060,7 @@ do -- DESIGNATE
|
|||||||
|
|
||||||
self.AttackSet:ForEachGroupAlive(
|
self.AttackSet:ForEachGroupAlive(
|
||||||
|
|
||||||
--- @param Wrapper.Group#GROUP GroupReport
|
-- @param Wrapper.Group#GROUP GroupReport
|
||||||
function( AttackGroup )
|
function( AttackGroup )
|
||||||
|
|
||||||
self:ScheduleOnce( Delay, self.SetMenu, self, AttackGroup )
|
self:ScheduleOnce( Delay, self.SetMenu, self, AttackGroup )
|
||||||
@ -1198,7 +1198,7 @@ do -- DESIGNATE
|
|||||||
--local ReportTypes = REPORT:New()
|
--local ReportTypes = REPORT:New()
|
||||||
--local ReportLaserCodes = REPORT:New()
|
--local ReportLaserCodes = REPORT:New()
|
||||||
|
|
||||||
TargetSetUnit:Flush( self )
|
--TargetSetUnit:Flush( self )
|
||||||
|
|
||||||
--self:F( { Recces = self.Recces } )
|
--self:F( { Recces = self.Recces } )
|
||||||
for TargetUnit, RecceData in pairs( self.Recces ) do
|
for TargetUnit, RecceData in pairs( self.Recces ) do
|
||||||
@ -1229,10 +1229,12 @@ do -- DESIGNATE
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if TargetSetUnit == nil then return end
|
||||||
|
|
||||||
if self.AutoLase or ( not self.AutoLase and ( self.LaseStart + Duration >= timer.getTime() ) ) then
|
if self.AutoLase or ( not self.AutoLase and ( self.LaseStart + Duration >= timer.getTime() ) ) then
|
||||||
|
|
||||||
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
|
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
|
||||||
--- @param Wrapper.Unit#UNIT SmokeUnit
|
-- @param Wrapper.Unit#UNIT SmokeUnit
|
||||||
function( TargetUnit )
|
function( TargetUnit )
|
||||||
|
|
||||||
self:F( { TargetUnit = TargetUnit:GetName() } )
|
self:F( { TargetUnit = TargetUnit:GetName() } )
|
||||||
@ -1253,7 +1255,7 @@ do -- DESIGNATE
|
|||||||
|
|
||||||
local RecceUnit = UnitData -- Wrapper.Unit#UNIT
|
local RecceUnit = UnitData -- Wrapper.Unit#UNIT
|
||||||
local RecceUnitDesc = RecceUnit:GetDesc()
|
local RecceUnitDesc = RecceUnit:GetDesc()
|
||||||
--self:F( { RecceUnit = RecceUnit:GetName(), RecceDescription = RecceUnitDesc } )
|
--self:F( { RecceUnit = RecceUnit:GetName(), RecceDescription = RecceUnitDesc } )x
|
||||||
|
|
||||||
if RecceUnit:IsLasing() == false then
|
if RecceUnit:IsLasing() == false then
|
||||||
--self:F( { IsDetected = RecceUnit:IsDetected( TargetUnit ), IsLOS = RecceUnit:IsLOS( TargetUnit ) } )
|
--self:F( { IsDetected = RecceUnit:IsDetected( TargetUnit ), IsLOS = RecceUnit:IsLOS( TargetUnit ) } )
|
||||||
@ -1275,9 +1277,10 @@ do -- DESIGNATE
|
|||||||
local Spot = RecceUnit:LaseUnit( TargetUnit, LaserCode, Duration )
|
local Spot = RecceUnit:LaseUnit( TargetUnit, LaserCode, Duration )
|
||||||
local AttackSet = self.AttackSet
|
local AttackSet = self.AttackSet
|
||||||
local DesignateName = self.DesignateName
|
local DesignateName = self.DesignateName
|
||||||
|
local typename = TargetUnit:GetTypeName()
|
||||||
|
|
||||||
function Spot:OnAfterDestroyed( From, Event, To )
|
function Spot:OnAfterDestroyed( From, Event, To )
|
||||||
self.Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() .. " destroyed. " .. TargetSetUnit:Count() .. " targets left.",
|
self.Recce:MessageToSetGroup( "Target " ..typename .. " destroyed. " .. TargetSetUnit:CountAlive() .. " targets left.",
|
||||||
5, AttackSet, self.DesignateName )
|
5, AttackSet, self.DesignateName )
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1285,7 +1288,7 @@ do -- DESIGNATE
|
|||||||
-- OK. We have assigned for the Recce a TargetUnit. We can exit the function.
|
-- OK. We have assigned for the Recce a TargetUnit. We can exit the function.
|
||||||
MarkingCount = MarkingCount + 1
|
MarkingCount = MarkingCount + 1
|
||||||
local TargetUnitType = TargetUnit:GetTypeName()
|
local TargetUnitType = TargetUnit:GetTypeName()
|
||||||
RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.",
|
RecceUnit:MessageToSetGroup( "Marking " .. TargetUnitType .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.",
|
||||||
10, self.AttackSet, DesignateName )
|
10, self.AttackSet, DesignateName )
|
||||||
if not MarkedTypes[TargetUnitType] then
|
if not MarkedTypes[TargetUnitType] then
|
||||||
MarkedTypes[TargetUnitType] = true
|
MarkedTypes[TargetUnitType] = true
|
||||||
@ -1392,7 +1395,7 @@ do -- DESIGNATE
|
|||||||
local MarkedCount = 0
|
local MarkedCount = 0
|
||||||
|
|
||||||
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
|
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
|
||||||
--- @param Wrapper.Unit#UNIT SmokeUnit
|
-- @param Wrapper.Unit#UNIT SmokeUnit
|
||||||
function( SmokeUnit )
|
function( SmokeUnit )
|
||||||
|
|
||||||
if MarkedCount < self.MaximumMarkings then
|
if MarkedCount < self.MaximumMarkings then
|
||||||
@ -1457,9 +1460,10 @@ do -- DESIGNATE
|
|||||||
-- @param #DESIGNATE self
|
-- @param #DESIGNATE self
|
||||||
-- @return #DESIGNATE
|
-- @return #DESIGNATE
|
||||||
function DESIGNATE:onafterDoneSmoking( From, Event, To, Index )
|
function DESIGNATE:onafterDoneSmoking( From, Event, To, Index )
|
||||||
|
if self.Designating[Index] ~= nil then
|
||||||
self.Designating[Index] = string.gsub( self.Designating[Index], "S", "" )
|
self.Designating[Index] = string.gsub( self.Designating[Index], "S", "" )
|
||||||
self:SetDesignateMenu()
|
self:SetDesignateMenu()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- DoneIlluminating
|
--- DoneIlluminating
|
||||||
@ -1472,5 +1476,3 @@ do -- DESIGNATE
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -545,7 +545,7 @@ do -- DETECTION_BASE
|
|||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
function DETECTION_BASE:onafterDetect( From, Event, To )
|
function DETECTION_BASE:onafterDetect( From, Event, To )
|
||||||
|
|
||||||
local DetectDelay = 0.1
|
local DetectDelay = 0.15
|
||||||
self.DetectionCount = 0
|
self.DetectionCount = 0
|
||||||
self.DetectionRun = 0
|
self.DetectionRun = 0
|
||||||
self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table
|
self:UnIdentifyAllDetectedObjects() -- Resets the DetectedObjectsIdentified table
|
||||||
@ -604,7 +604,7 @@ do -- DETECTION_BASE
|
|||||||
-- @param #number DetectionTimeStamp Time stamp of detection event.
|
-- @param #number DetectionTimeStamp Time stamp of detection event.
|
||||||
function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp )
|
function DETECTION_BASE:onafterDetection( From, Event, To, Detection, DetectionTimeStamp )
|
||||||
|
|
||||||
-- self:F( { DetectedObjects = self.DetectedObjects } )
|
self:I( { DetectedObjects = self.DetectedObjects } )
|
||||||
|
|
||||||
self.DetectionRun = self.DetectionRun + 1
|
self.DetectionRun = self.DetectionRun + 1
|
||||||
|
|
||||||
@ -612,14 +612,14 @@ do -- DETECTION_BASE
|
|||||||
|
|
||||||
if Detection and Detection:IsAlive() then
|
if Detection and Detection:IsAlive() then
|
||||||
|
|
||||||
-- self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } )
|
self:I( { "DetectionGroup is Alive", Detection:GetName() } )
|
||||||
|
|
||||||
local DetectionGroupName = Detection:GetName()
|
local DetectionGroupName = Detection:GetName()
|
||||||
local DetectionUnit = Detection:GetUnit( 1 )
|
local DetectionUnit = Detection:GetUnit( 1 )
|
||||||
|
|
||||||
local DetectedUnits = {}
|
local DetectedUnits = {}
|
||||||
|
|
||||||
local DetectedTargets = Detection:GetDetectedTargets(
|
local DetectedTargets = DetectionUnit:GetDetectedTargets(
|
||||||
self.DetectVisual,
|
self.DetectVisual,
|
||||||
self.DetectOptical,
|
self.DetectOptical,
|
||||||
self.DetectRadar,
|
self.DetectRadar,
|
||||||
@ -628,8 +628,10 @@ do -- DETECTION_BASE
|
|||||||
self.DetectDLINK
|
self.DetectDLINK
|
||||||
)
|
)
|
||||||
|
|
||||||
self:F( { DetectedTargets = DetectedTargets } )
|
--self:I( { DetectedTargets = DetectedTargets } )
|
||||||
|
--self:I(UTILS.PrintTableToLog(DetectedTargets))
|
||||||
|
|
||||||
|
|
||||||
for DetectionObjectID, Detection in pairs( DetectedTargets ) do
|
for DetectionObjectID, Detection in pairs( DetectedTargets ) do
|
||||||
local DetectedObject = Detection.object -- DCS#Object
|
local DetectedObject = Detection.object -- DCS#Object
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
-- @module Functional.Mantis
|
-- @module Functional.Mantis
|
||||||
-- @image Functional.Mantis.jpg
|
-- @image Functional.Mantis.jpg
|
||||||
--
|
--
|
||||||
-- Last Update: Feb 2024
|
-- Last Update: May 2024
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--- **MANTIS** class, extends Core.Base#BASE
|
--- **MANTIS** class, extends Core.Base#BASE
|
||||||
@ -58,6 +58,7 @@
|
|||||||
-- @field #boolean ShoradLink If true, #MANTIS has #SHORAD enabled
|
-- @field #boolean ShoradLink If true, #MANTIS has #SHORAD enabled
|
||||||
-- @field #number ShoradTime Timer in seconds, how long #SHORAD will be active after a detection inside of the defense range
|
-- @field #number ShoradTime Timer in seconds, how long #SHORAD will be active after a detection inside of the defense range
|
||||||
-- @field #number ShoradActDistance Distance of an attacker in meters from a Mantis SAM site, on which Shorad will be switched on. Useful to not give away Shorad sites too early. Default 15km. Should be smaller than checkradius.
|
-- @field #number ShoradActDistance Distance of an attacker in meters from a Mantis SAM site, on which Shorad will be switched on. Useful to not give away Shorad sites too early. Default 15km. Should be smaller than checkradius.
|
||||||
|
-- @field #boolean checkforfriendlies If true, do not activate a SAM installation if a friendly aircraft is in firing range.
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
|
||||||
@ -187,29 +188,34 @@
|
|||||||
-- -- This is effectively a 3-stage filter allowing for zone overlap. A coordinate is accepted first when
|
-- -- This is effectively a 3-stage filter allowing for zone overlap. A coordinate is accepted first when
|
||||||
-- -- it is inside any AcceptZone. Then RejectZones are checked, which enforces both borders, but also overlaps of
|
-- -- it is inside any AcceptZone. Then RejectZones are checked, which enforces both borders, but also overlaps of
|
||||||
-- -- Accept- and RejectZones. Last, if it is inside a conflict zone, it is accepted.
|
-- -- Accept- and RejectZones. Last, if it is inside a conflict zone, it is accepted.
|
||||||
-- `mybluemantis:AddZones(AcceptZones,RejectZones,ConflictZones)`
|
-- mybluemantis:AddZones(AcceptZones,RejectZones,ConflictZones)
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- ### 2.1.2 Change the number of long-, mid- and short-range systems going live on a detected target:
|
-- ### 2.1.2 Change the number of long-, mid- and short-range systems going live on a detected target:
|
||||||
--
|
--
|
||||||
-- -- parameters are numbers. Defaults are 1,2,2,6 respectively
|
-- -- parameters are numbers. Defaults are 1,2,2,6 respectively
|
||||||
-- `mybluemantis:SetMaxActiveSAMs(Short,Mid,Long,Classic)`
|
-- mybluemantis:SetMaxActiveSAMs(Short,Mid,Long,Classic)
|
||||||
--
|
--
|
||||||
-- ### 2.1.3 SHORAD will automatically be added from SAM sites of type "short-range"
|
-- ### 2.1.3 SHORAD will automatically be added from SAM sites of type "short-range"
|
||||||
--
|
--
|
||||||
-- ### 2.1.4 Advanced features
|
-- ### 2.1.4 Advanced features
|
||||||
--
|
--
|
||||||
-- -- switch off auto mode **before** you start MANTIS.
|
-- -- switch off auto mode **before** you start MANTIS.
|
||||||
-- `mybluemantis.automode = false`
|
-- mybluemantis.automode = false
|
||||||
--
|
--
|
||||||
-- -- switch off auto shorad **before** you start MANTIS.
|
-- -- switch off auto shorad **before** you start MANTIS.
|
||||||
-- `mybluemantis.autoshorad = false`
|
-- mybluemantis.autoshorad = false
|
||||||
--
|
--
|
||||||
-- -- scale of the activation range, i.e. don't activate at the fringes of max range, defaults below.
|
-- -- scale of the activation range, i.e. don't activate at the fringes of max range, defaults below.
|
||||||
-- -- also see engagerange below.
|
-- -- also see engagerange below.
|
||||||
-- ` self.radiusscale[MANTIS.SamType.LONG] = 1.1`
|
-- self.radiusscale[MANTIS.SamType.LONG] = 1.1
|
||||||
-- ` self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2`
|
-- self.radiusscale[MANTIS.SamType.MEDIUM] = 1.2
|
||||||
-- ` self.radiusscale[MANTIS.SamType.SHORT] = 1.3`
|
-- self.radiusscale[MANTIS.SamType.SHORT] = 1.3
|
||||||
|
--
|
||||||
|
-- ### 2.1.5 Friendlies check in firing range
|
||||||
|
--
|
||||||
|
-- -- For some scenarios, like Cold War, it might be useful not to activate SAMs if friendly aircraft are around to avoid death by friendly fire.
|
||||||
|
-- mybluemantis.checkforfriendlies = true
|
||||||
--
|
--
|
||||||
-- # 3. Default settings [both modes unless stated otherwise]
|
-- # 3. Default settings [both modes unless stated otherwise]
|
||||||
--
|
--
|
||||||
@ -321,6 +327,7 @@ MANTIS = {
|
|||||||
automode = true,
|
automode = true,
|
||||||
autoshorad = true,
|
autoshorad = true,
|
||||||
ShoradGroupSet = nil,
|
ShoradGroupSet = nil,
|
||||||
|
checkforfriendlies = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Advanced state enumerator
|
--- Advanced state enumerator
|
||||||
@ -502,7 +509,8 @@ do
|
|||||||
-- DONE: Treat Awacs separately, since they might be >80km off site
|
-- DONE: Treat Awacs separately, since they might be >80km off site
|
||||||
-- DONE: Allow tables of prefixes for the setup
|
-- DONE: Allow tables of prefixes for the setup
|
||||||
-- DONE: Auto-Mode with range setups for various known SAM types.
|
-- DONE: Auto-Mode with range setups for various known SAM types.
|
||||||
|
|
||||||
|
self.name = name or "mymantis"
|
||||||
self.SAM_Templates_Prefix = samprefix or "Red SAM"
|
self.SAM_Templates_Prefix = samprefix or "Red SAM"
|
||||||
self.EWR_Templates_Prefix = ewrprefix or "Red EWR"
|
self.EWR_Templates_Prefix = ewrprefix or "Red EWR"
|
||||||
self.HQ_Template_CC = hq or nil
|
self.HQ_Template_CC = hq or nil
|
||||||
@ -631,7 +639,7 @@ do
|
|||||||
|
|
||||||
-- TODO Version
|
-- TODO Version
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
self.version="0.8.16"
|
self.version="0.8.18"
|
||||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||||
|
|
||||||
--- FSM Functions ---
|
--- FSM Functions ---
|
||||||
@ -1255,6 +1263,10 @@ do
|
|||||||
-- DEBUG
|
-- DEBUG
|
||||||
set = self:_PreFilterHeight(height)
|
set = self:_PreFilterHeight(height)
|
||||||
end
|
end
|
||||||
|
local friendlyset -- Core.Set#SET_GROUP
|
||||||
|
if self.checkforfriendlies == true then
|
||||||
|
friendlyset = SET_GROUP:New():FilterCoalitions(self.Coalition):FilterCategories({"plane","helicopter"}):FilterFunction(function(grp) if grp and grp:InAir() then return true else return false end end):FilterOnce()
|
||||||
|
end
|
||||||
for _,_coord in pairs (set) do
|
for _,_coord in pairs (set) do
|
||||||
local coord = _coord -- get current coord to check
|
local coord = _coord -- get current coord to check
|
||||||
-- output for cross-check
|
-- output for cross-check
|
||||||
@ -1279,8 +1291,16 @@ do
|
|||||||
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
|
local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug)
|
||||||
self:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
end
|
end
|
||||||
|
-- friendlies around?
|
||||||
|
local nofriendlies = true
|
||||||
|
if self.checkforfriendlies == true then
|
||||||
|
local closestfriend, distance = friendlyset:GetClosestGroup(samcoordinate)
|
||||||
|
if closestfriend and distance and distance < rad then
|
||||||
|
nofriendlies = false
|
||||||
|
end
|
||||||
|
end
|
||||||
-- end output to cross-check
|
-- end output to cross-check
|
||||||
if targetdistance <= rad and zonecheck then
|
if targetdistance <= rad and zonecheck == true and nofriendlies == true then
|
||||||
return true, targetdistance
|
return true, targetdistance
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
-- Implementation is based on the [Simple Range Script](https://forums.eagle.ru/showthread.php?t=157991) by Ciribob, which itself was motivated
|
-- Implementation is based on the [Simple Range Script](https://forums.eagle.ru/showthread.php?t=157991) by Ciribob, which itself was motivated
|
||||||
-- by a script by SNAFU [see here](https://forums.eagle.ru/showthread.php?t=109174).
|
-- by a script by SNAFU [see here](https://forums.eagle.ru/showthread.php?t=109174).
|
||||||
--
|
--
|
||||||
-- [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is highly recommended for this class.
|
-- [476th - Air Weapons Range Objects mod](https://www.476vfightergroup.com/downloads.php?do=download&downloadid=482) is highly recommended for this class.
|
||||||
--
|
--
|
||||||
-- **Main Features:**
|
-- **Main Features:**
|
||||||
--
|
--
|
||||||
@ -46,7 +46,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Contributions: FlightControl, Ciribob
|
-- ### Contributions: FlightControl, Ciribob
|
||||||
-- ### SRS Additions: Applevangelist
|
-- ### SRS Additions: Applevangelist
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
-- @module Functional.Range
|
-- @module Functional.Range
|
||||||
-- @image Range.JPG
|
-- @image Range.JPG
|
||||||
@ -102,7 +102,7 @@
|
|||||||
-- @field #string targetpath Path where to save the target sheets.
|
-- @field #string targetpath Path where to save the target sheets.
|
||||||
-- @field #string targetprefix File prefix for target sheet files.
|
-- @field #string targetprefix File prefix for target sheet files.
|
||||||
-- @field Sound.SRS#MSRS controlmsrs SRS wrapper for range controller.
|
-- @field Sound.SRS#MSRS controlmsrs SRS wrapper for range controller.
|
||||||
-- @field Sound.SRS#MSRSQUEUE controlsrsQ SRS queue for range controller.
|
-- @field Sound.SRS#MSRSQUEUE controlsrsQ SRS queue for range controller.
|
||||||
-- @field Sound.SRS#MSRS instructmsrs SRS wrapper for range instructor.
|
-- @field Sound.SRS#MSRS instructmsrs SRS wrapper for range instructor.
|
||||||
-- @field Sound.SRS#MSRSQUEUE instructsrsQ SRS queue for range instructor.
|
-- @field Sound.SRS#MSRSQUEUE instructsrsQ SRS queue for range instructor.
|
||||||
-- @field #number Coalition Coalition side for the menu, if any.
|
-- @field #number Coalition Coalition side for the menu, if any.
|
||||||
@ -169,7 +169,7 @@
|
|||||||
--
|
--
|
||||||
-- ## Specifying Coordinates
|
-- ## Specifying Coordinates
|
||||||
--
|
--
|
||||||
-- It is also possible to specify coordinates rather than unit or static objects as bombing target locations. This has the advantage, that even when the unit/static object is dead, the specified
|
-- It is also possible to specify coordinates rather than unit or static objects as bombing target locations. This has the advantage, that even when the unit/static object is dead, the specified
|
||||||
-- coordinate will still be a valid impact point. This can be done via the @{#RANGE.AddBombingTargetCoordinate}(*coord*, *name*, *goodhitrange*) function.
|
-- coordinate will still be a valid impact point. This can be done via the @{#RANGE.AddBombingTargetCoordinate}(*coord*, *name*, *goodhitrange*) function.
|
||||||
--
|
--
|
||||||
-- # Fine Tuning
|
-- # Fine Tuning
|
||||||
@ -231,8 +231,8 @@
|
|||||||
-- By default, the sound files are placed in the "Range Soundfiles/" folder inside the mission (.miz) file. Another folder can be specified via the @{#RANGE.SetSoundfilesPath}(*path*) function.
|
-- By default, the sound files are placed in the "Range Soundfiles/" folder inside the mission (.miz) file. Another folder can be specified via the @{#RANGE.SetSoundfilesPath}(*path*) function.
|
||||||
--
|
--
|
||||||
-- ## Voice output via SRS
|
-- ## Voice output via SRS
|
||||||
--
|
--
|
||||||
-- Alternatively, the voice output can be fully done via SRS, **no sound file additions needed**. Set up SRS with @{#RANGE.SetSRS}().
|
-- Alternatively, the voice output can be fully done via SRS, **no sound file additions needed**. Set up SRS with @{#RANGE.SetSRS}().
|
||||||
-- Range control and instructor frequencies and voices can then be set via @{#RANGE.SetSRSRangeControl}() and @{#RANGE.SetSRSRangeInstructor}().
|
-- Range control and instructor frequencies and voices can then be set via @{#RANGE.SetSRSRangeControl}() and @{#RANGE.SetSRSRangeInstructor}().
|
||||||
--
|
--
|
||||||
-- # Persistence
|
-- # Persistence
|
||||||
@ -243,11 +243,11 @@
|
|||||||
-- The next time you start the mission, these results are also automatically loaded.
|
-- The next time you start the mission, these results are also automatically loaded.
|
||||||
--
|
--
|
||||||
-- Strafing results are currently **not** saved.
|
-- Strafing results are currently **not** saved.
|
||||||
--
|
--
|
||||||
-- # FSM Events
|
-- # FSM Events
|
||||||
--
|
--
|
||||||
-- This class creates additional events that can be used by mission designers for custom reactions
|
-- This class creates additional events that can be used by mission designers for custom reactions
|
||||||
--
|
--
|
||||||
-- * `EnterRange` when a player enters a range zone. See @{#RANGE.OnAfterEnterRange}
|
-- * `EnterRange` when a player enters a range zone. See @{#RANGE.OnAfterEnterRange}
|
||||||
-- * `ExitRange` when a player leaves a range zone. See @{#RANGE.OnAfterExitRange}
|
-- * `ExitRange` when a player leaves a range zone. See @{#RANGE.OnAfterExitRange}
|
||||||
-- * `Impact` on impact of a player's weapon on a bombing target. See @{#RANGE.OnAfterImpact}
|
-- * `Impact` on impact of a player's weapon on a bombing target. See @{#RANGE.OnAfterImpact}
|
||||||
@ -371,7 +371,7 @@ RANGE = {
|
|||||||
-- @param #number boxlength Length of strafe pit box in meters.
|
-- @param #number boxlength Length of strafe pit box in meters.
|
||||||
-- @param #number boxwidth Width of strafe pit box in meters.
|
-- @param #number boxwidth Width of strafe pit box in meters.
|
||||||
-- @param #number goodpass Number of hits for a good strafing pit pass.
|
-- @param #number goodpass Number of hits for a good strafing pit pass.
|
||||||
-- @param #number foulline Distance of foul line in meters.
|
-- @param #number foulline Distance of foul line in meters.
|
||||||
RANGE.Defaults = {
|
RANGE.Defaults = {
|
||||||
goodhitrange = 25,
|
goodhitrange = 25,
|
||||||
strafemaxalt = 914,
|
strafemaxalt = 914,
|
||||||
@ -625,9 +625,9 @@ function RANGE:New( RangeName, Coalition )
|
|||||||
-- Get range name.
|
-- Get range name.
|
||||||
-- TODO: make sure that the range name is not given twice. This would lead to problems in the F10 radio menu.
|
-- TODO: make sure that the range name is not given twice. This would lead to problems in the F10 radio menu.
|
||||||
self.rangename = RangeName or "Practice Range"
|
self.rangename = RangeName or "Practice Range"
|
||||||
|
|
||||||
self.Coalition = Coalition
|
self.Coalition = Coalition
|
||||||
|
|
||||||
-- Log id.
|
-- Log id.
|
||||||
self.lid = string.format( "RANGE %s | ", self.rangename )
|
self.lid = string.format( "RANGE %s | ", self.rangename )
|
||||||
|
|
||||||
@ -993,9 +993,9 @@ end
|
|||||||
-- @param #string Host Host. Default "127.0.0.1".
|
-- @param #string Host Host. Default "127.0.0.1".
|
||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
function RANGE:SetFunkManOn(Port, Host)
|
function RANGE:SetFunkManOn(Port, Host)
|
||||||
|
|
||||||
self.funkmanSocket=SOCKET:New(Port, Host)
|
self.funkmanSocket=SOCKET:New(Port, Host)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1200,7 +1200,7 @@ end
|
|||||||
-- @param #string PathToSRS Path to SRS directory.
|
-- @param #string PathToSRS Path to SRS directory.
|
||||||
-- @param #number Port SRS port. Default 5002.
|
-- @param #number Port SRS port. Default 5002.
|
||||||
-- @param #number Coalition Coalition side, e.g. `coalition.side.BLUE` or `coalition.side.RED`. Default `coalition.side.BLUE`.
|
-- @param #number Coalition Coalition side, e.g. `coalition.side.BLUE` or `coalition.side.RED`. Default `coalition.side.BLUE`.
|
||||||
-- @param #number Frequency Frequency to use. Default is 256 MHz for range control and 305 MHz for instructor. If given, both control and instructor get this frequency.
|
-- @param #number Frequency Frequency to use. Default is 256 MHz for range control and 305 MHz for instructor. If given, both control and instructor get this frequency.
|
||||||
-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM
|
-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM
|
||||||
-- @param #number Volume Volume, between 0.0 and 1.0. Defaults to 1.0
|
-- @param #number Volume Volume, between 0.0 and 1.0. Defaults to 1.0
|
||||||
-- @param #string PathToGoogleKey Path to Google TTS credentials.
|
-- @param #string PathToGoogleKey Path to Google TTS credentials.
|
||||||
@ -1208,9 +1208,9 @@ end
|
|||||||
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
|
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
|
||||||
|
|
||||||
if PathToSRS or MSRS.path then
|
if PathToSRS or MSRS.path then
|
||||||
|
|
||||||
self.useSRS=true
|
self.useSRS=true
|
||||||
|
|
||||||
self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM)
|
self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM)
|
||||||
self.controlmsrs:SetPort(Port or MSRS.port)
|
self.controlmsrs:SetPort(Port or MSRS.port)
|
||||||
self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
||||||
@ -1224,14 +1224,12 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume,
|
|||||||
self.instructmsrs:SetLabel("RANGEI")
|
self.instructmsrs:SetLabel("RANGEI")
|
||||||
self.instructmsrs:SetVolume(Volume or 1.0)
|
self.instructmsrs:SetVolume(Volume or 1.0)
|
||||||
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
|
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
|
||||||
|
|
||||||
if PathToGoogleKey then
|
if PathToGoogleKey then
|
||||||
self.controlmsrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey)
|
self.controlmsrs:SetGoogle(PathToGoogleKey)
|
||||||
self.controlmsrs:SetProvider(MSRS.Provider.GOOGLE)
|
self.instructmsrs:SetGoogle(PathToGoogleKey)
|
||||||
self.instructmsrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey)
|
|
||||||
self.instructmsrs:SetProvider(MSRS.Provider.GOOGLE)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E(self.lid..string.format("ERROR: No SRS path specified!"))
|
self:E(self.lid..string.format("ERROR: No SRS path specified!"))
|
||||||
end
|
end
|
||||||
@ -1741,9 +1739,9 @@ end
|
|||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function RANGE:OnEventBirth( EventData )
|
function RANGE:OnEventBirth( EventData )
|
||||||
self:F( { eventbirth = EventData } )
|
self:F( { eventbirth = EventData } )
|
||||||
|
|
||||||
if not EventData.IniPlayerName then return end
|
if not EventData.IniPlayerName then return end
|
||||||
|
|
||||||
local _unitName = EventData.IniUnitName
|
local _unitName = EventData.IniUnitName
|
||||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||||
|
|
||||||
@ -1764,7 +1762,7 @@ function RANGE:OnEventBirth( EventData )
|
|||||||
|
|
||||||
-- Reset current strafe status.
|
-- Reset current strafe status.
|
||||||
self.strafeStatus[_uid] = nil
|
self.strafeStatus[_uid] = nil
|
||||||
|
|
||||||
if self.Coalition then
|
if self.Coalition then
|
||||||
if EventData.IniCoalition == self.Coalition then
|
if EventData.IniCoalition == self.Coalition then
|
||||||
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
|
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
|
||||||
@ -1773,7 +1771,7 @@ function RANGE:OnEventBirth( EventData )
|
|||||||
-- Add Menu commands after a delay of 0.1 seconds.
|
-- Add Menu commands after a delay of 0.1 seconds.
|
||||||
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
|
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- By default, some bomb impact points and do not flare each hit on target.
|
-- By default, some bomb impact points and do not flare each hit on target.
|
||||||
self.PlayerSettings[_playername] = {} -- #RANGE.PlayerData
|
self.PlayerSettings[_playername] = {} -- #RANGE.PlayerData
|
||||||
self.PlayerSettings[_playername].smokebombimpact = self.defaultsmokebomb
|
self.PlayerSettings[_playername].smokebombimpact = self.defaultsmokebomb
|
||||||
@ -1907,21 +1905,21 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
|
|||||||
local _distance = nil
|
local _distance = nil
|
||||||
local _closeCoord = nil --Core.Point#COORDINATE
|
local _closeCoord = nil --Core.Point#COORDINATE
|
||||||
local _hitquality = "POOR"
|
local _hitquality = "POOR"
|
||||||
|
|
||||||
-- Get callsign.
|
-- Get callsign.
|
||||||
local _callsign = self:_myname( playerData.unitname )
|
local _callsign = self:_myname( playerData.unitname )
|
||||||
|
|
||||||
local _playername=playerData.playername
|
local _playername=playerData.playername
|
||||||
|
|
||||||
local _unit=playerData.unit
|
local _unit=playerData.unit
|
||||||
|
|
||||||
-- Coordinate of impact point.
|
-- Coordinate of impact point.
|
||||||
local impactcoord = weapon:GetImpactCoordinate()
|
local impactcoord = weapon:GetImpactCoordinate()
|
||||||
|
|
||||||
-- Check if impact happened in range zone.
|
-- Check if impact happened in range zone.
|
||||||
local insidezone = self.rangezone:IsCoordinateInZone( impactcoord )
|
local insidezone = self.rangezone:IsCoordinateInZone( impactcoord )
|
||||||
|
|
||||||
|
|
||||||
-- Smoke impact point of bomb.
|
-- Smoke impact point of bomb.
|
||||||
if playerData.smokebombimpact and insidezone then
|
if playerData.smokebombimpact and insidezone then
|
||||||
if playerData.delaysmoke then
|
if playerData.delaysmoke then
|
||||||
@ -1930,19 +1928,19 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
|
|||||||
impactcoord:Smoke( playerData.smokecolor )
|
impactcoord:Smoke( playerData.smokecolor )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Loop over defined bombing targets.
|
-- Loop over defined bombing targets.
|
||||||
for _, _bombtarget in pairs( self.bombingTargets ) do
|
for _, _bombtarget in pairs( self.bombingTargets ) do
|
||||||
local bombtarget=_bombtarget --#RANGE.BombTarget
|
local bombtarget=_bombtarget --#RANGE.BombTarget
|
||||||
|
|
||||||
-- Get target coordinate.
|
-- Get target coordinate.
|
||||||
local targetcoord = self:_GetBombTargetCoordinate( _bombtarget )
|
local targetcoord = self:_GetBombTargetCoordinate( _bombtarget )
|
||||||
|
|
||||||
if targetcoord then
|
if targetcoord then
|
||||||
|
|
||||||
-- Distance between bomb and target.
|
-- Distance between bomb and target.
|
||||||
local _temp = impactcoord:Get2DDistance( targetcoord )
|
local _temp = impactcoord:Get2DDistance( targetcoord )
|
||||||
|
|
||||||
-- Find closest target to last known position of the bomb.
|
-- Find closest target to last known position of the bomb.
|
||||||
if _distance == nil or _temp < _distance then
|
if _distance == nil or _temp < _distance then
|
||||||
_distance = _temp
|
_distance = _temp
|
||||||
@ -1959,21 +1957,21 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
|
|||||||
else
|
else
|
||||||
_hitquality = "POOR"
|
_hitquality = "POOR"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Count if bomb fell less than ~1 km away from the target.
|
-- Count if bomb fell less than ~1 km away from the target.
|
||||||
if _distance and _distance <= self.scorebombdistance then
|
if _distance and _distance <= self.scorebombdistance then
|
||||||
-- Init bomb player results.
|
-- Init bomb player results.
|
||||||
if not self.bombPlayerResults[_playername] then
|
if not self.bombPlayerResults[_playername] then
|
||||||
self.bombPlayerResults[_playername] = {}
|
self.bombPlayerResults[_playername] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Local results.
|
-- Local results.
|
||||||
local _results = self.bombPlayerResults[_playername]
|
local _results = self.bombPlayerResults[_playername]
|
||||||
|
|
||||||
local result = {} -- #RANGE.BombResult
|
local result = {} -- #RANGE.BombResult
|
||||||
result.command=SOCKET.DataType.BOMBRESULT
|
result.command=SOCKET.DataType.BOMBRESULT
|
||||||
result.name = _closetTarget.name or "unknown"
|
result.name = _closetTarget.name or "unknown"
|
||||||
@ -1995,24 +1993,24 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
|
|||||||
result.attackVel = attackVel
|
result.attackVel = attackVel
|
||||||
result.attackAlt = attackAlt
|
result.attackAlt = attackAlt
|
||||||
result.date=os and os.date() or "n/a"
|
result.date=os and os.date() or "n/a"
|
||||||
|
|
||||||
-- Add to table.
|
-- Add to table.
|
||||||
table.insert( _results, result )
|
table.insert( _results, result )
|
||||||
|
|
||||||
-- Call impact.
|
-- Call impact.
|
||||||
self:Impact( result, playerData )
|
self:Impact( result, playerData )
|
||||||
|
|
||||||
elseif insidezone then
|
elseif insidezone then
|
||||||
|
|
||||||
-- Send message.
|
-- Send message.
|
||||||
-- DONE SRS message
|
-- DONE SRS message
|
||||||
local _message = string.format( "%s, weapon impacted too far from nearest range target (>%.1f km). No score!", _callsign, self.scorebombdistance / 1000 )
|
local _message = string.format( "%s, weapon impacted too far from nearest range target (>%.1f km). No score!", _callsign, self.scorebombdistance / 1000 )
|
||||||
if self.useSRS then
|
if self.useSRS then
|
||||||
local ttstext = string.format( "%s, weapon impacted too far from nearest range target, mor than %.1f kilometer. No score!", _callsign, self.scorebombdistance / 1000 )
|
local ttstext = string.format( "%s, weapon impacted too far from nearest range target, mor than %.1f kilometer. No score!", _callsign, self.scorebombdistance / 1000 )
|
||||||
self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
|
self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
|
||||||
end
|
end
|
||||||
self:_DisplayMessageToGroup( _unit, _message, nil, false )
|
self:_DisplayMessageToGroup( _unit, _message, nil, false )
|
||||||
|
|
||||||
if self.rangecontrol then
|
if self.rangecontrol then
|
||||||
-- weapon impacted too far from the nearest target! No Score!
|
-- weapon impacted too far from the nearest target! No Score!
|
||||||
if self.useSRS then
|
if self.useSRS then
|
||||||
@ -2021,11 +2019,11 @@ function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackV
|
|||||||
self.rangecontrol:NewTransmission( RANGE.Sound.RCWeaponImpactedTooFar.filename, RANGE.Sound.RCWeaponImpactedTooFar.duration, self.soundpath, nil, nil, _message, self.subduration )
|
self.rangecontrol:NewTransmission( RANGE.Sound.RCWeaponImpactedTooFar.filename, RANGE.Sound.RCWeaponImpactedTooFar.duration, self.soundpath, nil, nil, _message, self.subduration )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
self:T( self.lid .. "Weapon impacted outside range zone." )
|
self:T( self.lid .. "Weapon impacted outside range zone." )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
|
--- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
|
||||||
@ -2038,7 +2036,7 @@ function RANGE:OnEventShot( EventData )
|
|||||||
if EventData.Weapon == nil or EventData.IniDCSUnit == nil or EventData.IniPlayerName == nil then
|
if EventData.Weapon == nil or EventData.IniDCSUnit == nil or EventData.IniPlayerName == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create weapon object.
|
-- Create weapon object.
|
||||||
local weapon=WEAPON:New(EventData.weapon)
|
local weapon=WEAPON:New(EventData.weapon)
|
||||||
|
|
||||||
@ -2050,7 +2048,7 @@ function RANGE:OnEventShot( EventData )
|
|||||||
|
|
||||||
-- Get player unit and name.
|
-- Get player unit and name.
|
||||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||||
|
|
||||||
-- Distance Player-to-Range. Set this to larger value than the threshold.
|
-- Distance Player-to-Range. Set this to larger value than the threshold.
|
||||||
local dPR = self.BombtrackThreshold * 2
|
local dPR = self.BombtrackThreshold * 2
|
||||||
|
|
||||||
@ -2065,16 +2063,16 @@ function RANGE:OnEventShot( EventData )
|
|||||||
|
|
||||||
-- Player data.
|
-- Player data.
|
||||||
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
|
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
|
||||||
|
|
||||||
-- Attack parameters.
|
-- Attack parameters.
|
||||||
local attackHdg=_unit:GetHeading()
|
local attackHdg=_unit:GetHeading()
|
||||||
local attackAlt=_unit:GetHeight()
|
local attackAlt=_unit:GetHeight()
|
||||||
attackAlt = UTILS.MetersToFeet(attackAlt)
|
attackAlt = UTILS.MetersToFeet(attackAlt)
|
||||||
local attackVel=_unit:GetVelocityKNOTS()
|
local attackVel=_unit:GetVelocityKNOTS()
|
||||||
|
|
||||||
-- Tracking info and init of last bomb position.
|
-- Tracking info and init of last bomb position.
|
||||||
self:T( self.lid .. string.format( "RANGE %s: Tracking %s - %s.", self.rangename, weapon:GetTypeName(), weapon:GetName()))
|
self:T( self.lid .. string.format( "RANGE %s: Tracking %s - %s.", self.rangename, weapon:GetTypeName(), weapon:GetName()))
|
||||||
|
|
||||||
-- Set callback function on impact.
|
-- Set callback function on impact.
|
||||||
weapon:SetFuncImpact(RANGE._OnImpact, self, playerData, attackHdg, attackAlt, attackVel)
|
weapon:SetFuncImpact(RANGE._OnImpact, self, playerData, attackHdg, attackAlt, attackVel)
|
||||||
|
|
||||||
@ -2146,33 +2144,33 @@ end
|
|||||||
function RANGE:onafterEnterRange( From, Event, To, player )
|
function RANGE:onafterEnterRange( From, Event, To, player )
|
||||||
|
|
||||||
if self.instructor and self.rangecontrol then
|
if self.instructor and self.rangecontrol then
|
||||||
|
|
||||||
if self.useSRS then
|
if self.useSRS then
|
||||||
|
|
||||||
|
|
||||||
local text = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f MHz", self.rangecontrolfreq)
|
local text = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f MHz", self.rangecontrolfreq)
|
||||||
local ttstext = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f mega hertz.", self.rangecontrolfreq)
|
local ttstext = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f mega hertz.", self.rangecontrolfreq)
|
||||||
|
|
||||||
local group = player.client:GetGroup()
|
local group = player.client:GetGroup()
|
||||||
|
|
||||||
self.instructsrsQ:NewTransmission(ttstext, nil, self.instructmsrs, nil, 1, {group}, text, 10)
|
self.instructsrsQ:NewTransmission(ttstext, nil, self.instructmsrs, nil, 1, {group}, text, 10)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Range control radio frequency split.
|
-- Range control radio frequency split.
|
||||||
local RF = UTILS.Split( string.format( "%.3f", self.rangecontrolfreq ), "." )
|
local RF = UTILS.Split( string.format( "%.3f", self.rangecontrolfreq ), "." )
|
||||||
|
|
||||||
-- Radio message that player entered the range
|
-- Radio message that player entered the range
|
||||||
|
|
||||||
-- You entered the bombing range. For hit assessment, contact the range controller at xy MHz
|
-- You entered the bombing range. For hit assessment, contact the range controller at xy MHz
|
||||||
self.instructor:NewTransmission( RANGE.Sound.IREnterRange.filename, RANGE.Sound.IREnterRange.duration, self.soundpath )
|
self.instructor:NewTransmission( RANGE.Sound.IREnterRange.filename, RANGE.Sound.IREnterRange.duration, self.soundpath )
|
||||||
self.instructor:Number2Transmission( RF[1] )
|
self.instructor:Number2Transmission( RF[1] )
|
||||||
|
|
||||||
if tonumber( RF[2] ) > 0 then
|
if tonumber( RF[2] ) > 0 then
|
||||||
self.instructor:NewTransmission( RANGE.Sound.IRDecimal.filename, RANGE.Sound.IRDecimal.duration, self.soundpath )
|
self.instructor:NewTransmission( RANGE.Sound.IRDecimal.filename, RANGE.Sound.IRDecimal.duration, self.soundpath )
|
||||||
self.instructor:Number2Transmission( RF[2] )
|
self.instructor:Number2Transmission( RF[2] )
|
||||||
end
|
end
|
||||||
|
|
||||||
self.instructor:NewTransmission( RANGE.Sound.IRMegaHertz.filename, RANGE.Sound.IRMegaHertz.duration, self.soundpath )
|
self.instructor:NewTransmission( RANGE.Sound.IRMegaHertz.filename, RANGE.Sound.IRMegaHertz.duration, self.soundpath )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -2190,11 +2188,11 @@ function RANGE:onafterExitRange( From, Event, To, player )
|
|||||||
if self.instructor then
|
if self.instructor then
|
||||||
-- You left the bombing range zone. Have a nice day!
|
-- You left the bombing range zone. Have a nice day!
|
||||||
if self.useSRS then
|
if self.useSRS then
|
||||||
|
|
||||||
local text = "You left the bombing range zone. "
|
local text = "You left the bombing range zone. "
|
||||||
|
|
||||||
local r=math.random(5)
|
local r=math.random(5)
|
||||||
|
|
||||||
if r==1 then
|
if r==1 then
|
||||||
text=text.."Have a nice day!"
|
text=text.."Have a nice day!"
|
||||||
elseif r==2 then
|
elseif r==2 then
|
||||||
@ -2204,9 +2202,9 @@ function RANGE:onafterExitRange( From, Event, To, player )
|
|||||||
elseif r==4 then
|
elseif r==4 then
|
||||||
text=text.."See you in two weeks!"
|
text=text.."See you in two weeks!"
|
||||||
elseif r==5 then
|
elseif r==5 then
|
||||||
text=text.."!"
|
text=text.."!"
|
||||||
end
|
end
|
||||||
|
|
||||||
self.instructsrsQ:NewTransmission(text, nil, self.instructmsrs, nil, 1, {player.client:GetGroup()}, text, 10)
|
self.instructsrsQ:NewTransmission(text, nil, self.instructmsrs, nil, 1, {player.client:GetGroup()}, text, 10)
|
||||||
else
|
else
|
||||||
self.instructor:NewTransmission( RANGE.Sound.IRExitRange.filename, RANGE.Sound.IRExitRange.duration, self.soundpath )
|
self.instructor:NewTransmission( RANGE.Sound.IRExitRange.filename, RANGE.Sound.IRExitRange.duration, self.soundpath )
|
||||||
@ -2240,7 +2238,7 @@ function RANGE:onafterImpact( From, Event, To, result, player )
|
|||||||
text = text .. string.format( " %s hit.", result.quality )
|
text = text .. string.format( " %s hit.", result.quality )
|
||||||
|
|
||||||
if self.rangecontrol then
|
if self.rangecontrol then
|
||||||
|
|
||||||
if self.useSRS then
|
if self.useSRS then
|
||||||
local group = player.client:GetGroup()
|
local group = player.client:GetGroup()
|
||||||
self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1,{group},text,10)
|
self.controlsrsQ:NewTransmission(text,nil,self.controlmsrs,nil,1,{group},text,10)
|
||||||
@ -2265,10 +2263,10 @@ function RANGE:onafterImpact( From, Event, To, result, player )
|
|||||||
|
|
||||||
-- Unit.
|
-- Unit.
|
||||||
if player.unitname and not self.useSRS then
|
if player.unitname and not self.useSRS then
|
||||||
|
|
||||||
-- Get unit.
|
-- Get unit.
|
||||||
local unit = UNIT:FindByName( player.unitname )
|
local unit = UNIT:FindByName( player.unitname )
|
||||||
|
|
||||||
-- Send message.
|
-- Send message.
|
||||||
self:_DisplayMessageToGroup( unit, text, nil, true )
|
self:_DisplayMessageToGroup( unit, text, nil, true )
|
||||||
self:T( self.lid .. text )
|
self:T( self.lid .. text )
|
||||||
@ -2278,7 +2276,7 @@ function RANGE:onafterImpact( From, Event, To, result, player )
|
|||||||
if self.autosave then
|
if self.autosave then
|
||||||
self:Save()
|
self:Save()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Send result to FunkMan, which creates fancy MatLab figures and sends them to Discord via a bot.
|
-- Send result to FunkMan, which creates fancy MatLab figures and sends them to Discord via a bot.
|
||||||
if self.funkmanSocket then
|
if self.funkmanSocket then
|
||||||
self.funkmanSocket:SendTable(result)
|
self.funkmanSocket:SendTable(result)
|
||||||
@ -2547,7 +2545,7 @@ function RANGE:_DisplayMyStrafePitResults( _unitName )
|
|||||||
local _message = string.format( "My Top %d Strafe Pit Results:\n", self.ndisplayresult )
|
local _message = string.format( "My Top %d Strafe Pit Results:\n", self.ndisplayresult )
|
||||||
|
|
||||||
-- Get player results.
|
-- Get player results.
|
||||||
local _results = self.strafePlayerResults[_playername]
|
local _results = self.strafePlayerResults[_playername]
|
||||||
|
|
||||||
-- Create message.
|
-- Create message.
|
||||||
if _results == nil then
|
if _results == nil then
|
||||||
@ -2853,7 +2851,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
text = text .. string.format( "Instructor %.3f MHz (Relay=%s)\n", self.instructorfreq, alive )
|
text = text .. string.format( "Instructor %.3f MHz (Relay=%s)\n", self.instructorfreq, alive )
|
||||||
end
|
end
|
||||||
if self.rangecontrol then
|
if self.rangecontrol then
|
||||||
local alive = "N/A"
|
local alive = "N/A"
|
||||||
if self.rangecontrolrelayname then
|
if self.rangecontrolrelayname then
|
||||||
@ -3081,10 +3079,10 @@ function RANGE:_CheckInZone( _unitName )
|
|||||||
local unitheading = 0 -- RangeBoss
|
local unitheading = 0 -- RangeBoss
|
||||||
|
|
||||||
if _unit and _playername then
|
if _unit and _playername then
|
||||||
|
|
||||||
-- Player data.
|
-- Player data.
|
||||||
local playerData=self.PlayerSettings[_playername] -- #RANGE.PlayerData
|
local playerData=self.PlayerSettings[_playername] -- #RANGE.PlayerData
|
||||||
|
|
||||||
--- Function to check if unit is in zone and facing in the right direction and is below the max alt.
|
--- Function to check if unit is in zone and facing in the right direction and is below the max alt.
|
||||||
local function checkme( targetheading, _zone )
|
local function checkme( targetheading, _zone )
|
||||||
local zone = _zone -- Core.Zone#ZONE
|
local zone = _zone -- Core.Zone#ZONE
|
||||||
@ -3098,7 +3096,7 @@ function RANGE:_CheckInZone( _unitName )
|
|||||||
if towardspit then
|
if towardspit then
|
||||||
|
|
||||||
local vec3 = _unit:GetVec3()
|
local vec3 = _unit:GetVec3()
|
||||||
local vec2 = { x = vec3.x, y = vec3.z } -- DCS#Vec2
|
local vec2 = { x = vec3.x, y = vec3.z } -- DCS#Vec2
|
||||||
local landheight = land.getHeight( vec2 )
|
local landheight = land.getHeight( vec2 )
|
||||||
local unitalt = vec3.y - landheight
|
local unitalt = vec3.y - landheight
|
||||||
|
|
||||||
@ -3145,7 +3143,7 @@ function RANGE:_CheckInZone( _unitName )
|
|||||||
|
|
||||||
-- Send message.
|
-- Send message.
|
||||||
self:_DisplayMessageToGroup( _unit, _msg, nil, true )
|
self:_DisplayMessageToGroup( _unit, _msg, nil, true )
|
||||||
|
|
||||||
if self.rangecontrol then
|
if self.rangecontrol then
|
||||||
if self.useSRS then
|
if self.useSRS then
|
||||||
local group = _unit:GetGroup()
|
local group = _unit:GetGroup()
|
||||||
@ -3164,9 +3162,9 @@ function RANGE:_CheckInZone( _unitName )
|
|||||||
|
|
||||||
-- Result.
|
-- Result.
|
||||||
local _result = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
|
local _result = self.strafeStatus[_unitID] --#RANGE.StrafeStatus
|
||||||
|
|
||||||
local _sound = nil -- #RANGE.Soundfile
|
local _sound = nil -- #RANGE.Soundfile
|
||||||
|
|
||||||
-- Calculate accuracy of run. Number of hits wrt number of rounds fired.
|
-- Calculate accuracy of run. Number of hits wrt number of rounds fired.
|
||||||
local shots = _result.ammo - _ammo
|
local shots = _result.ammo - _ammo
|
||||||
local accur = 0
|
local accur = 0
|
||||||
@ -3176,7 +3174,7 @@ function RANGE:_CheckInZone( _unitName )
|
|||||||
accur = 100
|
accur = 100
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Results text and sound message.
|
-- Results text and sound message.
|
||||||
local resulttext=""
|
local resulttext=""
|
||||||
if _result.pastfoulline == true then --
|
if _result.pastfoulline == true then --
|
||||||
@ -3213,7 +3211,7 @@ function RANGE:_CheckInZone( _unitName )
|
|||||||
|
|
||||||
-- Send message.
|
-- Send message.
|
||||||
self:_DisplayMessageToGroup( _unit, _text )
|
self:_DisplayMessageToGroup( _unit, _text )
|
||||||
|
|
||||||
-- Strafe result.
|
-- Strafe result.
|
||||||
local result = {} -- #RANGE.StrafeResult
|
local result = {} -- #RANGE.StrafeResult
|
||||||
result.command=SOCKET.DataType.STRAFERESULT
|
result.command=SOCKET.DataType.STRAFERESULT
|
||||||
@ -3230,14 +3228,14 @@ function RANGE:_CheckInZone( _unitName )
|
|||||||
result.rangename = self.rangename
|
result.rangename = self.rangename
|
||||||
result.airframe=playerData.airframe
|
result.airframe=playerData.airframe
|
||||||
result.invalid = _result.pastfoulline
|
result.invalid = _result.pastfoulline
|
||||||
|
|
||||||
-- Griger Results.
|
-- Griger Results.
|
||||||
self:StrafeResult(playerData, result)
|
self:StrafeResult(playerData, result)
|
||||||
|
|
||||||
-- Save trap sheet.
|
-- Save trap sheet.
|
||||||
if playerData and playerData.targeton and self.targetsheet then
|
if playerData and playerData.targeton and self.targetsheet then
|
||||||
self:_SaveTargetSheet( _playername, result )
|
self:_SaveTargetSheet( _playername, result )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Voice over.
|
-- Voice over.
|
||||||
if self.rangecontrol then
|
if self.rangecontrol then
|
||||||
@ -3302,7 +3300,7 @@ function RANGE:_CheckInZone( _unitName )
|
|||||||
|
|
||||||
-- Send message.
|
-- Send message.
|
||||||
self:_DisplayMessageToGroup( _unit, _msg, 10, true )
|
self:_DisplayMessageToGroup( _unit, _msg, 10, true )
|
||||||
|
|
||||||
-- Trigger event that player is rolling in.
|
-- Trigger event that player is rolling in.
|
||||||
self:RollingIn(playerData, target)
|
self:RollingIn(playerData, target)
|
||||||
|
|
||||||
@ -3438,18 +3436,18 @@ function RANGE:_GetBombTargetCoordinate( target )
|
|||||||
local coord = nil -- Core.Point#COORDINATE
|
local coord = nil -- Core.Point#COORDINATE
|
||||||
|
|
||||||
if target.type == RANGE.TargetType.UNIT then
|
if target.type == RANGE.TargetType.UNIT then
|
||||||
|
|
||||||
-- Check if alive
|
-- Check if alive
|
||||||
if target.target and target.target:IsAlive() then
|
if target.target and target.target:IsAlive() then
|
||||||
-- Get current position.
|
-- Get current position.
|
||||||
coord = target.target:GetCoordinate()
|
coord = target.target:GetCoordinate()
|
||||||
-- Save as last known position in case target dies.
|
-- Save as last known position in case target dies.
|
||||||
target.coordinate=coord
|
target.coordinate=coord
|
||||||
else
|
else
|
||||||
-- Use stored position.
|
-- Use stored position.
|
||||||
coord = target.coordinate
|
coord = target.coordinate
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif target.type == RANGE.TargetType.STATIC then
|
elseif target.type == RANGE.TargetType.STATIC then
|
||||||
|
|
||||||
-- Static targets dont move.
|
-- Static targets dont move.
|
||||||
@ -3459,11 +3457,11 @@ function RANGE:_GetBombTargetCoordinate( target )
|
|||||||
|
|
||||||
-- Coordinates dont move.
|
-- Coordinates dont move.
|
||||||
coord = target.coordinate
|
coord = target.coordinate
|
||||||
|
|
||||||
elseif target.type == RANGE.TargetType.SCENERY then
|
elseif target.type == RANGE.TargetType.SCENERY then
|
||||||
|
|
||||||
-- Coordinates dont move.
|
-- Coordinates dont move.
|
||||||
coord = target.coordinate
|
coord = target.coordinate
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E( self.lid .. "ERROR: Unknown target type." )
|
self:E( self.lid .. "ERROR: Unknown target type." )
|
||||||
@ -3670,7 +3668,7 @@ function RANGE:_DisplayMessageToGroup( _unit, _text, _time, _clear, display, _to
|
|||||||
local playermessage = self.PlayerSettings[playername].messages
|
local playermessage = self.PlayerSettings[playername].messages
|
||||||
|
|
||||||
-- Send message to player if messages enabled and not only for the examiner.
|
-- Send message to player if messages enabled and not only for the examiner.
|
||||||
|
|
||||||
if _gid and (playermessage == true or display) and (not self.examinerexclusive) then
|
if _gid and (playermessage == true or display) and (not self.examinerexclusive) then
|
||||||
if _togroup and _grp then
|
if _togroup and _grp then
|
||||||
local m = MESSAGE:New(_text,_time,nil,_clear):ToGroup(_grp)
|
local m = MESSAGE:New(_text,_time,nil,_clear):ToGroup(_grp)
|
||||||
@ -4025,9 +4023,9 @@ function RANGE:_GetPlayerUnitAndName( _unitName )
|
|||||||
self:F2( _unitName )
|
self:F2( _unitName )
|
||||||
|
|
||||||
if _unitName ~= nil then
|
if _unitName ~= nil then
|
||||||
|
|
||||||
local multiplayer = false
|
local multiplayer = false
|
||||||
|
|
||||||
-- Get DCS unit from its name.
|
-- Get DCS unit from its name.
|
||||||
local DCSunit = Unit.getByName( _unitName )
|
local DCSunit = Unit.getByName( _unitName )
|
||||||
|
|
||||||
@ -4066,7 +4064,7 @@ function RANGE:_myname( unitname )
|
|||||||
if grp and grp:IsAlive() then
|
if grp and grp:IsAlive() then
|
||||||
pname = grp:GetCustomCallSign(true,true)
|
pname = grp:GetCustomCallSign(true,true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return pname
|
return pname
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
--
|
--
|
||||||
-- @module Functional.Stratego
|
-- @module Functional.Stratego
|
||||||
-- @image Functional.Stratego.png
|
-- @image Functional.Stratego.png
|
||||||
-- Last Update April 2024
|
-- Last Update May 2024
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -42,6 +42,7 @@
|
|||||||
-- @field #boolean usebudget
|
-- @field #boolean usebudget
|
||||||
-- @field #number CaptureUnits
|
-- @field #number CaptureUnits
|
||||||
-- @field #number CaptureThreatlevel
|
-- @field #number CaptureThreatlevel
|
||||||
|
-- @field #table CaptureObjectCategories
|
||||||
-- @field #boolean ExcludeShips
|
-- @field #boolean ExcludeShips
|
||||||
-- @field Core.Zone#ZONE StrategoZone
|
-- @field Core.Zone#ZONE StrategoZone
|
||||||
-- @extends Core.Base#BASE
|
-- @extends Core.Base#BASE
|
||||||
@ -180,7 +181,7 @@ STRATEGO = {
|
|||||||
debug = false,
|
debug = false,
|
||||||
drawzone = false,
|
drawzone = false,
|
||||||
markzone = false,
|
markzone = false,
|
||||||
version = "0.2.7",
|
version = "0.3.1",
|
||||||
portweight = 3,
|
portweight = 3,
|
||||||
POIweight = 1,
|
POIweight = 1,
|
||||||
maxrunways = 3,
|
maxrunways = 3,
|
||||||
@ -199,6 +200,7 @@ STRATEGO = {
|
|||||||
usebudget = false,
|
usebudget = false,
|
||||||
CaptureUnits = 3,
|
CaptureUnits = 3,
|
||||||
CaptureThreatlevel = 1,
|
CaptureThreatlevel = 1,
|
||||||
|
CaptureObjectCategories = {Object.Category.UNIT},
|
||||||
ExcludeShips = true,
|
ExcludeShips = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,9 +212,10 @@ STRATEGO = {
|
|||||||
-- @field #number coalition
|
-- @field #number coalition
|
||||||
-- @field #boolean port
|
-- @field #boolean port
|
||||||
-- @field Core.Zone#ZONE_RADIUS zone,
|
-- @field Core.Zone#ZONE_RADIUS zone,
|
||||||
-- @field Core.Point#COORDINATRE coord
|
-- @field Core.Point#COORDINATE coord
|
||||||
-- @field #string type
|
-- @field #string type
|
||||||
-- @field Ops.OpsZone#OPSZONE opszone
|
-- @field Ops.OpsZone#OPSZONE opszone
|
||||||
|
-- @field #number connections
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @type STRATEGO.DistData
|
-- @type STRATEGO.DistData
|
||||||
@ -419,11 +422,13 @@ end
|
|||||||
-- @param #STRATEGO self
|
-- @param #STRATEGO self
|
||||||
-- @param #number CaptureUnits Number of units needed, defaults to three.
|
-- @param #number CaptureUnits Number of units needed, defaults to three.
|
||||||
-- @param #number CaptureThreatlevel Threat level needed, can be 0..10, defaults to one.
|
-- @param #number CaptureThreatlevel Threat level needed, can be 0..10, defaults to one.
|
||||||
|
-- @param #table CaptureCategories Table of object categories which can capture a node, defaults to `{Object.Category.UNIT}`.
|
||||||
-- @return #STRATEGO self
|
-- @return #STRATEGO self
|
||||||
function STRATEGO:SetCaptureOptions(CaptureUnits,CaptureThreatlevel)
|
function STRATEGO:SetCaptureOptions(CaptureUnits,CaptureThreatlevel,CaptureCategories)
|
||||||
self:T(self.lid.."SetCaptureOptions")
|
self:T(self.lid.."SetCaptureOptions")
|
||||||
self.CaptureUnits = CaptureUnits or 3
|
self.CaptureUnits = CaptureUnits or 3
|
||||||
self.CaptureThreatlevel = CaptureThreatlevel or 1
|
self.CaptureThreatlevel = CaptureThreatlevel or 1
|
||||||
|
self.CaptureObjectCategories = CaptureCategories or {Object.Category.UNIT}
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -486,6 +491,7 @@ function STRATEGO:AnalyseBases()
|
|||||||
coord = coord,
|
coord = coord,
|
||||||
type = abtype,
|
type = abtype,
|
||||||
opszone = opszone,
|
opszone = opszone,
|
||||||
|
connections = 0,
|
||||||
}
|
}
|
||||||
airbasetable[abname] = tbl
|
airbasetable[abname] = tbl
|
||||||
nonconnectedab[abname] = true
|
nonconnectedab[abname] = true
|
||||||
@ -526,6 +532,7 @@ function STRATEGO:GetNewOpsZone(Zone,Coalition)
|
|||||||
local opszone = OPSZONE:New(Zone,Coalition or 0)
|
local opszone = OPSZONE:New(Zone,Coalition or 0)
|
||||||
opszone:SetCaptureNunits(self.CaptureUnits)
|
opszone:SetCaptureNunits(self.CaptureUnits)
|
||||||
opszone:SetCaptureThreatlevel(self.CaptureThreatlevel)
|
opszone:SetCaptureThreatlevel(self.CaptureThreatlevel)
|
||||||
|
opszone:SetObjectCategories(self.CaptureObjectCategories)
|
||||||
opszone:SetDrawZone(self.drawzone)
|
opszone:SetDrawZone(self.drawzone)
|
||||||
opszone:SetMarkZone(self.markzone)
|
opszone:SetMarkZone(self.markzone)
|
||||||
opszone:Start()
|
opszone:Start()
|
||||||
@ -571,10 +578,12 @@ function STRATEGO:AnalysePOIs(Set,Weight,Key)
|
|||||||
coord = coord,
|
coord = coord,
|
||||||
type = Key,
|
type = Key,
|
||||||
opszone = opszone,
|
opszone = opszone,
|
||||||
|
connections = 0,
|
||||||
}
|
}
|
||||||
airbasetable[zone:GetName()] = tbl
|
airbasetable[zname] = tbl
|
||||||
nonconnectedab[zone:GetName()] = true
|
nonconnectedab[zname] = true
|
||||||
local name = string.gsub(zname,"[%p%s]",".")
|
local name = string.gsub(zname,"[%p%s]",".")
|
||||||
|
--self:I({name=name,zone=zname})
|
||||||
easynames[name]=zname
|
easynames[name]=zname
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
@ -585,7 +594,7 @@ end
|
|||||||
-- @param #STRATEGO self
|
-- @param #STRATEGO self
|
||||||
-- @return #STRATEGO self
|
-- @return #STRATEGO self
|
||||||
function STRATEGO:GetToFrom(StartPoint,EndPoint)
|
function STRATEGO:GetToFrom(StartPoint,EndPoint)
|
||||||
self:T(self.lid.."GetToFrom")
|
self:T(self.lid.."GetToFrom "..tostring(StartPoint).." "..tostring(EndPoint))
|
||||||
local pstart = string.gsub(StartPoint,"[%p%s]",".")
|
local pstart = string.gsub(StartPoint,"[%p%s]",".")
|
||||||
local pend = string.gsub(EndPoint,"[%p%s]",".")
|
local pend = string.gsub(EndPoint,"[%p%s]",".")
|
||||||
local fromto = pstart..";"..pend
|
local fromto = pstart..";"..pend
|
||||||
@ -593,11 +602,35 @@ function STRATEGO:GetToFrom(StartPoint,EndPoint)
|
|||||||
return fromto, tofrom
|
return fromto, tofrom
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [USER] Get available connecting nodes from one start node
|
||||||
|
-- @param #STRATEGO self
|
||||||
|
-- @param #string StartPoint The starting name
|
||||||
|
-- @return #boolean found
|
||||||
|
-- @return #table Nodes
|
||||||
|
function STRATEGO:GetRoutesFromNode(StartPoint)
|
||||||
|
self:T(self.lid.."GetRoutesFromNode")
|
||||||
|
local pstart = string.gsub(StartPoint,"[%p%s]",".")
|
||||||
|
local found = false
|
||||||
|
pstart=pstart..";"
|
||||||
|
local routes = {}
|
||||||
|
local listed = {}
|
||||||
|
for _,_data in pairs(self.routexists) do
|
||||||
|
if string.find(_data,pstart,1,true) and not listed[_data] then
|
||||||
|
local target = string.gsub(_data,pstart,"")
|
||||||
|
local fname = self.easynames[target]
|
||||||
|
table.insert(routes,fname)
|
||||||
|
found = true
|
||||||
|
listed[_data] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return found,routes
|
||||||
|
end
|
||||||
|
|
||||||
--- [USER] Manually add a route, for e.g. Island hopping or to connect isolated networks. Use **after** STRATEGO has been started!
|
--- [USER] Manually add a route, for e.g. Island hopping or to connect isolated networks. Use **after** STRATEGO has been started!
|
||||||
-- @param #STRATEGO self
|
-- @param #STRATEGO self
|
||||||
-- @param #string Startpoint Starting Point, e.g. AIRBASE.Syria.Hatay
|
-- @param #string Startpoint Starting Point, e.g. AIRBASE.Syria.Hatay
|
||||||
-- @param #string Endpoint End Point, e.g. AIRBASE.Syria.H4
|
-- @param #string Endpoint End Point, e.g. AIRBASE.Syria.H4
|
||||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to lila.
|
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to violet.
|
||||||
-- @param #number Linetype (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 5.
|
-- @param #number Linetype (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 5.
|
||||||
-- @param #boolean Draw (Optional) If true, draw route on the F10 map. Defaukt false.
|
-- @param #boolean Draw (Optional) If true, draw route on the F10 map. Defaukt false.
|
||||||
-- @return #STRATEGO self
|
-- @return #STRATEGO self
|
||||||
@ -625,6 +658,8 @@ function STRATEGO:AddRoutesManually(Startpoint,Endpoint,Color,Linetype,Draw)
|
|||||||
local factor = self.airbasetable[Startpoint].baseweight*self.routefactor
|
local factor = self.airbasetable[Startpoint].baseweight*self.routefactor
|
||||||
self.airbasetable[Startpoint].weight = self.airbasetable[Startpoint].weight+factor
|
self.airbasetable[Startpoint].weight = self.airbasetable[Startpoint].weight+factor
|
||||||
self.airbasetable[Endpoint].weight = self.airbasetable[Endpoint].weight+factor
|
self.airbasetable[Endpoint].weight = self.airbasetable[Endpoint].weight+factor
|
||||||
|
self.airbasetable[Endpoint].connections = self.airbasetable[Endpoint].connections + 2
|
||||||
|
self.airbasetable[Startpoint].connections = self.airbasetable[Startpoint].connections+2
|
||||||
if self.debug or Draw then
|
if self.debug or Draw then
|
||||||
startcoordinate:LineToAll(targetcoordinate,-1,color,1,linetype,nil,string.format("%dkm",dist))
|
startcoordinate:LineToAll(targetcoordinate,-1,color,1,linetype,nil,string.format("%dkm",dist))
|
||||||
end
|
end
|
||||||
@ -643,7 +678,7 @@ function STRATEGO:AnalyseRoutes(tgtrwys,factor,color,linetype)
|
|||||||
for _,_data in pairs(self.airbasetable) do
|
for _,_data in pairs(self.airbasetable) do
|
||||||
local fromto,tofrom = self:GetToFrom(startpoint,_data.name)
|
local fromto,tofrom = self:GetToFrom(startpoint,_data.name)
|
||||||
if _data.name == startpoint then
|
if _data.name == startpoint then
|
||||||
-- sam as we
|
-- same as we
|
||||||
elseif _data.baseweight == tgtrwys and not (self.routexists[fromto] or self.routexists[tofrom]) then
|
elseif _data.baseweight == tgtrwys and not (self.routexists[fromto] or self.routexists[tofrom]) then
|
||||||
local tgtc = _data.coord
|
local tgtc = _data.coord
|
||||||
local dist = UTILS.Round(tgtc:Get2DDistance(startcoord),-2)/1000
|
local dist = UTILS.Round(tgtc:Get2DDistance(startcoord),-2)/1000
|
||||||
@ -665,6 +700,8 @@ function STRATEGO:AnalyseRoutes(tgtrwys,factor,color,linetype)
|
|||||||
self.nonconnectedab[startpoint] = false
|
self.nonconnectedab[startpoint] = false
|
||||||
self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+factor
|
self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+factor
|
||||||
self.airbasetable[_data.name].weight = self.airbasetable[_data.name].weight+factor
|
self.airbasetable[_data.name].weight = self.airbasetable[_data.name].weight+factor
|
||||||
|
self.airbasetable[startpoint].connections = self.airbasetable[startpoint].connections + 1
|
||||||
|
self.airbasetable[_data.name].connections = self.airbasetable[_data.name].connections + 1
|
||||||
if self.debug then
|
if self.debug then
|
||||||
startcoord:LineToAll(tgtc,-1,color,1,linetype,nil,string.format("%dkm",dist))
|
startcoord:LineToAll(tgtc,-1,color,1,linetype,nil,string.format("%dkm",dist))
|
||||||
end
|
end
|
||||||
@ -711,6 +748,8 @@ function STRATEGO:AnalyseUnconnected(Color)
|
|||||||
end
|
end
|
||||||
self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+1
|
self.airbasetable[startpoint].weight = self.airbasetable[startpoint].weight+1
|
||||||
self.airbasetable[closest].weight = self.airbasetable[closest].weight+1
|
self.airbasetable[closest].weight = self.airbasetable[closest].weight+1
|
||||||
|
self.airbasetable[startpoint].connections = self.airbasetable[startpoint].connections+2
|
||||||
|
self.airbasetable[closest].connections = self.airbasetable[closest].connections+2
|
||||||
local data = {
|
local data = {
|
||||||
start = startpoint,
|
start = startpoint,
|
||||||
target = closest,
|
target = closest,
|
||||||
@ -727,14 +766,50 @@ function STRATEGO:AnalyseUnconnected(Color)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
function STRATEGO:PruneDeadEnds(abtable)
|
||||||
|
local found = false
|
||||||
|
local newtable = {}
|
||||||
|
for name, _data in pairs(abtable) do
|
||||||
|
local data = _data -- #STRATEGO.Data
|
||||||
|
if data.connections > 2 then
|
||||||
|
newtable[name] = data
|
||||||
|
else
|
||||||
|
-- dead end
|
||||||
|
found = true
|
||||||
|
local neighbors, nearest, distance = self:FindNeighborNodes(name)
|
||||||
|
--self:I("Pruning "..name)
|
||||||
|
if nearest then
|
||||||
|
for _name,_ in pairs(neighbors) do
|
||||||
|
local abname = self.easynames[_name] or _name
|
||||||
|
--self:I({easyname=_name,airbasename=abname})
|
||||||
|
if abtable[abname] then
|
||||||
|
abtable[abname].connections = abtable[abname].connections -1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self.debug then
|
||||||
|
data.coord:CircleToAll(5000,-1,{1,1,1},1,{1,1,1},1,3,true,"Dead End")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
abtable = nil
|
||||||
|
return found,newtable
|
||||||
|
end
|
||||||
|
--]]
|
||||||
|
|
||||||
--- [USER] Get a list of the nodes with the highest weight.
|
--- [USER] Get a list of the nodes with the highest weight.
|
||||||
-- @param #STRATEGO self
|
-- @param #STRATEGO self
|
||||||
-- @param #number Coalition (Optional) Find for this coalition only. E.g. coalition.side.BLUE.
|
-- @param #number Coalition (Optional) Find for this coalition only. E.g. coalition.side.BLUE.
|
||||||
-- @return #table Table of nodes.
|
-- @return #table Table of nodes.
|
||||||
-- @return #number Weight The consolidated weight associated with the nodes.
|
-- @return #number Weight The consolidated weight associated with the nodes.
|
||||||
|
-- @return #number Highest Highest weight found.
|
||||||
|
-- @return #string Name of the node with the highest weight.
|
||||||
function STRATEGO:GetHighestWeightNodes(Coalition)
|
function STRATEGO:GetHighestWeightNodes(Coalition)
|
||||||
self:T(self.lid.."GetHighestWeightNodes")
|
self:T(self.lid.."GetHighestWeightNodes")
|
||||||
local weight = 0
|
local weight = 0
|
||||||
|
local highest = 0
|
||||||
|
local highname = nil
|
||||||
local airbases = {}
|
local airbases = {}
|
||||||
for _name,_data in pairs(self.airbasetable) do
|
for _name,_data in pairs(self.airbasetable) do
|
||||||
local okay = true
|
local okay = true
|
||||||
@ -748,8 +823,12 @@ function STRATEGO:GetHighestWeightNodes(Coalition)
|
|||||||
if not airbases[weight] then airbases[weight]={} end
|
if not airbases[weight] then airbases[weight]={} end
|
||||||
table.insert(airbases[weight],_name)
|
table.insert(airbases[weight],_name)
|
||||||
end
|
end
|
||||||
|
if _data.weight > highest and okay then
|
||||||
|
highest = _data.weight
|
||||||
|
highname = _name
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return airbases[weight],weight
|
return airbases[weight],weight,highest,highname
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [USER] Get a list of the nodes a weight less than the given parameter.
|
--- [USER] Get a list of the nodes a weight less than the given parameter.
|
||||||
@ -1136,35 +1215,67 @@ function STRATEGO:FindNeighborNodes(Name,Enemies,Friends)
|
|||||||
self:T(self.lid.."FindNeighborNodes")
|
self:T(self.lid.."FindNeighborNodes")
|
||||||
local neighbors = {}
|
local neighbors = {}
|
||||||
local name = string.gsub(Name,"[%p%s]",".")
|
local name = string.gsub(Name,"[%p%s]",".")
|
||||||
|
--self:I({Name=Name,name=name})
|
||||||
local shortestdist = 1000*1000
|
local shortestdist = 1000*1000
|
||||||
local nearest = nil
|
local nearest = nil
|
||||||
for _route,_data in pairs(self.disttable) do
|
for _route,_data in pairs(self.disttable) do
|
||||||
if string.find(_route,name,1,true) then
|
if string.find(_route,name,1,true) then
|
||||||
local dist = self.disttable[_route] -- #STRATEGO.DistData
|
local dist = self.disttable[_route] -- #STRATEGO.DistData
|
||||||
|
--self:I({route=_route,name=name})
|
||||||
local tname = string.gsub(_route,name,"")
|
local tname = string.gsub(_route,name,"")
|
||||||
local tname = string.gsub(tname,";","")
|
local tname = string.gsub(tname,";","")
|
||||||
|
--self:I({tname=tname,cname=self.easynames[tname]})
|
||||||
local cname = self.easynames[tname] -- name of target
|
local cname = self.easynames[tname] -- name of target
|
||||||
local encoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE
|
if cname then
|
||||||
if Enemies == true then
|
local encoa = self.coalition == coalition.side.BLUE and coalition.side.RED or coalition.side.BLUE
|
||||||
if self.airbasetable[cname].coalition == encoa then
|
if Enemies == true then
|
||||||
neighbors[cname] = dist
|
if self.airbasetable[cname].coalition == encoa then
|
||||||
|
neighbors[cname] = dist
|
||||||
|
end
|
||||||
|
elseif Friends == true then
|
||||||
|
if self.airbasetable[cname].coalition ~= encoa then
|
||||||
|
neighbors[cname] = dist
|
||||||
|
end
|
||||||
|
else
|
||||||
|
neighbors[cname] = dist
|
||||||
end
|
end
|
||||||
elseif Friends == true then
|
if neighbors[cname] and dist.dist < shortestdist then
|
||||||
if self.airbasetable[cname].coalition ~= encoa then
|
shortestdist = dist.dist
|
||||||
neighbors[cname] = dist
|
nearest = cname
|
||||||
end
|
end
|
||||||
else
|
|
||||||
neighbors[cname] = dist
|
|
||||||
end
|
|
||||||
if neighbors[cname] and dist.dist < shortestdist then
|
|
||||||
shortestdist = dist.dist
|
|
||||||
nearest = cname
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return neighbors, nearest, shortestdist
|
return neighbors, nearest, shortestdist
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [INTERNAL] Route Finding - Find the next hop towards an end node from a start node
|
||||||
|
-- @param #STRATEGO self
|
||||||
|
-- @param #string Start The name of the start node.
|
||||||
|
-- @param #string End The name of the end node.
|
||||||
|
-- @param #table InRoute Table of node names making up the route so far.
|
||||||
|
-- @return #string Name of the next closest node
|
||||||
|
function STRATEGO:_GetNextClosest(Start,End,InRoute)
|
||||||
|
local ecoord = self.airbasetable[End].coord
|
||||||
|
local nodes,nearest = self:FindNeighborNodes(Start)
|
||||||
|
--self:I(tostring(nearest))
|
||||||
|
local closest = nil
|
||||||
|
local closedist = 1000*1000
|
||||||
|
for _name,_dist in pairs(nodes) do
|
||||||
|
local kcoord = self.airbasetable[_name].coord
|
||||||
|
local nnodes = self.airbasetable[_name].connections > 2 and true or false
|
||||||
|
if _name == End then nnodes = true end
|
||||||
|
if kcoord ~= nil and ecoord ~= nil and nnodes == true and InRoute[_name] ~= true then
|
||||||
|
local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5)
|
||||||
|
if (dist < closedist ) then
|
||||||
|
closedist = dist
|
||||||
|
closest = _name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return closest
|
||||||
|
end
|
||||||
|
|
||||||
--- [USER] Find a route between two nodes.
|
--- [USER] Find a route between two nodes.
|
||||||
-- @param #STRATEGO self
|
-- @param #STRATEGO self
|
||||||
-- @param #string Start The name of the start node.
|
-- @param #string Start The name of the start node.
|
||||||
@ -1173,15 +1284,19 @@ end
|
|||||||
-- @param #boolean Draw If true, draw the route on the map.
|
-- @param #boolean Draw If true, draw the route on the map.
|
||||||
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to black.
|
-- @param #table Color (Optional) RGB color table {r, g, b}, e.g. {1,0,0} for red. Defaults to black.
|
||||||
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 6.
|
-- @param #number LineType (Optional) Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 6.
|
||||||
|
-- @param #boolean NoOptimize If set to true, do not optimize (shorten) the resulting route if possible.
|
||||||
-- @return #table Route Table of #string name entries of the route
|
-- @return #table Route Table of #string name entries of the route
|
||||||
-- @return #boolean Complete If true, the route was found end-to-end.
|
-- @return #boolean Complete If true, the route was found end-to-end.
|
||||||
function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType)
|
-- @return #boolean Reverse If true, the route was found with a reverse search, the route table will be from sorted from end point to start point.
|
||||||
|
function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType,NoOptimize)
|
||||||
self:T(self.lid.."FindRoute")
|
self:T(self.lid.."FindRoute")
|
||||||
--self:I({Start,End,Hops})
|
--self:I({Start,End,Hops})
|
||||||
--local bases = UTILS.DeepCopy(self.airbasetable)
|
--local bases = UTILS.DeepCopy(self.airbasetable)
|
||||||
local Route = {}
|
local Route = {}
|
||||||
|
local InRoute = {}
|
||||||
local hops = Hops or 4
|
local hops = Hops or 4
|
||||||
local routecomplete = false
|
local routecomplete = false
|
||||||
|
local reverse = false
|
||||||
|
|
||||||
local function Checker(neighbors)
|
local function Checker(neighbors)
|
||||||
for _name,_data in pairs(neighbors) do
|
for _name,_data in pairs(neighbors) do
|
||||||
@ -1192,26 +1307,7 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType)
|
|||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local function NextClosest(Start,End)
|
|
||||||
local ecoord = self.airbasetable[End].coord
|
|
||||||
local nodes = self:FindNeighborNodes(Start)
|
|
||||||
local closest = nil
|
|
||||||
local closedist = 1000*1000
|
|
||||||
for _name,_dist in pairs(nodes) do
|
|
||||||
local kcoord = self.airbasetable[_name].coord
|
|
||||||
local dist = math.floor((kcoord:Get2DDistance(ecoord)/1000)+0.5)
|
|
||||||
if dist < closedist then
|
|
||||||
closedist = dist
|
|
||||||
closest = _name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if closest then
|
|
||||||
--MESSAGE:New(string.format("Start %s | End %s | Nextclosest %s",Start,End,closest),10,"STRATEGO"):ToLog():ToAll()
|
|
||||||
return closest
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function DrawRoute(Route)
|
local function DrawRoute(Route)
|
||||||
for i=1,#Route-1 do
|
for i=1,#Route-1 do
|
||||||
local p1=Route[i]
|
local p1=Route[i]
|
||||||
@ -1226,6 +1322,7 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType)
|
|||||||
|
|
||||||
-- One hop
|
-- One hop
|
||||||
Route[#Route+1] = Start
|
Route[#Route+1] = Start
|
||||||
|
InRoute[Start] = true
|
||||||
local nodes = self:FindNeighborNodes(Start)
|
local nodes = self:FindNeighborNodes(Start)
|
||||||
local endpoint = Checker(nodes)
|
local endpoint = Checker(nodes)
|
||||||
|
|
||||||
@ -1235,9 +1332,11 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType)
|
|||||||
else
|
else
|
||||||
local spoint = Start
|
local spoint = Start
|
||||||
for i=1,hops do
|
for i=1,hops do
|
||||||
local Next = NextClosest(spoint,End)
|
--self:I("Start="..tostring(spoint))
|
||||||
if Next then
|
local Next = self:_GetNextClosest(spoint,End,InRoute)
|
||||||
|
if Next ~= nil then
|
||||||
Route[#Route+1] = Next
|
Route[#Route+1] = Next
|
||||||
|
InRoute[Next] = true
|
||||||
local nodes = self:FindNeighborNodes(Next)
|
local nodes = self:FindNeighborNodes(Next)
|
||||||
local endpoint = Checker(nodes)
|
local endpoint = Checker(nodes)
|
||||||
if endpoint then
|
if endpoint then
|
||||||
@ -1247,11 +1346,59 @@ function STRATEGO:FindRoute(Start,End,Hops,Draw,Color,LineType)
|
|||||||
else
|
else
|
||||||
spoint = Next
|
spoint = Next
|
||||||
end
|
end
|
||||||
end
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if (self.debug or Draw) then DrawRoute(Route) end
|
|
||||||
return Route, routecomplete
|
-- optimize route
|
||||||
|
local function OptimizeRoute(Route)
|
||||||
|
local foundcut = false
|
||||||
|
local largestcut = 0
|
||||||
|
local cut = {}
|
||||||
|
for i=1,#Route do
|
||||||
|
--self:I({Start=Route[i]})
|
||||||
|
local found,nodes = self:GetRoutesFromNode(Route[i])
|
||||||
|
for _,_name in pairs(nodes or {}) do
|
||||||
|
for j=i+2,#Route do
|
||||||
|
if _name == Route[j] then
|
||||||
|
--self:I({"Shortcut",Route[i],Route[j]})
|
||||||
|
if j-i > largestcut then
|
||||||
|
largestcut = j-i
|
||||||
|
cut = {i=i,j=j}
|
||||||
|
foundcut = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if foundcut then
|
||||||
|
local newroute = {}
|
||||||
|
for i=1,#Route do
|
||||||
|
if i<= cut.i or i>=cut.j then
|
||||||
|
table.insert(newroute,Route[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return newroute
|
||||||
|
end
|
||||||
|
return Route, foundcut
|
||||||
|
end
|
||||||
|
|
||||||
|
if routecomplete == true and NoOptimize ~= true then
|
||||||
|
local foundcut = true
|
||||||
|
while foundcut ~= false do
|
||||||
|
Route, foundcut = OptimizeRoute(Route)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- reverse search
|
||||||
|
Route, routecomplete = self:FindRoute(End,Start,Hops,Draw,Color,LineType)
|
||||||
|
reverse = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if (self.debug or Draw) then DrawRoute(Route) end
|
||||||
|
|
||||||
|
return Route, routecomplete, reverse
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [USER] Add budget points.
|
--- [USER] Add budget points.
|
||||||
@ -1358,6 +1505,139 @@ function STRATEGO:FindAffordableConsolidationTarget()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [INTERNAL] Internal helper function to check for islands, aka Floodtest
|
||||||
|
-- @param #STRATEGO self
|
||||||
|
-- @param #string next Name of the start node
|
||||||
|
-- @param #table filled #table of visited nodes
|
||||||
|
-- @param #table unfilled #table if unvisited nodes
|
||||||
|
-- @return #STRATEGO self
|
||||||
|
function STRATEGO:_FloodNext(next,filled,unfilled)
|
||||||
|
local start = self:FindNeighborNodes(next)
|
||||||
|
for _name,_ in pairs (start) do
|
||||||
|
if filled[_name] ~= true then
|
||||||
|
self:T("Flooding ".._name)
|
||||||
|
filled[_name] = true
|
||||||
|
unfilled[_name] = nil
|
||||||
|
self:_FloodNext(_name,filled,unfilled)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [INTERNAL] Internal helper function to check for islands, aka Floodtest
|
||||||
|
-- @param #STRATEGO self
|
||||||
|
-- @param #string Start Name of the start node
|
||||||
|
-- @param #table ABTable (Optional) #table of node names to check.
|
||||||
|
-- @return #STRATEGO self
|
||||||
|
function STRATEGO:_FloodFill(Start,ABTable)
|
||||||
|
self:T("Start = "..tostring(Start))
|
||||||
|
if Start == nil then return end
|
||||||
|
local filled = {}
|
||||||
|
local unfilled = {}
|
||||||
|
if ABTable then
|
||||||
|
unfilled = ABTable
|
||||||
|
else
|
||||||
|
for _name,_ in pairs(self.airbasetable) do
|
||||||
|
unfilled[_name] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
filled[Start] = true
|
||||||
|
unfilled[Start] = nil
|
||||||
|
local start = self:FindNeighborNodes(Start)
|
||||||
|
for _name,_ in pairs (start) do
|
||||||
|
if filled[_name] ~= true then
|
||||||
|
self:T("Flooding ".._name)
|
||||||
|
filled[_name] = true
|
||||||
|
unfilled[_name] = nil
|
||||||
|
self:_FloodNext(_name,filled,unfilled)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return filled, unfilled
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [INTERNAL] Internal helper function to check for islands, aka Floodtest
|
||||||
|
-- @param #STRATEGO self
|
||||||
|
-- @param #boolen connect If true, connect the two resulting islands at the shortest distance if necessary
|
||||||
|
-- @param #boolen draw If true, draw outer vertices of found node networks
|
||||||
|
-- @return #boolean Connected If true, all nodes are in one network
|
||||||
|
-- @return #table Network #table of node names in the network
|
||||||
|
-- @return #table Unconnected #table of node names **not** in the network
|
||||||
|
function STRATEGO:_FloodTest(connect,draw)
|
||||||
|
|
||||||
|
local function GetElastic(bases)
|
||||||
|
local vec2table = {}
|
||||||
|
for _name,_ in pairs(bases) do
|
||||||
|
local coord = self.airbasetable[_name].coord
|
||||||
|
local vec2 = coord:GetVec2()
|
||||||
|
table.insert(vec2table,vec2)
|
||||||
|
end
|
||||||
|
local zone = ZONE_ELASTIC:New("STRATEGO-Floodtest-"..math.random(1,10000),vec2table)
|
||||||
|
return zone
|
||||||
|
end
|
||||||
|
|
||||||
|
local function DrawElastic(filled,drawit)
|
||||||
|
local zone = GetElastic(filled)
|
||||||
|
if drawit then
|
||||||
|
zone:SetColor({1,1,1},1)
|
||||||
|
zone:SetDrawCoalition(-1)
|
||||||
|
zone:Update(1,true) -- draw zone
|
||||||
|
end
|
||||||
|
return zone
|
||||||
|
end
|
||||||
|
|
||||||
|
local _,_,weight,name = self:GetHighestWeightNodes()
|
||||||
|
local filled, unfilled = self:_FloodFill(name)
|
||||||
|
local allin = true
|
||||||
|
if table.length(unfilled) > 0 then
|
||||||
|
MESSAGE:New("There is at least one node island!",15,"STRATEGO"):ToAllIf(self.debug):ToLog()
|
||||||
|
allin = false
|
||||||
|
if self.debug == true then
|
||||||
|
local zone1 = DrawElastic(filled,draw)
|
||||||
|
local zone2 = DrawElastic(unfilled,draw)
|
||||||
|
local vertices1 = zone1:GetVerticiesVec2()
|
||||||
|
local vertices2 = zone2:GetVerticiesVec2()
|
||||||
|
-- get closest vertices
|
||||||
|
local corner1 = nil
|
||||||
|
local corner2 = nil
|
||||||
|
local mindist = math.huge
|
||||||
|
local found = false
|
||||||
|
for _,_edge in pairs(vertices1) do
|
||||||
|
for _,_edge2 in pairs(vertices2) do
|
||||||
|
local dist=UTILS.VecDist2D(_edge,_edge2)
|
||||||
|
if dist < mindist then
|
||||||
|
mindist = dist
|
||||||
|
corner1 = _edge
|
||||||
|
corner2 = _edge2
|
||||||
|
found = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if found then
|
||||||
|
local Corner = COORDINATE:NewFromVec2(corner1)
|
||||||
|
local Corner2 = COORDINATE:NewFromVec2(corner2)
|
||||||
|
Corner:LineToAll(Corner2,-1,{1,1,1},1,1,true,"Island2Island")
|
||||||
|
local cornername
|
||||||
|
local cornername2
|
||||||
|
for _name,_data in pairs(self.airbasetable) do
|
||||||
|
local zone = _data.zone
|
||||||
|
if zone:IsVec2InZone(corner1) then
|
||||||
|
cornername = _name
|
||||||
|
self:T("Corner1 = ".._name)
|
||||||
|
end
|
||||||
|
if zone:IsVec2InZone(corner2) then
|
||||||
|
cornername2 = _name
|
||||||
|
self:T("Corner2 = ".._name)
|
||||||
|
end
|
||||||
|
if cornername and cornername2 and connect == true then
|
||||||
|
self:AddRoutesManually(cornername,cornername2,Color,Linetype,self.debug)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return allin, filled, unfilled
|
||||||
|
end
|
||||||
|
|
||||||
---------------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------------
|
||||||
--
|
--
|
||||||
-- End
|
-- End
|
||||||
|
|||||||
@ -255,6 +255,7 @@
|
|||||||
-- @field #boolean skipperUturn U-turn on/off via menu.
|
-- @field #boolean skipperUturn U-turn on/off via menu.
|
||||||
-- @field #number skipperOffset Holding offset angle in degrees for Case II/III manual recoveries.
|
-- @field #number skipperOffset Holding offset angle in degrees for Case II/III manual recoveries.
|
||||||
-- @field #number skipperTime Recovery time in min for manual recovery.
|
-- @field #number skipperTime Recovery time in min for manual recovery.
|
||||||
|
-- @field #boolean intowindold If true, use old into wind calculation.
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- Be the boss!
|
--- Be the boss!
|
||||||
@ -2724,6 +2725,18 @@ function AIRBOSS:SetLSOCallInterval( TimeInterval )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set if old into wind calculation is used when carrier turns into the wind for a recovery.
|
||||||
|
-- @param #AIRBOSS self
|
||||||
|
-- @param #boolean SwitchOn If `true` or `nil`, use old into wind calculation.
|
||||||
|
-- @return #AIRBOSS self
|
||||||
|
function AIRBOSS:SetIntoWindLegacy( SwitchOn )
|
||||||
|
if SwitchOn==nil then
|
||||||
|
SwitchOn=true
|
||||||
|
end
|
||||||
|
self.intowindold=SwitchOn
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Airboss is a rather nice guy and not strictly following the rules. Fore example, he does allow you into the landing pattern if you are not coming from the Marshal stack.
|
--- Airboss is a rather nice guy and not strictly following the rules. Fore example, he does allow you into the landing pattern if you are not coming from the Marshal stack.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
-- @param #boolean Switch If true or nil, Airboss bends the rules a bit.
|
-- @param #boolean Switch If true or nil, Airboss bends the rules a bit.
|
||||||
@ -3642,6 +3655,12 @@ function AIRBOSS:onafterStatus( From, Event, To )
|
|||||||
local pos = self:GetCoordinate()
|
local pos = self:GetCoordinate()
|
||||||
local speed = self.carrier:GetVelocityKNOTS()
|
local speed = self.carrier:GetVelocityKNOTS()
|
||||||
|
|
||||||
|
-- Update magnetic variation if we can get it from DCS.
|
||||||
|
if require then
|
||||||
|
self.magvar=pos:GetMagneticDeclination()
|
||||||
|
--env.info(string.format("FF magvar=%.1f", self.magvar))
|
||||||
|
end
|
||||||
|
|
||||||
-- Check water is ahead.
|
-- Check water is ahead.
|
||||||
local collision = false -- self:_CheckCollisionCoord(pos:Translate(self.collisiondist, hdg))
|
local collision = false -- self:_CheckCollisionCoord(pos:Translate(self.collisiondist, hdg))
|
||||||
|
|
||||||
@ -5201,6 +5220,7 @@ function AIRBOSS:_InitVoiceOvers()
|
|||||||
TOMCAT = { file = "PILOT-Tomcat", suffix = "ogg", loud = false, subtitle = "", duration = 0.66, subduration = 5 },
|
TOMCAT = { file = "PILOT-Tomcat", suffix = "ogg", loud = false, subtitle = "", duration = 0.66, subduration = 5 },
|
||||||
HORNET = { file = "PILOT-Hornet", suffix = "ogg", loud = false, subtitle = "", duration = 0.56, subduration = 5 },
|
HORNET = { file = "PILOT-Hornet", suffix = "ogg", loud = false, subtitle = "", duration = 0.56, subduration = 5 },
|
||||||
VIKING = { file = "PILOT-Viking", suffix = "ogg", loud = false, subtitle = "", duration = 0.61, subduration = 5 },
|
VIKING = { file = "PILOT-Viking", suffix = "ogg", loud = false, subtitle = "", duration = 0.61, subduration = 5 },
|
||||||
|
GREYHOUND = { file = "PILOT-Greyhound", suffix = "ogg", loud = false, subtitle = "", duration = 0.61, subduration = 5 },
|
||||||
BALL = { file = "PILOT-Ball", suffix = "ogg", loud = false, subtitle = "", duration = 0.50, subduration = 5 },
|
BALL = { file = "PILOT-Ball", suffix = "ogg", loud = false, subtitle = "", duration = 0.50, subduration = 5 },
|
||||||
BINGOFUEL = { file = "PILOT-BingoFuel", suffix = "ogg", loud = false, subtitle = "", duration = 0.80 },
|
BINGOFUEL = { file = "PILOT-BingoFuel", suffix = "ogg", loud = false, subtitle = "", duration = 0.80 },
|
||||||
GASATDIVERT = { file = "PILOT-GasAtDivert", suffix = "ogg", loud = false, subtitle = "", duration = 1.80 },
|
GASATDIVERT = { file = "PILOT-GasAtDivert", suffix = "ogg", loud = false, subtitle = "", duration = 1.80 },
|
||||||
@ -6475,7 +6495,7 @@ function AIRBOSS:_LandAI( flight )
|
|||||||
or flight.actype == AIRBOSS.AircraftCarrier.RHINOF
|
or flight.actype == AIRBOSS.AircraftCarrier.RHINOF
|
||||||
or flight.actype == AIRBOSS.AircraftCarrier.GROWLER then
|
or flight.actype == AIRBOSS.AircraftCarrier.GROWLER then
|
||||||
Speed = UTILS.KnotsToKmph( 200 )
|
Speed = UTILS.KnotsToKmph( 200 )
|
||||||
elseif flight.actype == AIRBOSS.AircraftCarrier.E2D then
|
elseif flight.actype == AIRBOSS.AircraftCarrier.E2D or flight.actype == AIRBOSS.AircraftCarrier.C2A then
|
||||||
Speed = UTILS.KnotsToKmph( 150 )
|
Speed = UTILS.KnotsToKmph( 150 )
|
||||||
elseif flight.actype == AIRBOSS.AircraftCarrier.F14A_AI or flight.actype == AIRBOSS.AircraftCarrier.F14A or flight.actype == AIRBOSS.AircraftCarrier.F14B then
|
elseif flight.actype == AIRBOSS.AircraftCarrier.F14A_AI or flight.actype == AIRBOSS.AircraftCarrier.F14A or flight.actype == AIRBOSS.AircraftCarrier.F14B then
|
||||||
Speed = UTILS.KnotsToKmph( 175 )
|
Speed = UTILS.KnotsToKmph( 175 )
|
||||||
@ -11476,7 +11496,7 @@ end
|
|||||||
|
|
||||||
--- Get wind direction and speed at carrier position.
|
--- Get wind direction and speed at carrier position.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
-- @param #number alt Altitude ASL in meters. Default 15 m.
|
-- @param #number alt Altitude ASL in meters. Default 18 m.
|
||||||
-- @param #boolean magnetic Direction including magnetic declination.
|
-- @param #boolean magnetic Direction including magnetic declination.
|
||||||
-- @param Core.Point#COORDINATE coord (Optional) Coordinate at which to get the wind. Default is current carrier position.
|
-- @param Core.Point#COORDINATE coord (Optional) Coordinate at which to get the wind. Default is current carrier position.
|
||||||
-- @return #number Direction the wind is blowing **from** in degrees.
|
-- @return #number Direction the wind is blowing **from** in degrees.
|
||||||
@ -11548,10 +11568,31 @@ end
|
|||||||
|
|
||||||
--- Get true (or magnetic) heading of carrier into the wind. This accounts for the angled runway.
|
--- Get true (or magnetic) heading of carrier into the wind. This accounts for the angled runway.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
|
-- @param #number vdeck Desired wind velocity over deck in knots.
|
||||||
-- @param #boolean magnetic If true, calculate magnetic heading. By default true heading is returned.
|
-- @param #boolean magnetic If true, calculate magnetic heading. By default true heading is returned.
|
||||||
-- @param Core.Point#COORDINATE coord (Optional) Coordinate from which heading is calculated. Default is current carrier position.
|
-- @param Core.Point#COORDINATE coord (Optional) Coordinate from which heading is calculated. Default is current carrier position.
|
||||||
-- @return #number Carrier heading in degrees.
|
-- @return #number Carrier heading in degrees.
|
||||||
function AIRBOSS:GetHeadingIntoWind_old( magnetic, coord )
|
-- @return #number Carrier speed in knots to reach desired wind speed on deck.
|
||||||
|
function AIRBOSS:GetHeadingIntoWind(vdeck, magnetic, coord )
|
||||||
|
|
||||||
|
if self.intowindold then
|
||||||
|
--env.info("FF use OLD into wind")
|
||||||
|
return self:GetHeadingIntoWind_old(vdeck, magnetic, coord)
|
||||||
|
else
|
||||||
|
--env.info("FF use NEW into wind")
|
||||||
|
return self:GetHeadingIntoWind_new(vdeck, magnetic, coord)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get true (or magnetic) heading of carrier into the wind. This accounts for the angled runway.
|
||||||
|
-- @param #AIRBOSS self
|
||||||
|
-- @param #number vdeck Desired wind velocity over deck in knots.
|
||||||
|
-- @param #boolean magnetic If true, calculate magnetic heading. By default true heading is returned.
|
||||||
|
-- @param Core.Point#COORDINATE coord (Optional) Coordinate from which heading is calculated. Default is current carrier position.
|
||||||
|
-- @return #number Carrier heading in degrees.
|
||||||
|
function AIRBOSS:GetHeadingIntoWind_old( vdeck, magnetic, coord )
|
||||||
|
|
||||||
local function adjustDegreesForWindSpeed(windSpeed)
|
local function adjustDegreesForWindSpeed(windSpeed)
|
||||||
local degreesAdjustment = 0
|
local degreesAdjustment = 0
|
||||||
@ -11608,7 +11649,13 @@ function AIRBOSS:GetHeadingIntoWind_old( magnetic, coord )
|
|||||||
intowind = intowind + 360
|
intowind = intowind + 360
|
||||||
end
|
end
|
||||||
|
|
||||||
return intowind
|
-- Wind speed.
|
||||||
|
--local _, vwind = self:GetWind()
|
||||||
|
|
||||||
|
-- Speed of carrier in m/s but at least 4 knots.
|
||||||
|
local vtot = math.max(vdeck-UTILS.MpsToKnots(vwind), 4)
|
||||||
|
|
||||||
|
return intowind, vtot
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get true (or magnetic) heading of carrier into the wind. This accounts for the angled runway.
|
--- Get true (or magnetic) heading of carrier into the wind. This accounts for the angled runway.
|
||||||
@ -11619,7 +11666,7 @@ end
|
|||||||
-- @param Core.Point#COORDINATE coord (Optional) Coordinate from which heading is calculated. Default is current carrier position.
|
-- @param Core.Point#COORDINATE coord (Optional) Coordinate from which heading is calculated. Default is current carrier position.
|
||||||
-- @return #number Carrier heading in degrees.
|
-- @return #number Carrier heading in degrees.
|
||||||
-- @return #number Carrier speed in knots to reach desired wind speed on deck.
|
-- @return #number Carrier speed in knots to reach desired wind speed on deck.
|
||||||
function AIRBOSS:GetHeadingIntoWind( vdeck, magnetic, coord )
|
function AIRBOSS:GetHeadingIntoWind_new( vdeck, magnetic, coord )
|
||||||
|
|
||||||
-- Default offset angle.
|
-- Default offset angle.
|
||||||
local Offset=self.carrierparam.rwyangle or 0
|
local Offset=self.carrierparam.rwyangle or 0
|
||||||
@ -14280,6 +14327,8 @@ function AIRBOSS:_GetACNickname( actype )
|
|||||||
nickname = "Harrier"
|
nickname = "Harrier"
|
||||||
elseif actype == AIRBOSS.AircraftCarrier.E2D then
|
elseif actype == AIRBOSS.AircraftCarrier.E2D then
|
||||||
nickname = "Hawkeye"
|
nickname = "Hawkeye"
|
||||||
|
elseif actype == AIRBOSS.AircraftCarrier.C2A then
|
||||||
|
nickname = "Greyhound"
|
||||||
elseif actype == AIRBOSS.AircraftCarrier.F14A_AI or actype == AIRBOSS.AircraftCarrier.F14A or actype == AIRBOSS.AircraftCarrier.F14B then
|
elseif actype == AIRBOSS.AircraftCarrier.F14A_AI or actype == AIRBOSS.AircraftCarrier.F14A or actype == AIRBOSS.AircraftCarrier.F14B then
|
||||||
nickname = "Tomcat"
|
nickname = "Tomcat"
|
||||||
elseif actype == AIRBOSS.AircraftCarrier.FA18C or actype == AIRBOSS.AircraftCarrier.HORNET then
|
elseif actype == AIRBOSS.AircraftCarrier.FA18C or actype == AIRBOSS.AircraftCarrier.HORNET then
|
||||||
@ -14317,32 +14366,55 @@ function AIRBOSS:_GetOnboardNumbers( group, playeronly )
|
|||||||
-- Debug text.
|
-- Debug text.
|
||||||
local text = string.format( "Onboard numbers of group %s:", groupname )
|
local text = string.format( "Onboard numbers of group %s:", groupname )
|
||||||
|
|
||||||
-- Units of template group.
|
local template=group:GetTemplate()
|
||||||
local units = group:GetTemplate().units
|
|
||||||
|
|
||||||
-- Get numbers.
|
|
||||||
local numbers = {}
|
local numbers = {}
|
||||||
for _, unit in pairs( units ) do
|
if template then
|
||||||
|
|
||||||
-- Onboard number and unit name.
|
-- Units of template group.
|
||||||
local n = tostring( unit.onboard_num )
|
local units = template.units
|
||||||
local name = unit.name
|
|
||||||
local skill = unit.skill or "Unknown"
|
|
||||||
|
|
||||||
-- Debug text.
|
-- Get numbers.
|
||||||
text = text .. string.format( "\n- unit %s: onboard #=%s skill=%s", name, n, tostring( skill ) )
|
for _, unit in pairs( units ) do
|
||||||
|
|
||||||
if playeronly and skill == "Client" or skill == "Player" then
|
-- Onboard number and unit name.
|
||||||
-- There can be only one player in the group, so we skip everything else.
|
local n = tostring( unit.onboard_num )
|
||||||
return n
|
local name = unit.name
|
||||||
|
local skill = unit.skill or "Unknown"
|
||||||
|
|
||||||
|
-- Debug text.
|
||||||
|
text = text .. string.format( "\n- unit %s: onboard #=%s skill=%s", name, n, tostring( skill ) )
|
||||||
|
|
||||||
|
if playeronly and skill == "Client" or skill == "Player" then
|
||||||
|
-- There can be only one player in the group, so we skip everything else.
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Table entry.
|
||||||
|
numbers[name] = n
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Table entry.
|
-- Debug info.
|
||||||
numbers[name] = n
|
self:T2( self.lid .. text )
|
||||||
end
|
|
||||||
|
|
||||||
-- Debug info.
|
else
|
||||||
self:T2( self.lid .. text )
|
|
||||||
|
if playeronly then
|
||||||
|
return 101
|
||||||
|
else
|
||||||
|
|
||||||
|
local units=group:GetUnits()
|
||||||
|
|
||||||
|
for i,_unit in pairs(units) do
|
||||||
|
local name=_unit:GetName()
|
||||||
|
|
||||||
|
numbers[name]=100+i
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
return numbers
|
return numbers
|
||||||
end
|
end
|
||||||
|
|||||||
@ -2109,7 +2109,7 @@ function ARMYGROUP:_InitGroup(Template, Delay)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self:I(self.lid.."FF Initializing Group")
|
self:T(self.lid.."FF Initializing Group")
|
||||||
|
|
||||||
-- Get template of group.
|
-- Get template of group.
|
||||||
local template=Template or self:_GetTemplate()
|
local template=Template or self:_GetTemplate()
|
||||||
|
|||||||
@ -291,10 +291,12 @@ CSAR.AircraftType["UH-60L"] = 10
|
|||||||
CSAR.AircraftType["AH-64D_BLK_II"] = 2
|
CSAR.AircraftType["AH-64D_BLK_II"] = 2
|
||||||
CSAR.AircraftType["Bronco-OV-10A"] = 2
|
CSAR.AircraftType["Bronco-OV-10A"] = 2
|
||||||
CSAR.AircraftType["MH-60R"] = 10
|
CSAR.AircraftType["MH-60R"] = 10
|
||||||
|
CSAR.AircraftType["OH-6A"] = 2
|
||||||
|
CSAR.AircraftType["OH58D"] = 2
|
||||||
|
|
||||||
--- CSAR class version.
|
--- CSAR class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
CSAR.version="1.0.21"
|
CSAR.version="1.0.24"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- ToDo list
|
-- ToDo list
|
||||||
@ -734,7 +736,7 @@ function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet)
|
|||||||
:NewWithAlias(template,alias)
|
:NewWithAlias(template,alias)
|
||||||
:InitCoalition(coalition)
|
:InitCoalition(coalition)
|
||||||
:InitCountry(country)
|
:InitCountry(country)
|
||||||
:InitAIOnOff(pilotcacontrol)
|
--:InitAIOnOff(pilotcacontrol)
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
:SpawnFromCoordinate(point)
|
:SpawnFromCoordinate(point)
|
||||||
|
|
||||||
@ -1238,10 +1240,24 @@ function CSAR:_InitSARForPilot(_downedGroup, _GroupName, _freq, _nomessage, _pla
|
|||||||
if not _nomessage then
|
if not _nomessage then
|
||||||
if _freq ~= 0 then --shagrat
|
if _freq ~= 0 then --shagrat
|
||||||
local _text = string.format("%s requests SAR at %s, beacon at %.2f KHz", _groupName, _coordinatesText, _freqk)--shagrat _groupName to prevent 'f15_Pilot_Parachute'
|
local _text = string.format("%s requests SAR at %s, beacon at %.2f KHz", _groupName, _coordinatesText, _freqk)--shagrat _groupName to prevent 'f15_Pilot_Parachute'
|
||||||
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
|
if self.coordtype ~= 2 then --not MGRS
|
||||||
|
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
|
||||||
|
else
|
||||||
|
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime,false,true)
|
||||||
|
local coordtext = UTILS.MGRSStringToSRSFriendly(_coordinatesText,true)
|
||||||
|
local _text = string.format("%s requests SAR at %s, beacon at %.2f kilo hertz", _groupName, coordtext, _freqk)
|
||||||
|
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime,true,false)
|
||||||
|
end
|
||||||
else --shagrat CASEVAC msg
|
else --shagrat CASEVAC msg
|
||||||
local _text = string.format("Pickup Zone at %s.", _coordinatesText )
|
local _text = string.format("Pickup Zone at %s.", _coordinatesText )
|
||||||
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
|
if self.coordtype ~= 2 then --not MGRS
|
||||||
|
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime)
|
||||||
|
else
|
||||||
|
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime,false,true)
|
||||||
|
local coordtext = UTILS.MGRSStringToSRSFriendly(_coordinatesText,true)
|
||||||
|
local _text = string.format("Pickup Zone at %s.", coordtext )
|
||||||
|
self:_DisplayToAllSAR(_text,self.coalition,self.messageTime,true,false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1945,23 +1961,28 @@ end
|
|||||||
--- (Internal) Display info to all SAR groups.
|
--- (Internal) Display info to all SAR groups.
|
||||||
-- @param #CSAR self
|
-- @param #CSAR self
|
||||||
-- @param #string _message Message to display.
|
-- @param #string _message Message to display.
|
||||||
-- @param #number _side Coalition of message.
|
-- @param #number _side Coalition of message.
|
||||||
-- @param #number _messagetime How long to show.
|
-- @param #number _messagetime How long to show.
|
||||||
function CSAR:_DisplayToAllSAR(_message, _side, _messagetime)
|
-- @param #boolean ToSRS If true or nil, send to SRS TTS
|
||||||
|
-- @param #boolean ToScreen If true or nil, send to Screen
|
||||||
|
function CSAR:_DisplayToAllSAR(_message, _side, _messagetime,ToSRS,ToScreen)
|
||||||
self:T(self.lid .. " _DisplayToAllSAR")
|
self:T(self.lid .. " _DisplayToAllSAR")
|
||||||
local messagetime = _messagetime or self.messageTime
|
local messagetime = _messagetime or self.messageTime
|
||||||
if self.msrs then
|
self:T({_message,ToSRS=ToSRS,ToScreen=ToScreen})
|
||||||
|
if self.msrs and (ToSRS == true or ToSRS == nil) then
|
||||||
local voice = self.CSARVoice or MSRS.Voices.Google.Standard.en_GB_Standard_F
|
local voice = self.CSARVoice or MSRS.Voices.Google.Standard.en_GB_Standard_F
|
||||||
if self.msrs:GetProvider() == MSRS.Provider.WINDOWS then
|
if self.msrs:GetProvider() == MSRS.Provider.WINDOWS then
|
||||||
voice = self.CSARVoiceMS or MSRS.Voices.Microsoft.Hedda
|
voice = self.CSARVoiceMS or MSRS.Voices.Microsoft.Hedda
|
||||||
end
|
end
|
||||||
self:I("Voice = "..voice)
|
self:F("Voice = "..voice)
|
||||||
self.SRSQueue:NewTransmission(_message,duration,self.msrs,tstart,2,subgroups,subtitle,subduration,self.SRSchannel,self.SRSModulation,gender,culture,voice,volume,label,self.coordinate)
|
self.SRSQueue:NewTransmission(_message,duration,self.msrs,tstart,2,subgroups,subtitle,subduration,self.SRSchannel,self.SRSModulation,gender,culture,voice,volume,label,self.coordinate)
|
||||||
end
|
end
|
||||||
for _, _unitName in pairs(self.csarUnits) do
|
if ToScreen == true or ToScreen == nil then
|
||||||
local _unit = self:_GetSARHeli(_unitName)
|
for _, _unitName in pairs(self.csarUnits) do
|
||||||
if _unit and not self.suppressmessages then
|
local _unit = self:_GetSARHeli(_unitName)
|
||||||
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
|
if _unit and not self.suppressmessages then
|
||||||
|
self:_DisplayMessageToSAR(_unit, _message, _messagetime)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
|
|||||||
@ -1249,11 +1249,13 @@ CTLD.UnitTypeCapabilities = {
|
|||||||
["SH-60B"] = {type="SH-60B", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
|
["SH-60B"] = {type="SH-60B", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
|
||||||
["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, -- 2 ppl **outside** the helo
|
["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, -- 2 ppl **outside** the helo
|
||||||
["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450},
|
["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450},
|
||||||
|
["OH-6A"] = {type="OH-6A", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 7, cargoweightlimit = 550},
|
||||||
|
["OH58D"] = {type="OH58D", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 14, cargoweightlimit = 400},
|
||||||
}
|
}
|
||||||
|
|
||||||
--- CTLD class version.
|
--- CTLD class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
CTLD.version="1.0.51"
|
CTLD.version="1.0.54"
|
||||||
|
|
||||||
--- Instantiate a new CTLD.
|
--- Instantiate a new CTLD.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
@ -3607,7 +3609,7 @@ function CTLD:_MoveGroupToZone(Group)
|
|||||||
local groupcoord = Group:GetCoordinate()
|
local groupcoord = Group:GetCoordinate()
|
||||||
-- Get closest zone of type
|
-- Get closest zone of type
|
||||||
local outcome, name, zone, distance = self:IsUnitInZone(Group,CTLD.CargoZoneType.MOVE)
|
local outcome, name, zone, distance = self:IsUnitInZone(Group,CTLD.CargoZoneType.MOVE)
|
||||||
if (distance <= self.movetroopsdistance) and zone then
|
if (distance <= self.movetroopsdistance) and outcome == true and zone~= nil then
|
||||||
-- yes, we can ;)
|
-- yes, we can ;)
|
||||||
local groupname = Group:GetName()
|
local groupname = Group:GetName()
|
||||||
local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE
|
local zonecoord = zone:GetRandomCoordinate(20,125) -- Core.Point#COORDINATE
|
||||||
@ -4464,10 +4466,9 @@ function CTLD:IsUnitInZone(Unit,Zonetype)
|
|||||||
zonewidth = zoneradius
|
zonewidth = zoneradius
|
||||||
end
|
end
|
||||||
local distance = self:_GetDistance(zonecoord,unitcoord)
|
local distance = self:_GetDistance(zonecoord,unitcoord)
|
||||||
if zone:IsVec2InZone(unitVec2) and active then
|
self:T("Distance Zone: "..distance)
|
||||||
|
if (zone:IsVec2InZone(unitVec2) or Zonetype == CTLD.CargoZoneType.MOVE) and active == true and maxdist > distance then
|
||||||
outcome = true
|
outcome = true
|
||||||
end
|
|
||||||
if maxdist > distance then
|
|
||||||
maxdist = distance
|
maxdist = distance
|
||||||
zoneret = zone
|
zoneret = zone
|
||||||
zonenameret = zonename
|
zonenameret = zonename
|
||||||
|
|||||||
@ -1078,6 +1078,13 @@ function CHIEF:SetStrategy(Strategy)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get current strategy.
|
||||||
|
-- @param #CHIEF self
|
||||||
|
-- @return #string Strategy.
|
||||||
|
function CHIEF:GetStrategy()
|
||||||
|
return self.strategy
|
||||||
|
end
|
||||||
|
|
||||||
--- Get defence condition.
|
--- Get defence condition.
|
||||||
-- @param #CHIEF self
|
-- @param #CHIEF self
|
||||||
-- @param #string Current Defence condition. See @{#CHIEF.DEFCON}, e.g. `CHIEF.DEFCON.RED`.
|
-- @param #string Current Defence condition. See @{#CHIEF.DEFCON}, e.g. `CHIEF.DEFCON.RED`.
|
||||||
@ -1456,7 +1463,7 @@ end
|
|||||||
--- Add a CAP zone. Flights will engage detected targets inside this zone.
|
--- Add a CAP zone. Flights will engage detected targets inside this zone.
|
||||||
-- @param #CHIEF self
|
-- @param #CHIEF self
|
||||||
-- @param Core.Zone#ZONE Zone CAP Zone. Has to be a circular zone.
|
-- @param Core.Zone#ZONE Zone CAP Zone. Has to be a circular zone.
|
||||||
-- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet.
|
-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet.
|
||||||
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
||||||
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
||||||
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
||||||
@ -1472,7 +1479,7 @@ end
|
|||||||
--- Add a GCI CAP.
|
--- Add a GCI CAP.
|
||||||
-- @param #CHIEF self
|
-- @param #CHIEF self
|
||||||
-- @param Core.Zone#ZONE Zone Zone, where the flight orbits.
|
-- @param Core.Zone#ZONE Zone Zone, where the flight orbits.
|
||||||
-- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet.
|
-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet.
|
||||||
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
||||||
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
||||||
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
||||||
@ -1499,7 +1506,7 @@ end
|
|||||||
--- Add an AWACS zone.
|
--- Add an AWACS zone.
|
||||||
-- @param #CHIEF self
|
-- @param #CHIEF self
|
||||||
-- @param Core.Zone#ZONE Zone Zone.
|
-- @param Core.Zone#ZONE Zone Zone.
|
||||||
-- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet.
|
-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet.
|
||||||
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
||||||
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
||||||
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
||||||
@ -1526,7 +1533,7 @@ end
|
|||||||
--- Add a refuelling tanker zone.
|
--- Add a refuelling tanker zone.
|
||||||
-- @param #CHIEF self
|
-- @param #CHIEF self
|
||||||
-- @param Core.Zone#ZONE Zone Zone.
|
-- @param Core.Zone#ZONE Zone Zone.
|
||||||
-- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet.
|
-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet.
|
||||||
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
||||||
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
||||||
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
||||||
|
|||||||
@ -663,7 +663,7 @@ end
|
|||||||
--- Add a CAP zone.
|
--- Add a CAP zone.
|
||||||
-- @param #COMMANDER self
|
-- @param #COMMANDER self
|
||||||
-- @param Core.Zone#ZONE Zone CapZone Zone.
|
-- @param Core.Zone#ZONE Zone CapZone Zone.
|
||||||
-- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet.
|
-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet.
|
||||||
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
||||||
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
||||||
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
||||||
@ -689,7 +689,7 @@ end
|
|||||||
--- Add a GCICAP zone.
|
--- Add a GCICAP zone.
|
||||||
-- @param #COMMANDER self
|
-- @param #COMMANDER self
|
||||||
-- @param Core.Zone#ZONE Zone CapZone Zone.
|
-- @param Core.Zone#ZONE Zone CapZone Zone.
|
||||||
-- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet.
|
-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet.
|
||||||
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
||||||
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
||||||
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
||||||
@ -735,7 +735,7 @@ end
|
|||||||
--- Add an AWACS zone.
|
--- Add an AWACS zone.
|
||||||
-- @param #COMMANDER self
|
-- @param #COMMANDER self
|
||||||
-- @param Core.Zone#ZONE Zone Zone.
|
-- @param Core.Zone#ZONE Zone Zone.
|
||||||
-- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet.
|
-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet.
|
||||||
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
||||||
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
||||||
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
||||||
@ -782,7 +782,7 @@ end
|
|||||||
--- Add a refuelling tanker zone.
|
--- Add a refuelling tanker zone.
|
||||||
-- @param #COMMANDER self
|
-- @param #COMMANDER self
|
||||||
-- @param Core.Zone#ZONE Zone Zone.
|
-- @param Core.Zone#ZONE Zone Zone.
|
||||||
-- @param #number Altitude Orbit altitude in feet. Default is 12,0000 feet.
|
-- @param #number Altitude Orbit altitude in feet. Default is 12,000 feet.
|
||||||
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
-- @param #number Speed Orbit speed in KIAS. Default 350 kts.
|
||||||
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
|
||||||
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
-- @param #number Leg Length of race-track in NM. Default 30 NM.
|
||||||
|
|||||||
@ -62,6 +62,9 @@
|
|||||||
-- @field #number runwayrepairtime Time in seconds until runway will be repaired after it was destroyed. Default is 3600 sec (one hour).
|
-- @field #number runwayrepairtime Time in seconds until runway will be repaired after it was destroyed. Default is 3600 sec (one hour).
|
||||||
-- @field #boolean markerParking If `true`, occupied parking spots are marked.
|
-- @field #boolean markerParking If `true`, occupied parking spots are marked.
|
||||||
-- @field #table warnings Warnings issued to flight groups.
|
-- @field #table warnings Warnings issued to flight groups.
|
||||||
|
-- @field #boolean nosubs If `true`, SRS TTS is without subtitles.
|
||||||
|
-- @field #number Nplayers Number of human players. Updated at each StatusUpdate call.
|
||||||
|
-- @field #boolean radioOnlyIfPlayers Activate to limit transmissions only if players are active at the airbase.
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- **Ground Control**: Airliner X, Good news, you are clear to taxi to the active.
|
--- **Ground Control**: Airliner X, Good news, you are clear to taxi to the active.
|
||||||
@ -273,6 +276,7 @@ FLIGHTCONTROL = {
|
|||||||
hpcounter = 0,
|
hpcounter = 0,
|
||||||
warnings = {},
|
warnings = {},
|
||||||
nosubs = false,
|
nosubs = false,
|
||||||
|
Nplayers = 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Holding point. Contains holding stacks.
|
--- Holding point. Contains holding stacks.
|
||||||
@ -351,7 +355,7 @@ FLIGHTCONTROL.Violation={
|
|||||||
|
|
||||||
--- FlightControl class version.
|
--- FlightControl class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
FLIGHTCONTROL.version="0.7.5"
|
FLIGHTCONTROL.version="0.7.7"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
@ -452,6 +456,16 @@ function FLIGHTCONTROL:New(AirbaseName, Frequency, Modulation, PathToSRS, Port,
|
|||||||
-- Init msrs queue.
|
-- Init msrs queue.
|
||||||
self.msrsqueue=MSRSQUEUE:New(self.alias)
|
self.msrsqueue=MSRSQUEUE:New(self.alias)
|
||||||
|
|
||||||
|
-- Set that transmission is only if alive players on the server.
|
||||||
|
self:SetTransmitOnlyWithPlayers(true)
|
||||||
|
|
||||||
|
-- Init msrs bases
|
||||||
|
local path = PathToSRS or MSRS.path
|
||||||
|
local port = Port or MSRS.port or 5002
|
||||||
|
|
||||||
|
-- Set SRS Port
|
||||||
|
self:SetSRSPort(port)
|
||||||
|
|
||||||
-- SRS for Tower.
|
-- SRS for Tower.
|
||||||
self.msrsTower=MSRS:New(path, Frequency, Modulation)
|
self.msrsTower=MSRS:New(path, Frequency, Modulation)
|
||||||
self.msrsTower:SetPort(port)
|
self.msrsTower:SetPort(port)
|
||||||
@ -605,6 +619,31 @@ function FLIGHTCONTROL:SetVerbosity(VerbosityLevel)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Limit radio transmissions only if human players are registered at the airbase.
|
||||||
|
-- This can be used to reduce TTS messages on heavy missions.
|
||||||
|
-- @param #FLIGHTCONTROL self
|
||||||
|
-- @param #boolean Switch If `true` or `nil` no transmission if there are no players. Use `false` enable TTS with no players.
|
||||||
|
-- @return #FLIGHTCONTROL self
|
||||||
|
function FLIGHTCONTROL:SetRadioOnlyIfPlayers(Switch)
|
||||||
|
if Switch==nil or Switch==true then
|
||||||
|
self.radioOnlyIfPlayers=true
|
||||||
|
else
|
||||||
|
self.radioOnlyIfPlayers=false
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Set whether to only transmit TTS messages if there are players on the server.
|
||||||
|
-- @param #FLIGHTCONTROL self
|
||||||
|
-- @param #boolean Switch If `true`, only send TTS messages if there are alive Players. If `false` or `nil`, transmission are done also if no players are on the server.
|
||||||
|
-- @return #FLIGHTCONTROL self
|
||||||
|
function FLIGHTCONTROL:SetTransmitOnlyWithPlayers(Switch)
|
||||||
|
self.msrsqueue:SetTransmitOnlyWithPlayers(Switch)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Set subtitles to appear on SRS TTS messages.
|
--- Set subtitles to appear on SRS TTS messages.
|
||||||
-- @param #FLIGHTCONTROL self
|
-- @param #FLIGHTCONTROL self
|
||||||
-- @return #FLIGHTCONTROL self
|
-- @return #FLIGHTCONTROL self
|
||||||
@ -2242,7 +2281,7 @@ function FLIGHTCONTROL:_InitParkingSpots()
|
|||||||
local unitname=unit and unit:GetName() or "unknown"
|
local unitname=unit and unit:GetName() or "unknown"
|
||||||
|
|
||||||
local isalive=unit:IsAlive()
|
local isalive=unit:IsAlive()
|
||||||
|
|
||||||
self:T2(self.lid..string.format("FF parking spot %d is occupied by unit %s alive=%s", spot.TerminalID, unitname, tostring(isalive)))
|
self:T2(self.lid..string.format("FF parking spot %d is occupied by unit %s alive=%s", spot.TerminalID, unitname, tostring(isalive)))
|
||||||
|
|
||||||
if isalive then
|
if isalive then
|
||||||
@ -4243,6 +4282,72 @@ function FLIGHTCONTROL:_CheckFlights()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Count number of players
|
||||||
|
self.Nplayers=0
|
||||||
|
for _,_flight in pairs(self.flights) do
|
||||||
|
local flight=_flight --Ops.FlightGroup#FLIGHTGROUP
|
||||||
|
if not flight.isAI then
|
||||||
|
self.Nplayers=self.Nplayers+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check speeding.
|
||||||
|
if self.speedLimitTaxi then
|
||||||
|
|
||||||
|
for _,_flight in pairs(self.flights) do
|
||||||
|
local flight=_flight --Ops.FlightGroup#FLIGHTGROUP
|
||||||
|
|
||||||
|
if not flight.isAI then
|
||||||
|
|
||||||
|
-- Get player element.
|
||||||
|
local playerElement=flight:GetPlayerElement()
|
||||||
|
|
||||||
|
-- Current flight status.
|
||||||
|
local flightstatus=self:GetFlightStatus(flight)
|
||||||
|
|
||||||
|
if playerElement then
|
||||||
|
|
||||||
|
-- Check if speeding while taxiing.
|
||||||
|
if (flightstatus==FLIGHTCONTROL.FlightStatus.TAXIINB or flightstatus==FLIGHTCONTROL.FlightStatus.TAXIOUT) and self.speedLimitTaxi then
|
||||||
|
|
||||||
|
-- Current speed in m/s.
|
||||||
|
local speed=playerElement.unit:GetVelocityMPS()
|
||||||
|
|
||||||
|
-- Current position.
|
||||||
|
local coord=playerElement.unit:GetCoord()
|
||||||
|
|
||||||
|
-- We do not want to check speed on runways.
|
||||||
|
local onRunway=self:IsCoordinateRunway(coord)
|
||||||
|
|
||||||
|
-- Debug output.
|
||||||
|
self:T(self.lid..string.format("Player %s speed %.1f knots (max=%.1f) onRunway=%s", playerElement.playerName, UTILS.MpsToKnots(speed), UTILS.MpsToKnots(self.speedLimitTaxi), tostring(onRunway)))
|
||||||
|
|
||||||
|
if speed and speed>self.speedLimitTaxi and not onRunway then
|
||||||
|
|
||||||
|
-- Callsign.
|
||||||
|
local callsign=self:_GetCallsignName(flight)
|
||||||
|
|
||||||
|
-- Radio text.
|
||||||
|
local text=string.format("%s, slow down, you are taxiing too fast!", callsign)
|
||||||
|
|
||||||
|
-- Radio message to player.
|
||||||
|
self:TransmissionTower(text, flight)
|
||||||
|
|
||||||
|
-- Get player data.
|
||||||
|
local PlayerData=flight:_GetPlayerData()
|
||||||
|
|
||||||
|
-- Trigger FSM speeding event.
|
||||||
|
self:PlayerSpeeding(PlayerData)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Loop over all flights.
|
-- Loop over all flights.
|
||||||
for _,_flight in pairs(self.flights) do
|
for _,_flight in pairs(self.flights) do
|
||||||
local flight=_flight --Ops.FlightGroup#FLIGHTGROUP
|
local flight=_flight --Ops.FlightGroup#FLIGHTGROUP
|
||||||
@ -4749,6 +4854,11 @@ end
|
|||||||
-- @param #number Delay Delay in seconds before the text is transmitted. Default 0 sec.
|
-- @param #number Delay Delay in seconds before the text is transmitted. Default 0 sec.
|
||||||
function FLIGHTCONTROL:TransmissionTower(Text, Flight, Delay)
|
function FLIGHTCONTROL:TransmissionTower(Text, Flight, Delay)
|
||||||
|
|
||||||
|
if self.radioOnlyIfPlayers==true and self.Nplayers==0 then
|
||||||
|
self:T(self.lid.."No players ==> skipping TOWER radio transmission")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Spoken text.
|
-- Spoken text.
|
||||||
local text=self:_GetTextForSpeech(Text)
|
local text=self:_GetTextForSpeech(Text)
|
||||||
|
|
||||||
@ -4815,6 +4925,12 @@ end
|
|||||||
-- @param #number Delay Delay in seconds before the text is transmitted. Default 0 sec.
|
-- @param #number Delay Delay in seconds before the text is transmitted. Default 0 sec.
|
||||||
function FLIGHTCONTROL:TransmissionPilot(Text, Flight, Delay)
|
function FLIGHTCONTROL:TransmissionPilot(Text, Flight, Delay)
|
||||||
|
|
||||||
|
if self.radioOnlyIfPlayers==true and self.Nplayers==0 then
|
||||||
|
self:T(self.lid.."No players ==> skipping PILOT radio transmission")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Get player data.
|
-- Get player data.
|
||||||
local playerData=Flight:_GetPlayerData()
|
local playerData=Flight:_GetPlayerData()
|
||||||
|
|
||||||
|
|||||||
@ -2831,6 +2831,11 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime)
|
|||||||
self:T(self.lid.."Engaging! Group NOT done...")
|
self:T(self.lid.."Engaging! Group NOT done...")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
-- Check if group is going for fuel.
|
||||||
|
if self:IsGoing4Fuel() then
|
||||||
|
self:T(self.lid.."Going for FUEL! Group NOT done...")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Number of tasks remaining.
|
-- Number of tasks remaining.
|
||||||
local nTasks=self:CountRemainingTasks()
|
local nTasks=self:CountRemainingTasks()
|
||||||
@ -3447,6 +3452,9 @@ function FLIGHTGROUP:onafterRefuel(From, Event, To, Coordinate)
|
|||||||
|
|
||||||
self:Route({wp0, wp9}, 1)
|
self:Route({wp0, wp9}, 1)
|
||||||
|
|
||||||
|
-- Set RTB on Bingo option. Currently DCS does not execute the refueling task if RTB_ON_BINGO is set to "NO RTB ON BINGO"
|
||||||
|
self.group:SetOption(AI.Option.Air.id.RTB_ON_BINGO, true)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- On after "Refueled" event.
|
--- On after "Refueled" event.
|
||||||
@ -3460,6 +3468,9 @@ function FLIGHTGROUP:onafterRefueled(From, Event, To)
|
|||||||
local text=string.format("Flight group finished refuelling")
|
local text=string.format("Flight group finished refuelling")
|
||||||
self:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
|
|
||||||
|
-- Set RTB on Bingo option to "NO RTB ON BINGO"
|
||||||
|
self.group:SetOption(AI.Option.Air.id.RTB_ON_BINGO, false)
|
||||||
|
|
||||||
-- Check if flight is done.
|
-- Check if flight is done.
|
||||||
self:_CheckGroupDone(1)
|
self:_CheckGroupDone(1)
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
-- ===
|
-- ===
|
||||||
-- @module Ops.PlayerTask
|
-- @module Ops.PlayerTask
|
||||||
-- @image OPS_PlayerTask.jpg
|
-- @image OPS_PlayerTask.jpg
|
||||||
-- @date Last Update Feb 2024
|
-- @date Last Update May 2024
|
||||||
|
|
||||||
|
|
||||||
do
|
do
|
||||||
@ -1213,6 +1213,9 @@ do
|
|||||||
-- AIRDEFENSE = "Airdefense",
|
-- AIRDEFENSE = "Airdefense",
|
||||||
-- SAM = "SAM",
|
-- SAM = "SAM",
|
||||||
-- GROUP = "Group",
|
-- GROUP = "Group",
|
||||||
|
-- ELEVATION = "\nTarget Elevation: %s %s",
|
||||||
|
-- METER = "meter",
|
||||||
|
-- FEET = "feet",
|
||||||
-- },
|
-- },
|
||||||
--
|
--
|
||||||
-- e.g.
|
-- e.g.
|
||||||
@ -1367,7 +1370,7 @@ PLAYERTASKCONTROLLER.Type = {
|
|||||||
AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing"
|
AUFTRAG.Type.PRECISIONBOMBING = "Precision Bombing"
|
||||||
AUFTRAG.Type.CTLD = "Combat Transport"
|
AUFTRAG.Type.CTLD = "Combat Transport"
|
||||||
AUFTRAG.Type.CSAR = "Combat Rescue"
|
AUFTRAG.Type.CSAR = "Combat Rescue"
|
||||||
|
AUFTRAG.Type.CONQUER = "Conquer"
|
||||||
---
|
---
|
||||||
-- @type Scores
|
-- @type Scores
|
||||||
PLAYERTASKCONTROLLER.Scores = {
|
PLAYERTASKCONTROLLER.Scores = {
|
||||||
@ -1380,7 +1383,8 @@ PLAYERTASKCONTROLLER.Scores = {
|
|||||||
[AUFTRAG.Type.BAI] = 100,
|
[AUFTRAG.Type.BAI] = 100,
|
||||||
[AUFTRAG.Type.SEAD] = 100,
|
[AUFTRAG.Type.SEAD] = 100,
|
||||||
[AUFTRAG.Type.BOMBING] = 100,
|
[AUFTRAG.Type.BOMBING] = 100,
|
||||||
[AUFTRAG.Type.BOMBRUNWAY] = 100,
|
[AUFTRAG.Type.BOMBRUNWAY] = 100,
|
||||||
|
[AUFTRAG.Type.CONQUER] = 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -1419,6 +1423,9 @@ PLAYERTASKCONTROLLER.Messages = {
|
|||||||
THREATMEDIUM = "medium",
|
THREATMEDIUM = "medium",
|
||||||
THREATLOW = "low",
|
THREATLOW = "low",
|
||||||
THREATTEXT = "%s\nThreat: %s\nTargets left: %d\nCoord: %s",
|
THREATTEXT = "%s\nThreat: %s\nTargets left: %d\nCoord: %s",
|
||||||
|
ELEVATION = "\nTarget Elevation: %s %s",
|
||||||
|
METER = "meter",
|
||||||
|
FEET = "feet",
|
||||||
THREATTEXTTTS = "%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.",
|
THREATTEXTTTS = "%s, %s. Target information for %s. Threat level %s. Targets left %d. Target location %s.",
|
||||||
MARKTASK = "%s, %s, copy, task %03d location marked on map!",
|
MARKTASK = "%s, %s, copy, task %03d location marked on map!",
|
||||||
SMOKETASK = "%s, %s, copy, task %03d location smoked!",
|
SMOKETASK = "%s, %s, copy, task %03d location smoked!",
|
||||||
@ -1499,6 +1506,9 @@ PLAYERTASKCONTROLLER.Messages = {
|
|||||||
THREATMEDIUM = "mittel",
|
THREATMEDIUM = "mittel",
|
||||||
THREATLOW = "niedrig",
|
THREATLOW = "niedrig",
|
||||||
THREATTEXT = "%s\nGefahrstufe: %s\nZiele: %d\nKoord: %s",
|
THREATTEXT = "%s\nGefahrstufe: %s\nZiele: %d\nKoord: %s",
|
||||||
|
ELEVATION = "\nZiel Höhe: %s %s",
|
||||||
|
METER = "Meter",
|
||||||
|
FEET = "Fuss",
|
||||||
THREATTEXTTTS = "%s, %s. Zielinformation zu %s. Gefahrstufe %s. Ziele %d. Zielposition %s.",
|
THREATTEXTTTS = "%s, %s. Zielinformation zu %s. Gefahrstufe %s. Ziele %d. Zielposition %s.",
|
||||||
MARKTASK = "%s, %s, verstanden, Zielposition %03d auf der Karte markiert!",
|
MARKTASK = "%s, %s, verstanden, Zielposition %03d auf der Karte markiert!",
|
||||||
SMOKETASK = "%s, %s, verstanden, Zielposition %03d mit Rauch markiert!",
|
SMOKETASK = "%s, %s, verstanden, Zielposition %03d mit Rauch markiert!",
|
||||||
@ -1561,7 +1571,7 @@ PLAYERTASKCONTROLLER.Messages = {
|
|||||||
|
|
||||||
--- PLAYERTASK class version.
|
--- PLAYERTASK class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
PLAYERTASKCONTROLLER.version="0.1.65"
|
PLAYERTASKCONTROLLER.version="0.1.66"
|
||||||
|
|
||||||
--- Create and run a new TASKCONTROLLER instance.
|
--- Create and run a new TASKCONTROLLER instance.
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
@ -2113,10 +2123,12 @@ function PLAYERTASKCONTROLLER:_GetPlayerName(Client)
|
|||||||
local ttsplayername = nil
|
local ttsplayername = nil
|
||||||
if not self.customcallsigns[playername] then
|
if not self.customcallsigns[playername] then
|
||||||
local playergroup = Client:GetGroup()
|
local playergroup = Client:GetGroup()
|
||||||
ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
if playergroup ~= nil then
|
||||||
local newplayername = self:_GetTextForSpeech(ttsplayername)
|
ttsplayername = playergroup:GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||||
self.customcallsigns[playername] = newplayername
|
local newplayername = self:_GetTextForSpeech(ttsplayername)
|
||||||
ttsplayername = newplayername
|
self.customcallsigns[playername] = newplayername
|
||||||
|
ttsplayername = newplayername
|
||||||
|
end
|
||||||
else
|
else
|
||||||
ttsplayername = self.customcallsigns[playername]
|
ttsplayername = self.customcallsigns[playername]
|
||||||
end
|
end
|
||||||
@ -3182,7 +3194,8 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
|
|||||||
local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale)
|
local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale)
|
||||||
local taskname = string.format(tname,task.Type,task.PlayerTaskNr)
|
local taskname = string.format(tname,task.Type,task.PlayerTaskNr)
|
||||||
local ttstaskname = string.format(ttsname,task.TTSType,task.PlayerTaskNr)
|
local ttstaskname = string.format(ttsname,task.TTSType,task.PlayerTaskNr)
|
||||||
local Coordinate = task.Target:GetCoordinate() or COORDINATE:New(0,0,0)
|
local Coordinate = task.Target:GetCoordinate() or COORDINATE:New(0,0,0) -- Core.Point#COORDINATE
|
||||||
|
local Elevation = Coordinate:GetLandHeight() or 0 -- meters
|
||||||
local CoordText = ""
|
local CoordText = ""
|
||||||
local CoordTextLLDM = nil
|
local CoordTextLLDM = nil
|
||||||
if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then
|
if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then
|
||||||
@ -3207,6 +3220,17 @@ function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
|
|||||||
local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel
|
local ThreatGraph = "[" .. string.rep( "■", ThreatLevel ) .. string.rep( "□", 10 - ThreatLevel ) .. "]: "..ThreatLevel
|
||||||
local ThreatLocaleText = self.gettext:GetEntry("THREATTEXT",self.locale)
|
local ThreatLocaleText = self.gettext:GetEntry("THREATTEXT",self.locale)
|
||||||
text = string.format(ThreatLocaleText, taskname, ThreatGraph, targets, CoordText)
|
text = string.format(ThreatLocaleText, taskname, ThreatGraph, targets, CoordText)
|
||||||
|
local settings = _DATABASE:GetPlayerSettings(playername) or _SETTINGS -- Core.Settings#SETTINGS
|
||||||
|
local elevationmeasure = self.gettext:GetEntry("FEET",self.locale)
|
||||||
|
if settings:IsMetric() then
|
||||||
|
elevationmeasure = self.gettext:GetEntry("METER",self.locale)
|
||||||
|
--Elevation = math.floor(UTILS.MetersToFeet(Elevation))
|
||||||
|
else
|
||||||
|
Elevation = math.floor(UTILS.MetersToFeet(Elevation))
|
||||||
|
end
|
||||||
|
-- ELEVATION = "\nTarget Elevation: %s %s",
|
||||||
|
local elev = self.gettext:GetEntry("ELEVATION",self.locale)
|
||||||
|
text = text .. string.format(elev,tostring(math.floor(Elevation)),elevationmeasure)
|
||||||
-- Prec bombing
|
-- Prec bombing
|
||||||
if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then
|
if task.Type == AUFTRAG.Type.PRECISIONBOMBING and self.precisionbombing then
|
||||||
if self.LasingDrone and self.LasingDrone.playertask then
|
if self.LasingDrone and self.LasingDrone.playertask then
|
||||||
|
|||||||
@ -55,6 +55,7 @@ BIGSMOKEPRESET = {
|
|||||||
-- @field #string MarianaIslands Mariana Islands map.
|
-- @field #string MarianaIslands Mariana Islands map.
|
||||||
-- @field #string Falklands South Atlantic map.
|
-- @field #string Falklands South Atlantic map.
|
||||||
-- @field #string Sinai Sinai map.
|
-- @field #string Sinai Sinai map.
|
||||||
|
-- @field #string Kola Kola map.
|
||||||
DCSMAP = {
|
DCSMAP = {
|
||||||
Caucasus="Caucasus",
|
Caucasus="Caucasus",
|
||||||
NTTR="Nevada",
|
NTTR="Nevada",
|
||||||
@ -64,7 +65,8 @@ DCSMAP = {
|
|||||||
Syria="Syria",
|
Syria="Syria",
|
||||||
MarianaIslands="MarianaIslands",
|
MarianaIslands="MarianaIslands",
|
||||||
Falklands="Falklands",
|
Falklands="Falklands",
|
||||||
Sinai="SinaiMap"
|
Sinai="SinaiMap",
|
||||||
|
Kola="Kola"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -102,7 +104,7 @@ CALLSIGN={
|
|||||||
Shell=3,
|
Shell=3,
|
||||||
Navy_One=4,
|
Navy_One=4,
|
||||||
Mauler=5,
|
Mauler=5,
|
||||||
Bloodhound=6,
|
Bloodhound=6,
|
||||||
},
|
},
|
||||||
-- JTAC
|
-- JTAC
|
||||||
JTAC={
|
JTAC={
|
||||||
@ -416,7 +418,7 @@ function UTILS._OneLineSerialize(tbl)
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
tbl_str[#tbl_str + 1] = '}'
|
tbl_str[#tbl_str + 1] = '}'
|
||||||
return table.concat(tbl_str)
|
return table.concat(tbl_str)
|
||||||
else
|
else
|
||||||
@ -433,7 +435,7 @@ UTILS.BasicSerialize = function(s)
|
|||||||
if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'userdata') ) then
|
if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'userdata') ) then
|
||||||
return tostring(s)
|
return tostring(s)
|
||||||
elseif type(s) == "table" then
|
elseif type(s) == "table" then
|
||||||
return UTILS._OneLineSerialize(s)
|
return UTILS._OneLineSerialize(s)
|
||||||
elseif type(s) == 'string' then
|
elseif type(s) == 'string' then
|
||||||
s = string.format('(%s)', s)
|
s = string.format('(%s)', s)
|
||||||
return s
|
return s
|
||||||
@ -562,15 +564,15 @@ end
|
|||||||
-- @param #string fname File name.
|
-- @param #string fname File name.
|
||||||
function UTILS.Gdump(fname)
|
function UTILS.Gdump(fname)
|
||||||
if lfs and io then
|
if lfs and io then
|
||||||
|
|
||||||
local fdir = lfs.writedir() .. [[Logs\]] .. fname
|
local fdir = lfs.writedir() .. [[Logs\]] .. fname
|
||||||
|
|
||||||
local f = io.open(fdir, 'w')
|
local f = io.open(fdir, 'w')
|
||||||
|
|
||||||
f:write(UTILS.TableShow(_G))
|
f:write(UTILS.TableShow(_G))
|
||||||
|
|
||||||
f:close()
|
f:close()
|
||||||
|
|
||||||
env.info(string.format('Wrote debug data to $1', fdir))
|
env.info(string.format('Wrote debug data to $1', fdir))
|
||||||
else
|
else
|
||||||
env.error("WARNING: lfs and/or io not de-sanitized - cannot dump _G!")
|
env.error("WARNING: lfs and/or io not de-sanitized - cannot dump _G!")
|
||||||
@ -894,17 +896,17 @@ UTILS.tostringLLM2KData = function( lat, lon, acc)
|
|||||||
-- degrees, decimal minutes.
|
-- degrees, decimal minutes.
|
||||||
latMin = UTILS.Round(latMin, acc)
|
latMin = UTILS.Round(latMin, acc)
|
||||||
lonMin = UTILS.Round(lonMin, acc)
|
lonMin = UTILS.Round(lonMin, acc)
|
||||||
|
|
||||||
if latMin == 60 then
|
if latMin == 60 then
|
||||||
latMin = 0
|
latMin = 0
|
||||||
latDeg = latDeg + 1
|
latDeg = latDeg + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if lonMin == 60 then
|
if lonMin == 60 then
|
||||||
lonMin = 0
|
lonMin = 0
|
||||||
lonDeg = lonDeg + 1
|
lonDeg = lonDeg + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local minFrmtStr -- create the formatting string for the minutes place
|
local minFrmtStr -- create the formatting string for the minutes place
|
||||||
if acc <= 0 then -- no decimal place.
|
if acc <= 0 then -- no decimal place.
|
||||||
minFrmtStr = '%02d'
|
minFrmtStr = '%02d'
|
||||||
@ -912,7 +914,7 @@ UTILS.tostringLLM2KData = function( lat, lon, acc)
|
|||||||
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
||||||
minFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
minFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 024 23'N or 024 23.123'N
|
-- 024 23'N or 024 23.123'N
|
||||||
return latHemi..string.format('%02d:', latDeg) .. string.format(minFrmtStr, latMin), lonHemi..string.format('%02d:', lonDeg) .. string.format(minFrmtStr, lonMin)
|
return latHemi..string.format('%02d:', latDeg) .. string.format(minFrmtStr, latMin), lonHemi..string.format('%02d:', lonDeg) .. string.format(minFrmtStr, lonMin)
|
||||||
|
|
||||||
@ -924,9 +926,9 @@ UTILS.tostringMGRS = function(MGRS, acc) --R2.1
|
|||||||
if acc <= 0 then
|
if acc <= 0 then
|
||||||
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph
|
return MGRS.UTMZone .. ' ' .. MGRS.MGRSDigraph
|
||||||
else
|
else
|
||||||
|
|
||||||
if acc > 5 then acc = 5 end
|
if acc > 5 then acc = 5 end
|
||||||
|
|
||||||
-- Test if Easting/Northing have less than 4 digits.
|
-- Test if Easting/Northing have less than 4 digits.
|
||||||
--MGRS.Easting=123 -- should be 00123
|
--MGRS.Easting=123 -- should be 00123
|
||||||
--MGRS.Northing=5432 -- should be 05432
|
--MGRS.Northing=5432 -- should be 05432
|
||||||
@ -1409,7 +1411,7 @@ end
|
|||||||
function UTILS.VecDist2D(a, b)
|
function UTILS.VecDist2D(a, b)
|
||||||
|
|
||||||
local d = math.huge
|
local d = math.huge
|
||||||
|
|
||||||
if (not a) or (not b) then return d end
|
if (not a) or (not b) then return d end
|
||||||
|
|
||||||
local c={x=b.x-a.x, y=b.y-a.y}
|
local c={x=b.x-a.x, y=b.y-a.y}
|
||||||
@ -1425,12 +1427,12 @@ end
|
|||||||
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 b Vector in 3D with x, y, z components.
|
||||||
-- @return #number Distance between the vectors.
|
-- @return #number Distance between the vectors.
|
||||||
function UTILS.VecDist3D(a, b)
|
function UTILS.VecDist3D(a, b)
|
||||||
|
|
||||||
|
|
||||||
local d = math.huge
|
local d = math.huge
|
||||||
|
|
||||||
if (not a) or (not b) then return d end
|
if (not a) or (not b) then return d end
|
||||||
|
|
||||||
local c={x=b.x-a.x, y=b.y-a.y, z=b.z-a.z}
|
local c={x=b.x-a.x, y=b.y-a.y, z=b.z-a.z}
|
||||||
|
|
||||||
d=math.sqrt(UTILS.VecDot(c, c))
|
d=math.sqrt(UTILS.VecDot(c, c))
|
||||||
@ -1778,6 +1780,7 @@ end
|
|||||||
-- * Mariana Islands +2 (East)
|
-- * Mariana Islands +2 (East)
|
||||||
-- * Falklands +12 (East) - note there's a LOT of deviation across the map, as we're closer to the South Pole
|
-- * Falklands +12 (East) - note there's a LOT of deviation across the map, as we're closer to the South Pole
|
||||||
-- * Sinai +4.8 (East)
|
-- * Sinai +4.8 (East)
|
||||||
|
-- * Kola +15 (East) - not there is a lot of deviation across the map (-1° to +24°), as we are close to the North pole
|
||||||
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
|
-- @param #string map (Optional) Map for which the declination is returned. Default is from env.mission.theatre
|
||||||
-- @return #number Declination in degrees.
|
-- @return #number Declination in degrees.
|
||||||
function UTILS.GetMagneticDeclination(map)
|
function UTILS.GetMagneticDeclination(map)
|
||||||
@ -1804,6 +1807,8 @@ function UTILS.GetMagneticDeclination(map)
|
|||||||
declination=12
|
declination=12
|
||||||
elseif map==DCSMAP.Sinai then
|
elseif map==DCSMAP.Sinai then
|
||||||
declination=4.8
|
declination=4.8
|
||||||
|
elseif map==DCSMAP.Kola then
|
||||||
|
declination=15
|
||||||
else
|
else
|
||||||
declination=0
|
declination=0
|
||||||
end
|
end
|
||||||
@ -1871,7 +1876,7 @@ function UTILS.GetCoalitionEnemy(Coalition, Neutral)
|
|||||||
|
|
||||||
local Coalitions={}
|
local Coalitions={}
|
||||||
if Coalition then
|
if Coalition then
|
||||||
if Coalition==coalition.side.RED then
|
if Coalition==coalition.side.RED then
|
||||||
Coalitions={coalition.side.BLUE}
|
Coalitions={coalition.side.BLUE}
|
||||||
elseif Coalition==coalition.side.BLUE then
|
elseif Coalition==coalition.side.BLUE then
|
||||||
Coalitions={coalition.side.RED}
|
Coalitions={coalition.side.RED}
|
||||||
@ -1879,7 +1884,7 @@ function UTILS.GetCoalitionEnemy(Coalition, Neutral)
|
|||||||
Coalitions={coalition.side.RED, coalition.side.BLUE}
|
Coalitions={coalition.side.RED, coalition.side.BLUE}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if Neutral then
|
if Neutral then
|
||||||
table.insert(Coalitions, coalition.side.NEUTRAL)
|
table.insert(Coalitions, coalition.side.NEUTRAL)
|
||||||
end
|
end
|
||||||
@ -1910,17 +1915,17 @@ end
|
|||||||
-- @param #number Typename The type name.
|
-- @param #number Typename The type name.
|
||||||
-- @return #string The Reporting name or "Bogey".
|
-- @return #string The Reporting name or "Bogey".
|
||||||
function UTILS.GetReportingName(Typename)
|
function UTILS.GetReportingName(Typename)
|
||||||
|
|
||||||
local typename = string.lower(Typename)
|
local typename = string.lower(Typename)
|
||||||
|
|
||||||
for name, value in pairs(ENUMS.ReportingName.NATO) do
|
for name, value in pairs(ENUMS.ReportingName.NATO) do
|
||||||
local svalue = string.lower(value)
|
local svalue = string.lower(value)
|
||||||
if string.find(typename,svalue,1,true) then
|
if string.find(typename,svalue,1,true) then
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return "Bogey"
|
return "Bogey"
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the callsign name from its enumerator value
|
--- Get the callsign name from its enumerator value
|
||||||
@ -1951,49 +1956,49 @@ function UTILS.GetCallsignName(Callsign)
|
|||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, value in pairs(CALLSIGN.B1B) do
|
for name, value in pairs(CALLSIGN.B1B) do
|
||||||
if value==Callsign then
|
if value==Callsign then
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, value in pairs(CALLSIGN.B52) do
|
for name, value in pairs(CALLSIGN.B52) do
|
||||||
if value==Callsign then
|
if value==Callsign then
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, value in pairs(CALLSIGN.F15E) do
|
for name, value in pairs(CALLSIGN.F15E) do
|
||||||
if value==Callsign then
|
if value==Callsign then
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, value in pairs(CALLSIGN.F16) do
|
for name, value in pairs(CALLSIGN.F16) do
|
||||||
if value==Callsign then
|
if value==Callsign then
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, value in pairs(CALLSIGN.F18) do
|
for name, value in pairs(CALLSIGN.F18) do
|
||||||
if value==Callsign then
|
if value==Callsign then
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, value in pairs(CALLSIGN.FARP) do
|
for name, value in pairs(CALLSIGN.FARP) do
|
||||||
if value==Callsign then
|
if value==Callsign then
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, value in pairs(CALLSIGN.TransportAircraft) do
|
for name, value in pairs(CALLSIGN.TransportAircraft) do
|
||||||
if value==Callsign then
|
if value==Callsign then
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return "Ghostrider"
|
return "Ghostrider"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2020,7 +2025,9 @@ function UTILS.GMTToLocalTimeDifference()
|
|||||||
elseif theatre==DCSMAP.Falklands then
|
elseif theatre==DCSMAP.Falklands then
|
||||||
return -3 -- Fireland is UTC-3 hours.
|
return -3 -- Fireland is UTC-3 hours.
|
||||||
elseif theatre==DCSMAP.Sinai then
|
elseif theatre==DCSMAP.Sinai then
|
||||||
return 2 -- Currently map is +2 but should be +3 (DCS bug?)
|
return 2 -- Currently map is +2 but should be +3 (DCS bug?)
|
||||||
|
elseif theatre==DCSMAP.Kola then
|
||||||
|
return 3 -- Currently map is +2 but should be +3 (DCS bug?)
|
||||||
else
|
else
|
||||||
BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre)))
|
BASE:E(string.format("ERROR: Unknown Map %s in UTILS.GMTToLocal function. Returning 0", tostring(theatre)))
|
||||||
return 0
|
return 0
|
||||||
@ -2225,19 +2232,19 @@ function UTILS.GetRandomTableElement(t, replace)
|
|||||||
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
|
BASE:I("Error in ShuffleTable: Missing or wrong type of Argument")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
math.random()
|
math.random()
|
||||||
math.random()
|
math.random()
|
||||||
math.random()
|
math.random()
|
||||||
|
|
||||||
local r=math.random(#t)
|
local r=math.random(#t)
|
||||||
|
|
||||||
local element=t[r]
|
local element=t[r]
|
||||||
|
|
||||||
if not replace then
|
if not replace then
|
||||||
table.remove(t, r)
|
table.remove(t, r)
|
||||||
end
|
end
|
||||||
|
|
||||||
return element
|
return element
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2266,7 +2273,7 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
|||||||
BASE:T(unit_name .. " a side door is open ")
|
BASE:T(unit_name .. " a side door is open ")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.find(type_name, "SA342" ) and (unit:getDrawArgumentValue(34) == 1) then
|
if string.find(type_name, "SA342" ) and (unit:getDrawArgumentValue(34) == 1) then
|
||||||
BASE:T(unit_name .. " front door(s) are open or doors removed")
|
BASE:T(unit_name .. " front door(s) are open or doors removed")
|
||||||
return true
|
return true
|
||||||
@ -2291,7 +2298,7 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
|||||||
BASE:T(unit_name .. " door is open")
|
BASE:T(unit_name .. " door is open")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if type_name == "UH-60L" and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then
|
if type_name == "UH-60L" and (unit:getDrawArgumentValue(401) == 1 or unit:getDrawArgumentValue(402) == 1) then
|
||||||
BASE:T(unit_name .. " cargo door is open")
|
BASE:T(unit_name .. " cargo door is open")
|
||||||
return true
|
return true
|
||||||
@ -2301,22 +2308,27 @@ function UTILS.IsLoadingDoorOpen( unit_name )
|
|||||||
BASE:T(unit_name .. " front door(s) are open")
|
BASE:T(unit_name .. " front door(s) are open")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if type_name == "AH-64D_BLK_II" then
|
if type_name == "AH-64D_BLK_II" then
|
||||||
BASE:T(unit_name .. " front door(s) are open")
|
BASE:T(unit_name .. " front door(s) are open")
|
||||||
return true -- no doors on this one ;)
|
return true -- no doors on this one ;)
|
||||||
end
|
end
|
||||||
|
|
||||||
if type_name == "Bronco-OV-10A" then
|
if type_name == "Bronco-OV-10A" then
|
||||||
BASE:T(unit_name .. " front door(s) are open")
|
BASE:T(unit_name .. " front door(s) are open")
|
||||||
return true -- no doors on this one ;)
|
return true -- no doors on this one ;)
|
||||||
end
|
end
|
||||||
|
|
||||||
if type_name == "MH-60R" and (unit:getDrawArgumentValue(403) > 0 or unit:getDrawArgumentValue(403) == -1) then
|
if type_name == "MH-60R" and (unit:getDrawArgumentValue(403) > 0 or unit:getDrawArgumentValue(403) == -1) then
|
||||||
BASE:T(unit_name .. " cargo door is open")
|
BASE:T(unit_name .. " cargo door is open")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if type_name == " OH-58D" and (unit:getDrawArgumentValue(35) > 0 or unit:getDrawArgumentValue(421) == -1) then
|
||||||
|
BASE:T(unit_name .. " cargo door is open")
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
||||||
end -- nil
|
end -- nil
|
||||||
@ -2425,7 +2437,7 @@ function UTILS.GenerateUHFrequencies(Start,End)
|
|||||||
|
|
||||||
local FreeUHFFrequencies = {}
|
local FreeUHFFrequencies = {}
|
||||||
local _start = 220000000
|
local _start = 220000000
|
||||||
|
|
||||||
if not Start then
|
if not Start then
|
||||||
while _start < 399000000 do
|
while _start < 399000000 do
|
||||||
if _start ~= 243000000 then
|
if _start ~= 243000000 then
|
||||||
@ -2436,7 +2448,7 @@ function UTILS.GenerateUHFrequencies(Start,End)
|
|||||||
else
|
else
|
||||||
local myend = End*1000000 or 399000000
|
local myend = End*1000000 or 399000000
|
||||||
local mystart = Start*1000000 or 220000000
|
local mystart = Start*1000000 or 220000000
|
||||||
|
|
||||||
while _start < 399000000 do
|
while _start < 399000000 do
|
||||||
if _start ~= 243000000 and (_start < mystart or _start > myend) then
|
if _start ~= 243000000 and (_start < mystart or _start > myend) then
|
||||||
print(_start)
|
print(_start)
|
||||||
@ -2444,10 +2456,10 @@ function UTILS.GenerateUHFrequencies(Start,End)
|
|||||||
end
|
end
|
||||||
_start = _start + 500000
|
_start = _start + 500000
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return FreeUHFFrequencies
|
return FreeUHFFrequencies
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2488,7 +2500,7 @@ function UTILS.GenerateLaserCodes()
|
|||||||
return jtacGeneratedLaserCodes
|
return jtacGeneratedLaserCodes
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Ensure the passed object is a table.
|
--- Ensure the passed object is a table.
|
||||||
-- @param #table Object The object that should be a table.
|
-- @param #table Object The object that should be a table.
|
||||||
-- @param #boolean ReturnNil If `true`, return `#nil` if `Object` is nil. Otherwise an empty table `{}` is returned.
|
-- @param #boolean ReturnNil If `true`, return `#nil` if `Object` is nil. Otherwise an empty table `{}` is returned.
|
||||||
-- @return #table The object that now certainly *is* a table.
|
-- @return #table The object that now certainly *is* a table.
|
||||||
@ -2500,11 +2512,11 @@ function UTILS.EnsureTable(Object, ReturnNil)
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
if ReturnNil then
|
if ReturnNil then
|
||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
Object={}
|
Object={}
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return Object
|
return Object
|
||||||
@ -2516,30 +2528,30 @@ end
|
|||||||
-- @param #table Data The LUA data structure to save. This will be e.g. a table of text lines with an \\n at the end of each line.
|
-- @param #table Data The LUA data structure to save. This will be e.g. a table of text lines with an \\n at the end of each line.
|
||||||
-- @return #boolean outcome True if saving is possible, else false.
|
-- @return #boolean outcome True if saving is possible, else false.
|
||||||
function UTILS.SaveToFile(Path,Filename,Data)
|
function UTILS.SaveToFile(Path,Filename,Data)
|
||||||
-- Thanks to @FunkyFranky
|
-- Thanks to @FunkyFranky
|
||||||
-- Check io module is available.
|
-- Check io module is available.
|
||||||
if not io then
|
if not io then
|
||||||
BASE:E("ERROR: io not desanitized. Can't save current file.")
|
BASE:E("ERROR: io not desanitized. Can't save current file.")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check default path.
|
-- Check default path.
|
||||||
if Path==nil and not lfs then
|
if Path==nil and not lfs then
|
||||||
BASE:E("WARNING: lfs not desanitized. File will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
BASE:E("WARNING: lfs not desanitized. File will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set path or default.
|
-- Set path or default.
|
||||||
local path = nil
|
local path = nil
|
||||||
if lfs then
|
if lfs then
|
||||||
path=Path or lfs.writedir()
|
path=Path or lfs.writedir()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set file name.
|
-- Set file name.
|
||||||
local filename=Filename
|
local filename=Filename
|
||||||
if path~=nil then
|
if path~=nil then
|
||||||
filename=path.."\\"..filename
|
filename=path.."\\"..filename
|
||||||
end
|
end
|
||||||
|
|
||||||
-- write
|
-- write
|
||||||
local f = assert(io.open(filename, "wb"))
|
local f = assert(io.open(filename, "wb"))
|
||||||
f:write(Data)
|
f:write(Data)
|
||||||
@ -2547,43 +2559,43 @@ function UTILS.SaveToFile(Path,Filename,Data)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function to save an object to a file
|
--- Function to load an object from a 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.
|
||||||
-- @return #boolean outcome True if reading is possible and successful, else false.
|
-- @return #boolean outcome True if reading is possible and successful, else false.
|
||||||
-- @return #table data The data read from the filesystem (table of lines of text). Each line is one single #string!
|
-- @return #table data The data read from the filesystem (table of lines of text). Each line is one single #string!
|
||||||
function UTILS.LoadFromFile(Path,Filename)
|
function UTILS.LoadFromFile(Path,Filename)
|
||||||
-- Thanks to @FunkyFranky
|
-- Thanks to @FunkyFranky
|
||||||
-- Check io module is available.
|
-- Check io module is available.
|
||||||
if not io then
|
if not io then
|
||||||
BASE:E("ERROR: io not desanitized. Can't save current state.")
|
BASE:E("ERROR: io not desanitized. Can't save current state.")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check default path.
|
-- Check default path.
|
||||||
if Path==nil and not lfs then
|
if Path==nil and not lfs then
|
||||||
BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set path or default.
|
-- Set path or default.
|
||||||
local path = nil
|
local path = nil
|
||||||
if lfs then
|
if lfs then
|
||||||
path=Path or lfs.writedir()
|
path=Path or lfs.writedir()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set file name.
|
-- Set file name.
|
||||||
local filename=Filename
|
local filename=Filename
|
||||||
if path~=nil then
|
if path~=nil then
|
||||||
filename=path.."\\"..filename
|
filename=path.."\\"..filename
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if file exists.
|
-- Check if file exists.
|
||||||
local exists=UTILS.CheckFileExists(Path,Filename)
|
local exists=UTILS.CheckFileExists(Path,Filename)
|
||||||
if not exists then
|
if not exists then
|
||||||
BASE:I(string.format("ERROR: File %s does not exist!",filename))
|
BASE:I(string.format("ERROR: File %s does not exist!",filename))
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- read
|
-- read
|
||||||
local file=assert(io.open(filename, "rb"))
|
local file=assert(io.open(filename, "rb"))
|
||||||
local loadeddata = {}
|
local loadeddata = {}
|
||||||
@ -2610,30 +2622,30 @@ function UTILS.CheckFileExists(Path,Filename)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check io module is available.
|
-- Check io module is available.
|
||||||
if not io then
|
if not io then
|
||||||
BASE:E("ERROR: io not desanitized.")
|
BASE:E("ERROR: io not desanitized.")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check default path.
|
-- Check default path.
|
||||||
if Path==nil and not lfs then
|
if Path==nil and not lfs then
|
||||||
BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
BASE:E("WARNING: lfs not desanitized. Loading will look into your DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set path or default.
|
-- Set path or default.
|
||||||
local path = nil
|
local path = nil
|
||||||
if lfs then
|
if lfs then
|
||||||
path=Path or lfs.writedir()
|
path=Path or lfs.writedir()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set file name.
|
-- Set file name.
|
||||||
local filename=Filename
|
local filename=Filename
|
||||||
if path~=nil then
|
if path~=nil then
|
||||||
filename=path.."\\"..filename
|
filename=path.."\\"..filename
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if file exists.
|
-- Check if file exists.
|
||||||
local exists=_fileexists(filename)
|
local exists=_fileexists(filename)
|
||||||
if not exists then
|
if not exists then
|
||||||
@ -2670,7 +2682,7 @@ end
|
|||||||
-- @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).
|
||||||
-- These groups are supposed to be put on the map in the ME and have *not* moved (e.g. stationary SAM sites).
|
-- These groups are supposed to be put on the map in the ME and have *not* moved (e.g. stationary SAM sites).
|
||||||
-- 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.
|
||||||
@ -2709,12 +2721,12 @@ end
|
|||||||
-- @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.
|
||||||
-- The idea is to respawn the groups **spawned during an earlier run of the mission** at the given location and reduce
|
-- The idea is to respawn the groups **spawned during an earlier run of the mission** at the given location and reduce
|
||||||
-- the number of units in the group when reloading the data again to restart the saved mission. Note that *dead* groups
|
-- the number of units in the group when reloading the data again to restart the saved mission. Note that *dead* groups
|
||||||
-- cannot be covered with this.
|
-- cannot be covered with this.
|
||||||
-- **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,Structured)
|
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"
|
||||||
@ -2724,9 +2736,12 @@ function UTILS.SaveSetOfGroups(Set,Path,Filename,Structured)
|
|||||||
if group and group:IsAlive() then
|
if group and group:IsAlive() then
|
||||||
local name = group:GetName()
|
local name = group:GetName()
|
||||||
local template = string.gsub(name,"-(.+)$","")
|
local template = string.gsub(name,"-(.+)$","")
|
||||||
|
if string.find(name,"AID") then
|
||||||
|
template = string.gsub(name,"(.AID.%d+$","")
|
||||||
|
end
|
||||||
if string.find(template,"#") then
|
if string.find(template,"#") then
|
||||||
template = string.gsub(name,"#(%d+)$","")
|
template = string.gsub(name,"#(%d+)$","")
|
||||||
end
|
end
|
||||||
local units = group:CountAliveUnits()
|
local units = group:CountAliveUnits()
|
||||||
local position = group:GetVec3()
|
local position = group:GetVec3()
|
||||||
if Structured then
|
if Structured then
|
||||||
@ -2738,7 +2753,7 @@ function UTILS.SaveSetOfGroups(Set,Path,Filename,Structured)
|
|||||||
data = string.format("%s%s,%s,%d,%d,%d,%d,%s\n",data,name,template,units,position.x,position.y,position.z,strucdata)
|
data = string.format("%s%s,%s,%d,%d,%d,%d,%s\n",data,name,template,units,position.x,position.y,position.z,strucdata)
|
||||||
else
|
else
|
||||||
data = string.format("%s%s,%s,%d,%d,%d,%d\n",data,name,template,units,position.x,position.y,position.z)
|
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
|
end
|
||||||
-- save the data
|
-- save the data
|
||||||
@ -2809,16 +2824,16 @@ end
|
|||||||
-- @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.
|
||||||
-- @return #table When using Cinematic: table of names of smoke and fire objects, so they can be extinguished with `COORDINATE.StopBigSmokeAndFire( name )`
|
-- @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)
|
function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce,Structured,Cinematic,Effect,Density)
|
||||||
|
|
||||||
local fires = {}
|
local fires = {}
|
||||||
|
|
||||||
local function Smokers(name,coord,effect,density)
|
local function Smokers(name,coord,effect,density)
|
||||||
local eff = math.random(8)
|
local eff = math.random(8)
|
||||||
if type(effect) == "number" then eff = effect end
|
if type(effect) == "number" then eff = effect end
|
||||||
coord:BigSmokeAndFire(eff,density,name)
|
coord:BigSmokeAndFire(eff,density,name)
|
||||||
table.insert(fires,name)
|
table.insert(fires,name)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function Cruncher(group,typename,anzahl)
|
local function Cruncher(group,typename,anzahl)
|
||||||
local units = group:GetUnits()
|
local units = group:GetUnits()
|
||||||
local reduced = 0
|
local reduced = 0
|
||||||
@ -2836,7 +2851,7 @@ function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce,Structured,Cinema
|
|||||||
end
|
end
|
||||||
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"
|
||||||
@ -2878,13 +2893,13 @@ function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce,Structured,Cinema
|
|||||||
end
|
end
|
||||||
local reduce = false
|
local reduce = false
|
||||||
if loadednumber < _number then reduce = true end
|
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)))
|
--BASE:I(string.format("Looking at: %s | Original number: %d | Loaded number: %d | Reduce: %s",_name,_number,loadednumber,tostring(reduce)))
|
||||||
|
|
||||||
if reduce then
|
if reduce then
|
||||||
Cruncher(actualgroup,_name,_number-loadednumber)
|
Cruncher(actualgroup,_name,_number-loadednumber)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local reduction = actualgroup:CountAliveUnits() - size
|
local reduction = actualgroup:CountAliveUnits() - size
|
||||||
@ -2899,7 +2914,7 @@ function UTILS.LoadStationaryListOfGroups(Path,Filename,Reduce,Structured,Cinema
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(datatable,data)
|
table.insert(datatable,data)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@ -2914,11 +2929,11 @@ end
|
|||||||
-- @param #boolean Cinematic (Optional, needs Structured=true) If true, place a fire/smoke effect on the dead static position.
|
-- @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 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.
|
-- @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 }`
|
||||||
-- @return #table When using Cinematic: table of names of smoke and fire objects, so they can be extinguished with `COORDINATE.StopBigSmokeAndFire( name )`
|
-- @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)
|
function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,Density)
|
||||||
|
|
||||||
local fires = {}
|
local fires = {}
|
||||||
local usedtemplates = {}
|
local usedtemplates = {}
|
||||||
local spawn = true
|
local spawn = true
|
||||||
@ -2926,14 +2941,14 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,D
|
|||||||
local filename = Filename or "SetOfGroups"
|
local filename = Filename or "SetOfGroups"
|
||||||
local setdata = SET_GROUP:New()
|
local setdata = SET_GROUP:New()
|
||||||
local datatable = {}
|
local datatable = {}
|
||||||
|
|
||||||
local function Smokers(name,coord,effect,density)
|
local function Smokers(name,coord,effect,density)
|
||||||
local eff = math.random(8)
|
local eff = math.random(8)
|
||||||
if type(effect) == "number" then eff = effect end
|
if type(effect) == "number" then eff = effect end
|
||||||
coord:BigSmokeAndFire(eff,density,name)
|
coord:BigSmokeAndFire(eff,density,name)
|
||||||
table.insert(fires,name)
|
table.insert(fires,name)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function Cruncher(group,typename,anzahl)
|
local function Cruncher(group,typename,anzahl)
|
||||||
local units = group:GetUnits()
|
local units = group:GetUnits()
|
||||||
local reduced = 0
|
local reduced = 0
|
||||||
@ -2951,7 +2966,7 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,D
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function PostSpawn(args)
|
local function PostSpawn(args)
|
||||||
local spwndgrp = args[1]
|
local spwndgrp = args[1]
|
||||||
local size = args[2]
|
local size = args[2]
|
||||||
@ -2961,16 +2976,16 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,D
|
|||||||
local actualsize = spwndgrp:CountAliveUnits()
|
local actualsize = spwndgrp:CountAliveUnits()
|
||||||
if actualsize > size then
|
if actualsize > size then
|
||||||
if Structured and structure then
|
if Structured and structure then
|
||||||
|
|
||||||
local loadedstructure = {}
|
local loadedstructure = {}
|
||||||
local strcset = UTILS.Split(structure,";")
|
local strcset = UTILS.Split(structure,";")
|
||||||
for _,_data in pairs(strcset) do
|
for _,_data in pairs(strcset) do
|
||||||
local datasplit = UTILS.Split(_data,"==")
|
local datasplit = UTILS.Split(_data,"==")
|
||||||
loadedstructure[datasplit[1]] = tonumber(datasplit[2])
|
loadedstructure[datasplit[1]] = tonumber(datasplit[2])
|
||||||
end
|
end
|
||||||
|
|
||||||
local originalstructure = UTILS.GetCountPerTypeName(spwndgrp)
|
local originalstructure = UTILS.GetCountPerTypeName(spwndgrp)
|
||||||
|
|
||||||
for _name,_number in pairs(originalstructure) do
|
for _name,_number in pairs(originalstructure) do
|
||||||
local loadednumber = 0
|
local loadednumber = 0
|
||||||
if loadedstructure[_name] then
|
if loadedstructure[_name] then
|
||||||
@ -2978,11 +2993,11 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,D
|
|||||||
end
|
end
|
||||||
local reduce = false
|
local reduce = false
|
||||||
if loadednumber < _number then reduce = true end
|
if loadednumber < _number then reduce = true end
|
||||||
|
|
||||||
if reduce then
|
if reduce then
|
||||||
Cruncher(spwndgrp,_name,_number-loadednumber)
|
Cruncher(spwndgrp,_name,_number-loadednumber)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local reduction = actualsize-size
|
local reduction = actualsize-size
|
||||||
@ -2995,16 +3010,16 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,D
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function MultiUse(Data)
|
local function MultiUse(Data)
|
||||||
local template = Data.template
|
local template = Data.template
|
||||||
if template and usedtemplates[template] and usedtemplates[template].used and usedtemplates[template].used > 1 then
|
if template and usedtemplates[template] and usedtemplates[template].used and usedtemplates[template].used > 1 then
|
||||||
-- multispawn
|
-- multispawn
|
||||||
if not usedtemplates[template].done then
|
if not usedtemplates[template].done then
|
||||||
local spwnd = 0
|
local spwnd = 0
|
||||||
local spawngrp = SPAWN:New(template)
|
local spawngrp = SPAWN:New(template)
|
||||||
spawngrp:InitLimit(0,usedtemplates[template].used)
|
spawngrp:InitLimit(0,usedtemplates[template].used)
|
||||||
for _,_entry in pairs(usedtemplates[template].data) do
|
for _,_entry in pairs(usedtemplates[template].data) do
|
||||||
spwnd = spwnd + 1
|
spwnd = spwnd + 1
|
||||||
local sgrp=spawngrp:SpawnFromCoordinate(_entry.coordinate,spwnd)
|
local sgrp=spawngrp:SpawnFromCoordinate(_entry.coordinate,spwnd)
|
||||||
BASE:ScheduleOnce(0.5,PostSpawn,{sgrp,_entry.size,_entry.structure})
|
BASE:ScheduleOnce(0.5,PostSpawn,{sgrp,_entry.size,_entry.structure})
|
||||||
@ -3016,7 +3031,7 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,D
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--BASE:I("Spawn = "..tostring(spawn))
|
--BASE:I("Spawn = "..tostring(spawn))
|
||||||
if UTILS.CheckFileExists(Path,filename) then
|
if UTILS.CheckFileExists(Path,filename) then
|
||||||
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
|
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
|
||||||
@ -3050,13 +3065,13 @@ function UTILS.LoadSetOfGroups(Path,Filename,Spawn,Structured,Cinematic,Effect,D
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for _id,_entry in pairs (datatable) do
|
for _id,_entry in pairs (datatable) do
|
||||||
if spawn and not MultiUse(_entry) and _entry.size > 0 then
|
if spawn and not MultiUse(_entry) and _entry.size > 0 then
|
||||||
local group = SPAWN:New(_entry.template)
|
local group = SPAWN:New(_entry.template)
|
||||||
local sgrp=group:SpawnFromCoordinate(_entry.coordinate)
|
local sgrp=group:SpawnFromCoordinate(_entry.coordinate)
|
||||||
BASE:ScheduleOnce(0.5,PostSpawn,{sgrp,_entry.size,_entry.structure})
|
BASE:ScheduleOnce(0.5,PostSpawn,{sgrp,_entry.size,_entry.structure})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@ -3085,7 +3100,7 @@ function UTILS.LoadSetOfStatics(Path,Filename)
|
|||||||
if StaticObject then
|
if StaticObject then
|
||||||
datatable:AddObject(StaticObject)
|
datatable:AddObject(StaticObject)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@ -3101,7 +3116,7 @@ end
|
|||||||
-- @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 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.
|
-- @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 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 )`
|
-- @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,Dead,Cinematic,Effect,Density)
|
function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce,Dead,Cinematic,Effect,Density)
|
||||||
local fires = {}
|
local fires = {}
|
||||||
@ -3137,7 +3152,7 @@ function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce,Dead,Cinematic,E
|
|||||||
if Cinematic then
|
if Cinematic then
|
||||||
local effect = math.random(8)
|
local effect = math.random(8)
|
||||||
if type(Effect) == "number" then
|
if type(Effect) == "number" then
|
||||||
effect = Effect
|
effect = Effect
|
||||||
end
|
end
|
||||||
coord:BigSmokeAndFire(effect,Density,staticname)
|
coord:BigSmokeAndFire(effect,Density,staticname)
|
||||||
table.insert(fires,staticname)
|
table.insert(fires,staticname)
|
||||||
@ -3147,7 +3162,7 @@ function UTILS.LoadStationaryListOfStatics(Path,Filename,Reduce,Dead,Cinematic,E
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@ -3251,10 +3266,10 @@ function UTILS.ToStringBRAANATO(FromGrp,ToGrp)
|
|||||||
if aspect == "" then
|
if aspect == "" then
|
||||||
BRAANATO = string.format("%s, BRA, %03d, %d miles, Angels %d, Track %s",GroupWords,bearing, rangeNM, alt, track)
|
BRAANATO = string.format("%s, BRA, %03d, %d miles, Angels %d, Track %s",GroupWords,bearing, rangeNM, alt, track)
|
||||||
else
|
else
|
||||||
BRAANATO = string.format("%s, BRAA, %03d, %d miles, Angels %d, %s, Track %s",GroupWords, bearing, rangeNM, alt, aspect, track)
|
BRAANATO = string.format("%s, BRAA, %03d, %d miles, Angels %d, %s, Track %s",GroupWords, bearing, rangeNM, alt, aspect, track)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return BRAANATO
|
return BRAANATO
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if an object is contained in a table.
|
--- Check if an object is contained in a table.
|
||||||
@ -3299,7 +3314,7 @@ function UTILS.IsAnyInTable(Table, Objects, Key)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
@ -3315,30 +3330,30 @@ end
|
|||||||
-- @param #table Color Color of the line in RGB, e.g. {1,0,0} for red
|
-- @param #table Color Color of the line in RGB, e.g. {1,0,0} for red
|
||||||
-- @param #number Alpha Transparency factor, between 0.1 and 1
|
-- @param #number Alpha Transparency factor, between 0.1 and 1
|
||||||
-- @param #number LineType Line type to be used, line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
-- @param #number LineType Line type to be used, line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
-- @param #boolean ReadOnly
|
-- @param #boolean ReadOnly
|
||||||
function UTILS.PlotRacetrack(Coordinate, Altitude, Speed, Heading, Leg, Coalition, Color, Alpha, LineType, ReadOnly)
|
function UTILS.PlotRacetrack(Coordinate, Altitude, Speed, Heading, Leg, Coalition, Color, Alpha, LineType, ReadOnly)
|
||||||
local fix_coordinate = Coordinate
|
local fix_coordinate = Coordinate
|
||||||
local altitude = Altitude
|
local altitude = Altitude
|
||||||
local speed = Speed or 350
|
local speed = Speed or 350
|
||||||
local heading = Heading or 270
|
local heading = Heading or 270
|
||||||
local leg_distance = Leg or 10
|
local leg_distance = Leg or 10
|
||||||
|
|
||||||
local coalition = Coalition or -1
|
local coalition = Coalition or -1
|
||||||
local color = Color or {1,0,0}
|
local color = Color or {1,0,0}
|
||||||
local alpha = Alpha or 1
|
local alpha = Alpha or 1
|
||||||
local lineType = LineType or 1
|
local lineType = LineType or 1
|
||||||
|
|
||||||
|
|
||||||
speed = UTILS.IasToTas(speed, UTILS.FeetToMeters(altitude), oatcorr)
|
speed = UTILS.IasToTas(speed, UTILS.FeetToMeters(altitude), oatcorr)
|
||||||
|
|
||||||
local turn_radius = 0.0211 * speed -3.01
|
local turn_radius = 0.0211 * speed -3.01
|
||||||
|
|
||||||
local point_two = fix_coordinate:Translate(UTILS.NMToMeters(leg_distance), heading, true, false)
|
local point_two = fix_coordinate:Translate(UTILS.NMToMeters(leg_distance), heading, true, false)
|
||||||
local point_three = point_two:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false)
|
local point_three = point_two:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false)
|
||||||
local point_four = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false)
|
local point_four = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false)
|
||||||
local circle_center_fix_four = point_two:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false)
|
local circle_center_fix_four = point_two:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false)
|
||||||
local circle_center_two_three = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false)
|
local circle_center_two_three = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false)
|
||||||
|
|
||||||
|
|
||||||
fix_coordinate:LineToAll(point_two, coalition, color, alpha, lineType)
|
fix_coordinate:LineToAll(point_two, coalition, color, alpha, lineType)
|
||||||
point_four:LineToAll(point_three, coalition, color, alpha, lineType)
|
point_four:LineToAll(point_three, coalition, color, alpha, lineType)
|
||||||
@ -4012,3 +4027,46 @@ function UTILS.ClockHeadingString(refHdg,tgtHdg)
|
|||||||
local clockPos = math.ceil((relativeAngle % 360) / 30)
|
local clockPos = math.ceil((relativeAngle % 360) / 30)
|
||||||
return clockPos.." o'clock"
|
return clockPos.." o'clock"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a NATO abbreviated MGRS text for SRS use, optionally with prosody slow tag
|
||||||
|
-- @param #string Text The input string, e.g. "MGRS 4Q FJ 12345 67890"
|
||||||
|
-- @param #boolean Slow Optional - add slow tags
|
||||||
|
-- @return #string Output for (Slow) spelling in SRS TTS e.g. "MGRS;<prosody rate="slow">4;Quebec;Foxtrot;Juliett;1;2;3;4;5;6;7;8;niner;zero;</prosody>"
|
||||||
|
function UTILS.MGRSStringToSRSFriendly(Text,Slow)
|
||||||
|
local Text = string.gsub(Text,"MGRS ","")
|
||||||
|
Text = string.gsub(Text,"%s+","")
|
||||||
|
Text = string.gsub(Text,"([%a%d])","%1;") -- "0;5;1;"
|
||||||
|
Text = string.gsub(Text,"A","Alpha")
|
||||||
|
Text = string.gsub(Text,"B","Bravo")
|
||||||
|
Text = string.gsub(Text,"C","Charlie")
|
||||||
|
Text = string.gsub(Text,"D","Delta")
|
||||||
|
Text = string.gsub(Text,"E","Echo")
|
||||||
|
Text = string.gsub(Text,"F","Foxtrot")
|
||||||
|
Text = string.gsub(Text,"G","Golf")
|
||||||
|
Text = string.gsub(Text,"H","Hotel")
|
||||||
|
Text = string.gsub(Text,"I","India")
|
||||||
|
Text = string.gsub(Text,"J","Juliett")
|
||||||
|
Text = string.gsub(Text,"K","Kilo")
|
||||||
|
Text = string.gsub(Text,"L","Lima")
|
||||||
|
Text = string.gsub(Text,"M","Mike")
|
||||||
|
Text = string.gsub(Text,"N","November")
|
||||||
|
Text = string.gsub(Text,"O","Oscar")
|
||||||
|
Text = string.gsub(Text,"P","Papa")
|
||||||
|
Text = string.gsub(Text,"Q","Quebec")
|
||||||
|
Text = string.gsub(Text,"R","Romeo")
|
||||||
|
Text = string.gsub(Text,"S","Sierra")
|
||||||
|
Text = string.gsub(Text,"T","Tango")
|
||||||
|
Text = string.gsub(Text,"U","Uniform")
|
||||||
|
Text = string.gsub(Text,"V","Victor")
|
||||||
|
Text = string.gsub(Text,"W","Whiskey")
|
||||||
|
Text = string.gsub(Text,"X","Xray")
|
||||||
|
Text = string.gsub(Text,"Y","Yankee")
|
||||||
|
Text = string.gsub(Text,"Z","Zulu")
|
||||||
|
Text = string.gsub(Text,"0","zero")
|
||||||
|
Text = string.gsub(Text,"9","niner")
|
||||||
|
if Slow then
|
||||||
|
Text = '<prosody rate="slow">'..Text..'</prosody>'
|
||||||
|
end
|
||||||
|
Text = "MGRS;"..Text
|
||||||
|
return Text
|
||||||
|
end
|
||||||
|
|||||||
@ -722,6 +722,39 @@ AIRBASE.Sinai = {
|
|||||||
["Wadi_al_Jandali"] = "Wadi al Jandali",
|
["Wadi_al_Jandali"] = "Wadi al Jandali",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- Airbases of the Kola map
|
||||||
|
--
|
||||||
|
-- * AIRBASE.Kola.Banak
|
||||||
|
-- * AIRBASE.Kola.Bas_100
|
||||||
|
-- * AIRBASE.Kola.Bodo
|
||||||
|
-- * AIRBASE.Kola.Jokkmokk
|
||||||
|
-- * AIRBASE.Kola.Kalixfors
|
||||||
|
-- * AIRBASE.Kola.Kemi_Tornio
|
||||||
|
-- * AIRBASE.Kola.Kiruna
|
||||||
|
-- * AIRBASE.Kola.Monchegorsk
|
||||||
|
-- * AIRBASE.Kola.Murmansk_International
|
||||||
|
-- * AIRBASE.Kola.Olenya
|
||||||
|
-- * AIRBASE.Kola.Rovaniemi
|
||||||
|
-- * AIRBASE.Kola.Severomorsk_1
|
||||||
|
-- * AIRBASE.Kola.Severomorsk_3
|
||||||
|
--
|
||||||
|
-- @field Kola
|
||||||
|
AIRBASE.Kola = {
|
||||||
|
["Banak"] = "Banak",
|
||||||
|
["Bas_100"] = "Bas 100",
|
||||||
|
["Bodo"] = "Bodo",
|
||||||
|
["Jokkmokk"] = "Jokkmokk",
|
||||||
|
["Kalixfors"] = "Kalixfors",
|
||||||
|
["Kemi_Tornio"] = "Kemi Tornio",
|
||||||
|
["Kiruna"] = "Kiruna",
|
||||||
|
["Monchegorsk"] = "Monchegorsk",
|
||||||
|
["Murmansk_International"] = "Murmansk International",
|
||||||
|
["Olenya"] = "Olenya",
|
||||||
|
["Rovaniemi"] = "Rovaniemi",
|
||||||
|
["Severomorsk_1"] = "Severomorsk-1",
|
||||||
|
["Severomorsk_3"] = "Severomorsk-3",
|
||||||
|
}
|
||||||
|
|
||||||
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||||
-- @type AIRBASE.ParkingSpot
|
-- @type AIRBASE.ParkingSpot
|
||||||
-- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot.
|
-- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot.
|
||||||
|
|||||||
@ -367,7 +367,7 @@ function GROUP:GetDCSObject()
|
|||||||
return DCSGroup
|
return DCSGroup
|
||||||
end
|
end
|
||||||
|
|
||||||
self:E(string.format("ERROR: Could not get DCS group object of group %s because DCS object could not be found!", tostring(self.GroupName)))
|
self:T2(string.format("ERROR: Could not get DCS group object of group %s because DCS object could not be found!", tostring(self.GroupName)))
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1228,15 +1228,17 @@ function GROUP:GetCoordinate()
|
|||||||
-- no luck, try the API way
|
-- no luck, try the API way
|
||||||
|
|
||||||
local DCSGroup = Group.getByName(self.GroupName)
|
local DCSGroup = Group.getByName(self.GroupName)
|
||||||
local DCSUnits = DCSGroup:getUnits() or {}
|
if DCSGroup then
|
||||||
for _,_unit in pairs(DCSUnits) do
|
local DCSUnits = DCSGroup:getUnits() or {}
|
||||||
if Object.isExist(_unit) then
|
for _,_unit in pairs(DCSUnits) do
|
||||||
local position = _unit:getPosition()
|
if Object.isExist(_unit) then
|
||||||
local point = position.p ~= nil and position.p or _unit:GetPoint()
|
local position = _unit:getPosition()
|
||||||
if point then
|
local point = position.p ~= nil and position.p or _unit:GetPoint()
|
||||||
--self:I(point)
|
if point then
|
||||||
local coord = COORDINATE:NewFromVec3(point)
|
--self:I(point)
|
||||||
return coord
|
local coord = COORDINATE:NewFromVec3(point)
|
||||||
|
return coord
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1794,10 +1796,14 @@ end
|
|||||||
|
|
||||||
--- Returns the group template from the global _DATABASE object (an instance of @{Core.Database#DATABASE}).
|
--- Returns the group template from the global _DATABASE object (an instance of @{Core.Database#DATABASE}).
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #table
|
-- @return #table Template table.
|
||||||
function GROUP:GetTemplate()
|
function GROUP:GetTemplate()
|
||||||
local GroupName = self:GetName()
|
local GroupName = self:GetName()
|
||||||
return UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
|
local template=_DATABASE:GetGroupTemplate( GroupName )
|
||||||
|
if template then
|
||||||
|
return UTILS.DeepCopy( template )
|
||||||
|
end
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the group template route.points[] (the waypoints) from the global _DATABASE object (an instance of @{Core.Database#DATABASE}).
|
--- Returns the group template route.points[] (the waypoints) from the global _DATABASE object (an instance of @{Core.Database#DATABASE}).
|
||||||
|
|||||||
@ -40,6 +40,7 @@
|
|||||||
-- @field #number coalition Coalition ID.
|
-- @field #number coalition Coalition ID.
|
||||||
-- @field #number country Country ID.
|
-- @field #number country Country ID.
|
||||||
-- @field DCS#Desc desc Descriptor table.
|
-- @field DCS#Desc desc Descriptor table.
|
||||||
|
-- @field DCS#Desc guidance Missile guidance descriptor.
|
||||||
-- @field DCS#Unit launcher Launcher DCS unit.
|
-- @field DCS#Unit launcher Launcher DCS unit.
|
||||||
-- @field Wrapper.Unit#UNIT launcherUnit Launcher Unit.
|
-- @field Wrapper.Unit#UNIT launcherUnit Launcher Unit.
|
||||||
-- @field #string launcherName Name of launcher unit.
|
-- @field #string launcherName Name of launcher unit.
|
||||||
@ -196,6 +197,9 @@ function WEAPON:New(WeaponObject)
|
|||||||
|
|
||||||
if self:IsMissile() and self.desc.missileCategory then
|
if self:IsMissile() and self.desc.missileCategory then
|
||||||
self.categoryMissile=self.desc.missileCategory
|
self.categoryMissile=self.desc.missileCategory
|
||||||
|
if self.desc.guidance then
|
||||||
|
self.guidance = self.desc.guidance
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get type name.
|
-- Get type name.
|
||||||
@ -667,6 +671,26 @@ function WEAPON:IsTorpedo()
|
|||||||
return self.category==Weapon.Category.TORPEDO
|
return self.category==Weapon.Category.TORPEDO
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a Fox One missile (Radar Semi-Active).
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a Fox One.
|
||||||
|
function WEAPON:IsFoxOne()
|
||||||
|
return self.guidance==Weapon.GuidanceType.RADAR_SEMI_ACTIVE
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a Fox Two missile (IR guided).
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a Fox Two.
|
||||||
|
function WEAPON:IsFoxTwo()
|
||||||
|
return self.guidance==Weapon.GuidanceType.IR
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a Fox Three missile (Radar Active).
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a Fox Three.
|
||||||
|
function WEAPON:IsFoxThree()
|
||||||
|
return self.guidance==Weapon.GuidanceType.RADAR_ACTIVE
|
||||||
|
end
|
||||||
|
|
||||||
--- Destroy the weapon object.
|
--- Destroy the weapon object.
|
||||||
-- @param #WEAPON self
|
-- @param #WEAPON self
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user