mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'master' into FF/MasterDevel
This commit is contained in:
commit
4eea8fcadd
@ -1348,7 +1348,8 @@ function EVENT:onEvent( Event )
|
|||||||
Event.Weapon = Event.weapon
|
Event.Weapon = Event.weapon
|
||||||
Event.WeaponName = Event.Weapon:getTypeName()
|
Event.WeaponName = Event.Weapon:getTypeName()
|
||||||
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
Event.WeaponUNIT = CLIENT:Find( Event.Weapon, '', true ) -- Sometimes, the weapon is a player unit!
|
||||||
Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon.getPlayerName and Event.Weapon:getPlayerName()
|
||||||
|
--Event.WeaponPlayerName = Event.WeaponUNIT and Event.Weapon:getPlayerName()
|
||||||
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
Event.WeaponCoalition = Event.WeaponUNIT and Event.Weapon:getCoalition()
|
||||||
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
Event.WeaponCategory = Event.WeaponUNIT and Event.Weapon:getDesc().category
|
||||||
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName()
|
||||||
|
|||||||
@ -368,7 +368,7 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
|||||||
if CoalitionSide then
|
if CoalitionSide then
|
||||||
if self.MessageDuration ~= 0 then
|
if self.MessageDuration ~= 0 then
|
||||||
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
self:T( self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ) .. " / " .. self.MessageDuration )
|
||||||
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub( "\n$", "" ):gsub( "\n$", "" ), self.MessageDuration, self.ClearScreen )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -8,22 +8,6 @@
|
|||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- # Demo Missions
|
|
||||||
--
|
|
||||||
-- ### [POINT_VEC Demo Missions source code]()
|
|
||||||
--
|
|
||||||
-- ### [POINT_VEC Demo Missions, only for beta testers]()
|
|
||||||
--
|
|
||||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- # YouTube Channel
|
|
||||||
--
|
|
||||||
-- ### [POINT_VEC YouTube Channel]()
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- ### Authors:
|
-- ### Authors:
|
||||||
--
|
--
|
||||||
-- * FlightControl (Design & Programming)
|
-- * FlightControl (Design & Programming)
|
||||||
@ -41,7 +25,8 @@
|
|||||||
|
|
||||||
do -- COORDINATE
|
do -- COORDINATE
|
||||||
|
|
||||||
--- @type COORDINATE
|
---
|
||||||
|
-- @type COORDINATE
|
||||||
-- @field #string ClassName Name of the class
|
-- @field #string ClassName Name of the class
|
||||||
-- @field #number x Component of the 3D vector.
|
-- @field #number x Component of the 3D vector.
|
||||||
-- @field #number y Component of the 3D vector.
|
-- @field #number y Component of the 3D vector.
|
||||||
@ -920,7 +905,7 @@ do -- COORDINATE
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Return an angle in radians from the COORDINATE using a direction vector in Vec3 format.
|
--- Return an angle in radians from the COORDINATE using a **direction vector in Vec3 format**.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param DCS#Vec3 DirectionVec3 The direction vector in Vec3 format.
|
-- @param DCS#Vec3 DirectionVec3 The direction vector in Vec3 format.
|
||||||
-- @return #number DirectionRadians The angle in radians.
|
-- @return #number DirectionRadians The angle in radians.
|
||||||
@ -933,10 +918,12 @@ do -- COORDINATE
|
|||||||
return DirectionRadians
|
return DirectionRadians
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Return an angle in degrees from the COORDINATE using a direction vector in Vec3 format.
|
--- Return an angle in degrees from the COORDINATE using a **direction vector in Vec3 format**.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param DCS#Vec3 DirectionVec3 The direction vector in Vec3 format.
|
-- @param DCS#Vec3 DirectionVec3 The direction vector in Vec3 format.
|
||||||
-- @return #number DirectionRadians The angle in degrees.
|
-- @return #number DirectionRadians The angle in degrees.
|
||||||
|
-- @usage
|
||||||
|
-- local directionAngle = currentCoordinate:GetAngleDegrees(currentCoordinate:GetDirectionVec3(sourceCoordinate:GetVec3()))
|
||||||
function COORDINATE:GetAngleDegrees( DirectionVec3 )
|
function COORDINATE:GetAngleDegrees( DirectionVec3 )
|
||||||
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
|
local AngleRadians = self:GetAngleRadians( DirectionVec3 )
|
||||||
local Angle = UTILS.ToDegree( AngleRadians )
|
local Angle = UTILS.ToDegree( AngleRadians )
|
||||||
@ -2565,7 +2552,7 @@ do -- COORDINATE
|
|||||||
|
|
||||||
Offset=Offset or 2
|
Offset=Offset or 2
|
||||||
|
|
||||||
-- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each Coordinate.
|
-- Measurement of visibility should not be from the ground, so Adding a hypothetical 2 meters to each Coordinate.
|
||||||
local FromVec3 = self:GetVec3()
|
local FromVec3 = self:GetVec3()
|
||||||
FromVec3.y = FromVec3.y + Offset
|
FromVec3.y = FromVec3.y + Offset
|
||||||
|
|
||||||
@ -2966,10 +2953,10 @@ do -- COORDINATE
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- corrected Track to be direction of travel of bogey (self in this case)
|
-- corrected Track to be direction of travel of bogey (self in this case)
|
||||||
local track = "Maneuver"
|
local track = "Maneuver"
|
||||||
|
|
||||||
if self.Heading then
|
if self.Heading then
|
||||||
track = UTILS.BearingToCardinal(self.Heading) or "North"
|
track = UTILS.BearingToCardinal(self.Heading) or "North"
|
||||||
end
|
end
|
||||||
|
|
||||||
if rangeNM > 3 then
|
if rangeNM > 3 then
|
||||||
@ -3021,6 +3008,16 @@ do -- COORDINATE
|
|||||||
return BRAANATO
|
return BRAANATO
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Return the BULLSEYE as COORDINATE Object
|
||||||
|
-- @param #number Coalition Coalition of the bulls eye to return, e.g. coalition.side.BLUE
|
||||||
|
-- @return #COORDINATE self
|
||||||
|
-- @usage
|
||||||
|
-- -- note the dot (.) here,not using the colon (:)
|
||||||
|
-- local redbulls = COORDINATE.GetBullseyeCoordinate(coalition.side.RED)
|
||||||
|
function COORDINATE.GetBullseyeCoordinate(Coalition)
|
||||||
|
return COORDINATE:NewFromVec3( coalition.getMainRefPoint( Coalition ) )
|
||||||
|
end
|
||||||
|
|
||||||
--- Return a BULLS string out of the BULLS of the coalition to the COORDINATE.
|
--- Return a BULLS string out of the BULLS of the coalition to the COORDINATE.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param DCS#coalition.side Coalition The coalition.
|
-- @param DCS#coalition.side Coalition The coalition.
|
||||||
@ -3105,6 +3102,49 @@ do -- COORDINATE
|
|||||||
return "MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy )
|
return "MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Provides a COORDINATE from an MGRS String
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #string MGRSString MGRS String, e.g. "MGRS 37T DK 12345 12345"
|
||||||
|
-- @return #COORDINATE self
|
||||||
|
function COORDINATE:NewFromMGRSString( MGRSString )
|
||||||
|
local myparts = UTILS.Split(MGRSString," ")
|
||||||
|
local northing = tostring(myparts[5]) or ""
|
||||||
|
local easting = tostring(myparts[4]) or ""
|
||||||
|
if string.len(easting) < 5 then easting = easting..string.rep("0",5-string.len(easting)) end
|
||||||
|
if string.len(northing) < 5 then northing = northing..string.rep("0",5-string.len(northing)) end
|
||||||
|
local MGRS = {
|
||||||
|
UTMZone = myparts[2],
|
||||||
|
MGRSDigraph = myparts[3],
|
||||||
|
Easting = easting,
|
||||||
|
Northing = northing,
|
||||||
|
}
|
||||||
|
local lat, lon = coord.MGRStoLL(MGRS)
|
||||||
|
local point = coord.LLtoLO(lat, lon, 0)
|
||||||
|
local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z})
|
||||||
|
return coord
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Provides a COORDINATE from an MGRS Coordinate
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #string UTMZone UTM Zone, e.g. "37T"
|
||||||
|
-- @param #string MGRSDigraph Digraph, e.g. "DK"
|
||||||
|
-- @param #string Easting Meters easting - string in order to allow for leading zeros, e.g. "01234". Should be 5 digits.
|
||||||
|
-- @param #string Northing Meters northing - string in order to allow for leading zeros, e.g. "12340". Should be 5 digits.
|
||||||
|
-- @return #COORDINATE self
|
||||||
|
function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing )
|
||||||
|
if string.len(Easting) < 5 then Easting = Easting..string.rep("0",5-string.len(Easting) )end
|
||||||
|
if string.len(Northing) < 5 then Northing = Northing..string.rep("0",5-string.len(Northing) )end
|
||||||
|
local MGRS = {
|
||||||
|
UTMZone = UTMZone,
|
||||||
|
MGRSDigraph = MGRSDigraph,
|
||||||
|
Easting = Easting,
|
||||||
|
Northing = Northing,
|
||||||
|
}
|
||||||
|
local lat, lon = coord.MGRStoLL(MGRS)
|
||||||
|
local point = coord.LLtoLO(lat, lon, 0)
|
||||||
|
local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z})
|
||||||
|
end
|
||||||
|
|
||||||
--- Provides a coordinate string of the point, based on a coordinate format system:
|
--- Provides a coordinate string of the point, based on a coordinate format system:
|
||||||
-- * Uses default settings in COORDINATE.
|
-- * Uses default settings in COORDINATE.
|
||||||
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
||||||
@ -3617,7 +3657,7 @@ end
|
|||||||
|
|
||||||
do -- POINT_VEC2
|
do -- POINT_VEC2
|
||||||
|
|
||||||
--- @type POINT_VEC2
|
-- @type POINT_VEC2
|
||||||
-- @field DCS#Distance x The x coordinate in meters.
|
-- @field DCS#Distance x The x coordinate in meters.
|
||||||
-- @field DCS#Distance y the y coordinate in meters.
|
-- @field DCS#Distance y the y coordinate in meters.
|
||||||
-- @extends Core.Point#COORDINATE
|
-- @extends Core.Point#COORDINATE
|
||||||
|
|||||||
@ -419,7 +419,11 @@ do -- SET_BASE
|
|||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @return Core.Base#BASE
|
-- @return Core.Base#BASE
|
||||||
function SET_BASE:GetRandom()
|
function SET_BASE:GetRandom()
|
||||||
local tablemax = table.maxn(self.Index)
|
local tablemax = 0
|
||||||
|
for _,_ind in pairs(self.Index) do
|
||||||
|
tablemax = tablemax + 1
|
||||||
|
end
|
||||||
|
--local tablemax = table.maxn(self.Index)
|
||||||
local RandomItem = self.Set[self.Index[math.random(1,tablemax)]]
|
local RandomItem = self.Set[self.Index[math.random(1,tablemax)]]
|
||||||
self:T3( { RandomItem } )
|
self:T3( { RandomItem } )
|
||||||
return RandomItem
|
return RandomItem
|
||||||
@ -561,10 +565,12 @@ do -- SET_BASE
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Iterate the SET_BASE while identifying the nearest object from a @{Core.Point#POINT_VEC2}.
|
--- Iterate the SET_BASE while identifying the nearest object in the set from a @{Core.Point#POINT_VEC2}.
|
||||||
-- @param #SET_BASE self
|
-- @param #SET_BASE self
|
||||||
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set.
|
-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#COORDINATE} or @{Core.Point#POINT_VEC2} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set.
|
||||||
-- @return Core.Base#BASE The closest object.
|
-- @return Core.Base#BASE The closest object.
|
||||||
|
-- @usage
|
||||||
|
-- myset:FindNearestObjectFromPointVec2( ZONE:New("Test Zone"):GetCoordinate() )
|
||||||
function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 )
|
function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 )
|
||||||
self:F2( PointVec2 )
|
self:F2( PointVec2 )
|
||||||
|
|
||||||
@ -1065,6 +1071,13 @@ do
|
|||||||
self:FilterActive( false )
|
self:FilterActive( false )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
--- Filter the set once
|
||||||
|
-- @function [parent=#SET_GROUP] FilterOnce
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @return #SET_GROUP self
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a *new* set that only contains alive groups.
|
--- Get a *new* set that only contains alive groups.
|
||||||
@ -1976,6 +1989,7 @@ do
|
|||||||
--- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search.
|
--- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search.
|
||||||
-- @param #SET_GROUP self
|
-- @param #SET_GROUP self
|
||||||
-- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined.
|
-- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined.
|
||||||
|
-- @param #table Coalitions (Optional) Table of coalition #number entries to filter for.
|
||||||
-- @return Wrapper.Group#GROUP The closest group (if any).
|
-- @return Wrapper.Group#GROUP The closest group (if any).
|
||||||
-- @return #number Distance in meters to the closest group.
|
-- @return #number Distance in meters to the closest group.
|
||||||
function SET_GROUP:GetClosestGroup(Coordinate, Coalitions)
|
function SET_GROUP:GetClosestGroup(Coordinate, Coalitions)
|
||||||
@ -2840,59 +2854,50 @@ do -- SET_UNIT
|
|||||||
-- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units.
|
-- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units.
|
||||||
function SET_UNIT:GetCoordinate()
|
function SET_UNIT:GetCoordinate()
|
||||||
|
|
||||||
local Coordinate = nil
|
local function GetSetVec3(units)
|
||||||
local unit = self:GetRandom()
|
-- Init.
|
||||||
if self:Count() == 1 and unit then
|
local x=0
|
||||||
return unit:GetCoordinate()
|
local y=0
|
||||||
end
|
local z=0
|
||||||
if unit then
|
local n=0
|
||||||
local Coordinate = unit:GetCoordinate()
|
-- Loop over all units.
|
||||||
--self:F({Coordinate:GetVec3()})
|
for _,unit in pairs(units) do
|
||||||
|
local vec3=nil --DCS#Vec3
|
||||||
|
if unit and unit:IsAlive() then
|
||||||
local x1 = Coordinate.x
|
vec3 = unit:GetVec3()
|
||||||
local x2 = Coordinate.x
|
end
|
||||||
local y1 = Coordinate.y
|
if vec3 then
|
||||||
local y2 = Coordinate.y
|
-- Sum up posits.
|
||||||
local z1 = Coordinate.z
|
x=x+vec3.x
|
||||||
local z2 = Coordinate.z
|
y=y+vec3.y
|
||||||
local MaxVelocity = 0
|
z=z+vec3.z
|
||||||
local AvgHeading = nil
|
-- Increase counter.
|
||||||
local MovingCount = 0
|
n=n+1
|
||||||
|
|
||||||
for UnitName, UnitData in pairs( self:GetAliveSet() ) do
|
|
||||||
|
|
||||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
|
||||||
local Coordinate = Unit:GetCoordinate()
|
|
||||||
|
|
||||||
x1 = (Coordinate.x < x1) and Coordinate.x or x1
|
|
||||||
x2 = (Coordinate.x > x2) and Coordinate.x or x2
|
|
||||||
y1 = (Coordinate.y < y1) and Coordinate.y or y1
|
|
||||||
y2 = (Coordinate.y > y2) and Coordinate.y or y2
|
|
||||||
z1 = (Coordinate.y < z1) and Coordinate.z or z1
|
|
||||||
z2 = (Coordinate.y > z2) and Coordinate.z or z2
|
|
||||||
|
|
||||||
local Velocity = Coordinate:GetVelocity()
|
|
||||||
if Velocity ~= 0 then
|
|
||||||
MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity
|
|
||||||
local Heading = Coordinate:GetHeading()
|
|
||||||
AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading
|
|
||||||
MovingCount = MovingCount + 1
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if n>0 then
|
||||||
AvgHeading = AvgHeading and (AvgHeading / MovingCount)
|
-- Average.
|
||||||
|
local Vec3={x=x/n, y=y/n, z=z/n} --DCS#Vec3
|
||||||
Coordinate.x = (x2 - x1) / 2 + x1
|
return Vec3
|
||||||
Coordinate.y = (y2 - y1) / 2 + y1
|
end
|
||||||
Coordinate.z = (z2 - z1) / 2 + z1
|
return nil
|
||||||
Coordinate:SetHeading( AvgHeading )
|
|
||||||
Coordinate:SetVelocity( MaxVelocity )
|
|
||||||
|
|
||||||
self:F( { Coordinate = Coordinate } )
|
|
||||||
end
|
end
|
||||||
return Coordinate
|
|
||||||
|
|
||||||
|
local Coordinate = nil
|
||||||
|
local Vec3 = GetSetVec3(self.Set)
|
||||||
|
if Vec3 then
|
||||||
|
Coordinate = COORDINATE:NewFromVec3(Vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
if Coordinate then
|
||||||
|
local heading = self:GetHeading() or 0
|
||||||
|
local velocity = self:GetVelocity() or 0
|
||||||
|
Coordinate:SetHeading( heading )
|
||||||
|
Coordinate:SetVelocity( velocity )
|
||||||
|
self:I(UTILS.PrintTableToLog(Coordinate))
|
||||||
|
end
|
||||||
|
|
||||||
|
return Coordinate
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the maximum velocity of the SET_UNIT.
|
--- Get the maximum velocity of the SET_UNIT.
|
||||||
|
|||||||
@ -320,7 +320,7 @@ function SPAWN:New( SpawnTemplatePrefix )
|
|||||||
self.AIOnOff = true -- The AI is on by default when spawning a group.
|
self.AIOnOff = true -- The AI is on by default when spawning a group.
|
||||||
self.SpawnUnControlled = false
|
self.SpawnUnControlled = false
|
||||||
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name.
|
||||||
self.DelayOnOff = false -- No intial delay when spawning the first group.
|
self.DelayOnOff = false -- No initial delay when spawning the first group.
|
||||||
self.SpawnGrouping = nil -- No grouping.
|
self.SpawnGrouping = nil -- No grouping.
|
||||||
self.SpawnInitLivery = nil -- No special livery.
|
self.SpawnInitLivery = nil -- No special livery.
|
||||||
self.SpawnInitSkill = nil -- No special skill.
|
self.SpawnInitSkill = nil -- No special skill.
|
||||||
@ -332,6 +332,7 @@ function SPAWN:New( SpawnTemplatePrefix )
|
|||||||
self.SpawnInitModexPostfix = nil
|
self.SpawnInitModexPostfix = nil
|
||||||
self.SpawnInitAirbase = nil
|
self.SpawnInitAirbase = nil
|
||||||
self.TweakedTemplate = false -- Check if the user is using self made template.
|
self.TweakedTemplate = false -- Check if the user is using self made template.
|
||||||
|
self.SpawnRandomCallsign = false
|
||||||
|
|
||||||
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned.
|
||||||
else
|
else
|
||||||
@ -1099,6 +1100,30 @@ function SPAWN:InitRandomizeZones( SpawnZoneTable )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [AIR/Fighter only!] This method randomizes the callsign for a new group.
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @return #SPAWN self
|
||||||
|
function SPAWN:InitRandomizeCallsign()
|
||||||
|
self.SpawnRandomCallsign = true
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [BLUE AIR only!] This method sets a specific callsign for a spawned group. Use for a group with one unit only!
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @param #number ID ID of the callsign enumerator, e.g. CALLSIGN.Tanker.Texaco - - resulting in e.g. Texaco-2-1
|
||||||
|
-- @param #string Name Name of this callsign as it cannot be determined from the ID because of the dependency on the task type of the plane, and the plane type. E.g. "Texaco"
|
||||||
|
-- @param #number Minor Minor number, i.e. the unit number within the group, e.g 2 - resulting in e.g. Texaco-2-1
|
||||||
|
-- @param #number Major Major number, i.e. the group number of this name, e.g. 1 - resulting in e.g. Texaco-2-1
|
||||||
|
-- @return #SPAWN self
|
||||||
|
function SPAWN:InitCallSign(ID,Name,Minor,Major)
|
||||||
|
self.SpawnInitCallSign = true
|
||||||
|
self.SpawnInitCallSignID = ID or 1
|
||||||
|
self.SpawnInitCallSignMinor = Minor or 1
|
||||||
|
self.SpawnInitCallSignMajor = Major or 1
|
||||||
|
self.SpawnInitCallSignName = string.lower(Name) or "enfield"
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- This method sets a spawn position for the group that is different from the location of the template.
|
--- This method sets a spawn position for the group that is different from the location of the template.
|
||||||
-- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @param Core.Point#COORDINATE Coordinate The position to spawn from
|
-- @param Core.Point#COORDINATE Coordinate The position to spawn from
|
||||||
@ -3275,17 +3300,78 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Callsign
|
-- Callsign
|
||||||
|
|
||||||
|
if self.SpawnRandomCallsign and SpawnTemplate.units[1].callsign then
|
||||||
|
if type( SpawnTemplate.units[1].callsign ) ~= "number" then
|
||||||
|
-- change callsign
|
||||||
|
local min = 1
|
||||||
|
local max = 8
|
||||||
|
local ctable = CALLSIGN.Aircraft
|
||||||
|
if string.find(SpawnTemplate.units[1].type, "A-10",1,true) then
|
||||||
|
max = 12
|
||||||
|
end
|
||||||
|
if string.find(SpawnTemplate.units[1].type, "18",1,true) then
|
||||||
|
min = 9
|
||||||
|
max = 20
|
||||||
|
ctable = CALLSIGN.F18
|
||||||
|
end
|
||||||
|
if string.find(SpawnTemplate.units[1].type, "16",1,true) then
|
||||||
|
min = 9
|
||||||
|
max = 20
|
||||||
|
ctable = CALLSIGN.F16
|
||||||
|
end
|
||||||
|
if SpawnTemplate.units[1].type == "F-15E" then
|
||||||
|
min = 9
|
||||||
|
max = 18
|
||||||
|
ctable = CALLSIGN.F15E
|
||||||
|
end
|
||||||
|
local callsignnr = math.random(min,max)
|
||||||
|
local callsignname = "Enfield"
|
||||||
|
for name, value in pairs(ctable) do
|
||||||
|
if value==callsignnr then
|
||||||
|
callsignname = name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
|
SpawnTemplate.units[UnitID].callsign[1] = callsignnr
|
||||||
|
SpawnTemplate.units[UnitID].callsign[2] = UnitID
|
||||||
|
SpawnTemplate.units[UnitID].callsign[3] = "1"
|
||||||
|
SpawnTemplate.units[UnitID].callsign["name"] = tostring(callsignname)..tostring(UnitID).."1"
|
||||||
|
-- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Russkis
|
||||||
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
|
SpawnTemplate.units[UnitID].callsign = math.random(1,999)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.SpawnInitCallSign then
|
||||||
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
|
local Callsign = SpawnTemplate.units[UnitID].callsign
|
||||||
|
if Callsign and type( Callsign ) ~= "number" then
|
||||||
|
SpawnTemplate.units[UnitID].callsign[1] = self.SpawnInitCallSignID
|
||||||
|
SpawnTemplate.units[UnitID].callsign[2] = self.SpawnInitCallSignMinor
|
||||||
|
SpawnTemplate.units[UnitID].callsign[3] = self.SpawnInitCallSignMajor
|
||||||
|
SpawnTemplate.units[UnitID].callsign["name"] = string.format("%s%d%d",self.SpawnInitCallSignName,self.SpawnInitCallSignMinor,self.SpawnInitCallSignMajor)
|
||||||
|
--UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
for UnitID = 1, #SpawnTemplate.units do
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
local Callsign = SpawnTemplate.units[UnitID].callsign
|
local Callsign = SpawnTemplate.units[UnitID].callsign
|
||||||
if Callsign then
|
if Callsign then
|
||||||
if type( Callsign ) ~= "number" then -- blue callsign
|
if type( Callsign ) ~= "number" and not self.SpawnInitCallSign then -- blue callsign
|
||||||
|
-- UTILS.PrintTableToLog(Callsign,1)
|
||||||
Callsign[2] = ((SpawnIndex - 1) % 10) + 1
|
Callsign[2] = ((SpawnIndex - 1) % 10) + 1
|
||||||
local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string
|
local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string
|
||||||
CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers
|
CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers
|
||||||
local CallsignLen = CallsignName:len()
|
local CallsignLen = CallsignName:len()
|
||||||
SpawnTemplate.units[UnitID].callsign[2] = UnitID
|
SpawnTemplate.units[UnitID].callsign[2] = UnitID
|
||||||
SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3]
|
SpawnTemplate.units[UnitID].callsign["name"] = CallsignName:sub( 1, CallsignLen ) .. SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3]
|
||||||
else
|
elseif type( Callsign ) == "number" then
|
||||||
SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex
|
SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -3293,25 +3379,77 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
|||||||
local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft
|
local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft
|
||||||
if AddProps then
|
if AddProps then
|
||||||
if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then
|
if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then
|
||||||
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16+UnitID-1
|
-- 4 digit octal with leading 0
|
||||||
if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 < 10000 then
|
if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) ~= nil then
|
||||||
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("0%d",SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16)
|
local octal = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16
|
||||||
|
local decimal = UTILS.OctalToDecimal(octal)+UnitID-1
|
||||||
|
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",UTILS.DecimalToOctal(decimal))
|
||||||
|
else -- ED bug - chars in here
|
||||||
|
local STN = math.floor(UTILS.RandomGaussian(4088/2,nil,1000,4088))
|
||||||
|
STN = STN+UnitID-1
|
||||||
|
local OSTN = UTILS.DecimalToOctal(STN)
|
||||||
|
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",OSTN)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- A10CII
|
||||||
|
if SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN then
|
||||||
|
-- 3 digit octal with leading 0
|
||||||
|
if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then
|
||||||
|
local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN
|
||||||
|
local decimal = UTILS.OctalToDecimal(octal)+UnitID-1
|
||||||
|
SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",UTILS.DecimalToOctal(decimal))
|
||||||
|
else -- ED bug - chars in here
|
||||||
|
local STN = math.floor(UTILS.RandomGaussian(504/2,nil,100,504))
|
||||||
|
STN = STN+UnitID-1
|
||||||
|
local OSTN = UTILS.DecimalToOctal(STN)
|
||||||
|
SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",OSTN)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- VoiceCallsignNumber
|
-- VoiceCallsignNumber
|
||||||
if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber then
|
if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber and type( Callsign ) ~= "number" then
|
||||||
SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber+UnitID-1
|
SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignNumber = SpawnTemplate.units[UnitID].callsign[2] .. SpawnTemplate.units[UnitID].callsign[3]
|
||||||
end
|
end
|
||||||
--UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1)
|
-- VoiceCallsignLabel
|
||||||
|
if SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel and type( Callsign ) ~= "number" then
|
||||||
|
local CallsignName = SpawnTemplate.units[UnitID].callsign["name"] -- #string
|
||||||
|
CallsignName = string.match(CallsignName,"^(%a+)") -- 2.8 - only the part w/o numbers
|
||||||
|
local label = "NY" -- Navy One exception
|
||||||
|
if not string.find(CallsignName," ") then
|
||||||
|
label = string.upper(string.match(CallsignName,"^%a")..string.match(CallsignName,"%a$"))
|
||||||
|
end
|
||||||
|
SpawnTemplate.units[UnitID].AddPropAircraft.VoiceCallsignLabel = label
|
||||||
|
end
|
||||||
|
-- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].AddPropAircraft,1)
|
||||||
-- FlightLead
|
-- FlightLead
|
||||||
if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then
|
if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.settings then
|
||||||
SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead = UnitID == 1 and true or false
|
SpawnTemplate.units[UnitID].datalinks.Link16.settings.flightLead = UnitID == 1 and true or false
|
||||||
end
|
end
|
||||||
--UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1)
|
-- A10CII
|
||||||
|
if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.SADL and SpawnTemplate.units[UnitID].datalinks.SADL.settings then
|
||||||
|
SpawnTemplate.units[UnitID].datalinks.SADL.settings.flightLead = UnitID == 1 and true or false
|
||||||
|
end
|
||||||
|
-- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Link16 team members
|
||||||
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
|
if SpawnTemplate.units[UnitID].datalinks and SpawnTemplate.units[UnitID].datalinks.Link16 and SpawnTemplate.units[UnitID].datalinks.Link16.network then
|
||||||
|
local team = {}
|
||||||
|
local isF16 = string.find(SpawnTemplate.units[UnitID].type,"F-16",1,true) and true or false
|
||||||
|
for ID = 1, #SpawnTemplate.units do
|
||||||
|
local member = {}
|
||||||
|
member.missionUnitId = ID
|
||||||
|
if isF16 then
|
||||||
|
member.TDOA = true
|
||||||
|
end
|
||||||
|
table.insert(team,member)
|
||||||
|
end
|
||||||
|
SpawnTemplate.units[UnitID].datalinks.Link16.network.teamMembers = team
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:T3( { "Template:", SpawnTemplate } )
|
self:T3( { "Template:", SpawnTemplate } )
|
||||||
|
--UTILS.PrintTableToLog(SpawnTemplate,1)
|
||||||
return SpawnTemplate
|
return SpawnTemplate
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -10,7 +10,7 @@
|
|||||||
-- * Create moving zones around a unit.
|
-- * Create moving zones around a unit.
|
||||||
-- * Create moving zones around a group.
|
-- * Create moving zones around a group.
|
||||||
-- * Provide the zone behavior. Some zones are static, while others are moveable.
|
-- * Provide the zone behavior. Some zones are static, while others are moveable.
|
||||||
-- * Enquiry if a coordinate is within a zone.
|
-- * Enquire if a coordinate is within a zone.
|
||||||
-- * Smoke zones.
|
-- * Smoke zones.
|
||||||
-- * Set a zone probability to control zone selection.
|
-- * Set a zone probability to control zone selection.
|
||||||
-- * Get zone coordinates.
|
-- * Get zone coordinates.
|
||||||
@ -42,11 +42,12 @@
|
|||||||
-- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Wrapper.Unit#UNIT} with a radius.
|
-- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Wrapper.Unit#UNIT} with a radius.
|
||||||
-- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius.
|
-- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius.
|
||||||
-- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon.
|
-- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon.
|
||||||
|
-- * @{#ZONE_OVAL}: The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle.
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
-- ### Contributions: **Applevangelist**, **FunkyFranky**
|
-- ### Contributions: **Applevangelist**, **FunkyFranky**, **coconutcockpit**
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@ -480,7 +481,13 @@ function ZONE_BASE:UndrawZone(Delay)
|
|||||||
self:ScheduleOnce(Delay, ZONE_BASE.UndrawZone, self)
|
self:ScheduleOnce(Delay, ZONE_BASE.UndrawZone, self)
|
||||||
else
|
else
|
||||||
if self.DrawID then
|
if self.DrawID then
|
||||||
UTILS.RemoveMark(self.DrawID)
|
if type(self.DrawID) ~= "table" then
|
||||||
|
UTILS.RemoveMark(self.DrawID)
|
||||||
|
else -- DrawID is a table with a collections of mark ids, as used in ZONE_POLYGON
|
||||||
|
for _, mark_id in pairs(self.DrawID) do
|
||||||
|
UTILS.RemoveMark(mark_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
@ -1994,6 +2001,111 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer )
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Triangle.lua
|
||||||
|
--- This triangle "zone" is not really to be used on its own, it only serves as building blocks for
|
||||||
|
--- ZONE_POLYGON to accurately find a point inside a polygon; as well as getting the correct surface area of
|
||||||
|
--- a polygon.
|
||||||
|
-- @type _ZONE_TRIANGLE
|
||||||
|
-- @extends Core.Zone#ZONE_BASE
|
||||||
|
|
||||||
|
--- ## _ZONE_TRIANGLE class, extends @{#ZONE_BASE}
|
||||||
|
--
|
||||||
|
-- _ZONE_TRIANGLE class is a helper class for ZONE_POLYGON
|
||||||
|
-- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties.
|
||||||
|
--
|
||||||
|
-- @field #_ZONE_TRIANGLE
|
||||||
|
_ZONE_TRIANGLE = {
|
||||||
|
ClassName="ZONE_TRIANGLE",
|
||||||
|
Points={},
|
||||||
|
Coords={},
|
||||||
|
CenterVec2={x=0, y=0},
|
||||||
|
SurfaceArea=0,
|
||||||
|
DrawIDs={}
|
||||||
|
}
|
||||||
|
---
|
||||||
|
-- @param #_ZONE_TRIANGLE self
|
||||||
|
-- @param DCS#Vec p1
|
||||||
|
-- @param DCS#Vec p2
|
||||||
|
-- @param DCS#Vec p3
|
||||||
|
-- @return #_ZONE_TRIANGLE self
|
||||||
|
function _ZONE_TRIANGLE:New(p1, p2, p3)
|
||||||
|
local self = BASE:Inherit(self, ZONE_BASE:New())
|
||||||
|
self.Points = {p1, p2, p3}
|
||||||
|
|
||||||
|
local center_x = (p1.x + p2.x + p3.x) / 3
|
||||||
|
local center_y = (p1.y + p2.y + p3.y) / 3
|
||||||
|
self.CenterVec2 = {x=center_x, y=center_y}
|
||||||
|
|
||||||
|
for _, pt in pairs({p1, p2, p3}) do
|
||||||
|
table.add(self.Coords, COORDINATE:NewFromVec2(pt))
|
||||||
|
end
|
||||||
|
|
||||||
|
self.SurfaceArea = math.abs((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)) * 0.5
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Checks if a point is contained within the triangle.
|
||||||
|
-- @param #_ZONE_TRIANGLE self
|
||||||
|
-- @param #table pt The point to check
|
||||||
|
-- @param #table points (optional) The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it
|
||||||
|
-- @return #bool True if the point is contained, false otherwise
|
||||||
|
function _ZONE_TRIANGLE:ContainsPoint(pt, points)
|
||||||
|
points = points or self.Points
|
||||||
|
|
||||||
|
local function sign(p1, p2, p3)
|
||||||
|
return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
local d1 = sign(pt, self.Points[1], self.Points[2])
|
||||||
|
local d2 = sign(pt, self.Points[2], self.Points[3])
|
||||||
|
local d3 = sign(pt, self.Points[3], self.Points[1])
|
||||||
|
|
||||||
|
local has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0)
|
||||||
|
local has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0)
|
||||||
|
|
||||||
|
return not (has_neg and has_pos)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns a random Vec2 within the triangle.
|
||||||
|
-- @param #_ZONE_TRIANGLE self
|
||||||
|
-- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it
|
||||||
|
-- @return #table The random Vec2
|
||||||
|
function _ZONE_TRIANGLE:GetRandomVec2(points)
|
||||||
|
points = points or self.Points
|
||||||
|
local pt = {math.random(), math.random()}
|
||||||
|
table.sort(pt)
|
||||||
|
local s = pt[1]
|
||||||
|
local t = pt[2] - pt[1]
|
||||||
|
local u = 1 - pt[2]
|
||||||
|
|
||||||
|
return {x = s * points[1].x + t * points[2].x + u * points[3].x,
|
||||||
|
y = s * points[1].y + t * points[2].y + u * points[3].y}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draw the triangle
|
||||||
|
-- @param #_ZONE_TRIANGLE self
|
||||||
|
-- @return #table of draw IDs
|
||||||
|
function _ZONE_TRIANGLE:Draw(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
||||||
|
Coalition=Coalition or -1
|
||||||
|
|
||||||
|
Color=Color or {1, 0, 0 }
|
||||||
|
Alpha=Alpha or 1
|
||||||
|
|
||||||
|
FillColor=FillColor or Color
|
||||||
|
if not FillColor then UTILS.DeepCopy(Color) end
|
||||||
|
FillAlpha=FillAlpha or Alpha
|
||||||
|
if not FillAlpha then FillAlpha=1 end
|
||||||
|
|
||||||
|
for i=1, #self.Coords do
|
||||||
|
local c1 = self.Coords[i]
|
||||||
|
local c2 = self.Coords[i % #self.Coords + 1]
|
||||||
|
table.add(self.DrawIDs, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly))
|
||||||
|
end
|
||||||
|
return self.DrawIDs
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @type ZONE_POLYGON_BASE
|
-- @type ZONE_POLYGON_BASE
|
||||||
-- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}.
|
-- @field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}.
|
||||||
@ -2021,7 +2133,10 @@ end
|
|||||||
-- @field #ZONE_POLYGON_BASE
|
-- @field #ZONE_POLYGON_BASE
|
||||||
ZONE_POLYGON_BASE = {
|
ZONE_POLYGON_BASE = {
|
||||||
ClassName="ZONE_POLYGON_BASE",
|
ClassName="ZONE_POLYGON_BASE",
|
||||||
}
|
_Triangles={}, -- _ZONE_TRIANGLES
|
||||||
|
SurfaceArea=0,
|
||||||
|
DrawID={} -- making a table out of the MarkID so its easier to draw an n-sided polygon, see ZONE_POLYGON_BASE:Draw()
|
||||||
|
}
|
||||||
|
|
||||||
--- A 2D points array.
|
--- A 2D points array.
|
||||||
-- @type ZONE_POLYGON_BASE.ListVec2
|
-- @type ZONE_POLYGON_BASE.ListVec2
|
||||||
@ -2053,11 +2168,104 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray )
|
|||||||
self._.Polygon[i].y = PointsArray[i].y
|
self._.Polygon[i].y = PointsArray[i].y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- triangulate the polygon so we can work with it
|
||||||
|
self._Triangles = self:_Triangulate()
|
||||||
|
-- set the polygon's surface area
|
||||||
|
self.SurfaceArea = self:_CalculateSurfaceArea()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Triangulates the polygon.
|
||||||
|
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @return #table The #_ZONE_TRIANGLE list that makes up the polygon
|
||||||
|
function ZONE_POLYGON_BASE:_Triangulate()
|
||||||
|
local points = self._.Polygon
|
||||||
|
local triangles = {}
|
||||||
|
|
||||||
|
local function get_orientation(shape_points)
|
||||||
|
local sum = 0
|
||||||
|
for i = 1, #shape_points do
|
||||||
|
local j = i % #shape_points + 1
|
||||||
|
sum = sum + (shape_points[j].x - shape_points[i].x) * (shape_points[j].y + shape_points[i].y)
|
||||||
|
end
|
||||||
|
return sum >= 0 and "clockwise" or "counter-clockwise" -- sum >= 0, return "clockwise", else return "counter-clockwise"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ensure_clockwise(shape_points)
|
||||||
|
local orientation = get_orientation(shape_points)
|
||||||
|
if orientation == "counter-clockwise" then
|
||||||
|
-- Reverse the order of shape_points so they're clockwise
|
||||||
|
local reversed = {}
|
||||||
|
for i = #shape_points, 1, -1 do
|
||||||
|
table.insert(reversed, shape_points[i])
|
||||||
|
end
|
||||||
|
return reversed
|
||||||
|
end
|
||||||
|
return shape_points
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_clockwise(p1, p2, p3)
|
||||||
|
local cross_product = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x)
|
||||||
|
return cross_product < 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function divide_recursively(shape_points)
|
||||||
|
if #shape_points == 3 then
|
||||||
|
table.insert(triangles, _ZONE_TRIANGLE:New(shape_points[1], shape_points[2], shape_points[3]))
|
||||||
|
elseif #shape_points > 3 then -- find an ear -> a triangle with no other points inside it
|
||||||
|
for i, p1 in ipairs(shape_points) do
|
||||||
|
local p2 = shape_points[(i % #shape_points) + 1]
|
||||||
|
local p3 = shape_points[(i + 1) % #shape_points + 1]
|
||||||
|
local triangle = _ZONE_TRIANGLE:New(p1, p2, p3)
|
||||||
|
local is_ear = true
|
||||||
|
|
||||||
|
if not is_clockwise(p1, p2, p3) then
|
||||||
|
is_ear = false
|
||||||
|
else
|
||||||
|
for _, point in ipairs(shape_points) do
|
||||||
|
if point ~= p1 and point ~= p2 and point ~= p3 and triangle:ContainsPoint(point) then
|
||||||
|
is_ear = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_ear then
|
||||||
|
-- Check if any point in the original polygon is inside the ear triangle
|
||||||
|
local is_valid_triangle = true
|
||||||
|
for _, point in ipairs(points) do
|
||||||
|
if point ~= p1 and point ~= p2 and point ~= p3 and triangle:ContainsPoint(point) then
|
||||||
|
is_valid_triangle = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if is_valid_triangle then
|
||||||
|
table.insert(triangles, triangle)
|
||||||
|
local remaining_points = {}
|
||||||
|
for j, point in ipairs(shape_points) do
|
||||||
|
if point ~= p2 then
|
||||||
|
table.insert(remaining_points, point)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
divide_recursively(remaining_points)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
else
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
points = ensure_clockwise(points)
|
||||||
|
divide_recursively(points)
|
||||||
|
return triangles
|
||||||
|
end
|
||||||
|
|
||||||
--- Update polygon points with an array of @{DCS#Vec2}.
|
--- Update polygon points with an array of @{DCS#Vec2}.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @param #ZONE_POLYGON_BASE.ListVec2 Vec2Array An array of @{DCS#Vec2}, forming a polygon.
|
-- @param #ZONE_POLYGON_BASE.ListVec2 Vec2Array An array of @{DCS#Vec2}, forming a polygon.
|
||||||
@ -2072,6 +2280,10 @@ function ZONE_POLYGON_BASE:UpdateFromVec2(Vec2Array)
|
|||||||
self._.Polygon[i].y=Vec2Array[i].y
|
self._.Polygon[i].y=Vec2Array[i].y
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- triangulate the polygon so we can work with it
|
||||||
|
self._Triangles = self:_Triangulate()
|
||||||
|
-- set the polygon's surface area
|
||||||
|
self.SurfaceArea = self:_CalculateSurfaceArea()
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2089,9 +2301,25 @@ function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array)
|
|||||||
self._.Polygon[i].y=Vec3Array[i].z
|
self._.Polygon[i].y=Vec3Array[i].z
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- triangulate the polygon so we can work with it
|
||||||
|
self._Triangles = self:_Triangulate()
|
||||||
|
-- set the polygon's surface area
|
||||||
|
self.SurfaceArea = self:_CalculateSurfaceArea()
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Calculates the surface area of the polygon. The surface area is the sum of the areas of the triangles that make up the polygon.
|
||||||
|
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @return #number The surface area of the polygon
|
||||||
|
function ZONE_POLYGON_BASE:_CalculateSurfaceArea()
|
||||||
|
local area = 0
|
||||||
|
for _, triangle in pairs(self._Triangles) do
|
||||||
|
area = area + triangle.SurfaceArea
|
||||||
|
end
|
||||||
|
return area
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the center location of the polygon.
|
--- Returns the center location of the polygon.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location.
|
-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location.
|
||||||
@ -2233,63 +2461,77 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Draw the zone on the F10 map. **NOTE** Currently, only polygons **up to ten points** are supported!
|
--- Draw the zone on the F10 map. Infinite number of points supported
|
||||||
|
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||||
-- @param #number Alpha Transparency [0,1]. Default 1.
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value.
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work
|
||||||
-- @param #number FillAlpha Transparency [0,1]. Default 0.15.
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work
|
||||||
-- @param #number LineType 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: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
-- @return #ZONE_POLYGON_BASE self
|
-- @return #ZONE_POLYGON_BASE self
|
||||||
function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, IncludeTriangles)
|
||||||
|
if self._.Polygon and #self._.Polygon >= 3 then
|
||||||
|
Coalition = Coalition or self:GetDrawCoalition()
|
||||||
|
|
||||||
if self._.Polygon and #self._.Polygon>=3 then
|
-- Set draw coalition.
|
||||||
|
self:SetDrawCoalition(Coalition)
|
||||||
|
|
||||||
local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1])
|
Color = Color or self:GetColorRGB()
|
||||||
|
Alpha = Alpha or 1
|
||||||
|
|
||||||
Coalition=Coalition or self:GetDrawCoalition()
|
-- Set color.
|
||||||
|
self:SetColor(Color, Alpha)
|
||||||
|
|
||||||
-- Set draw coalition.
|
FillColor = FillColor or self:GetFillColorRGB()
|
||||||
self:SetDrawCoalition(Coalition)
|
if not FillColor then
|
||||||
|
UTILS.DeepCopy(Color)
|
||||||
|
end
|
||||||
|
FillAlpha = FillAlpha or self:GetFillColorAlpha()
|
||||||
|
if not FillAlpha then
|
||||||
|
FillAlpha = 0.15
|
||||||
|
end
|
||||||
|
|
||||||
Color=Color or self:GetColorRGB()
|
-- Set fill color -----------> has fill color worked in recent versions of DCS?
|
||||||
Alpha=Alpha or 1
|
-- doing something like
|
||||||
|
--
|
||||||
|
-- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "")
|
||||||
|
--
|
||||||
|
-- doesn't seem to fill in the shape for an n-sided polygon
|
||||||
|
self:SetFillColor(FillColor, FillAlpha)
|
||||||
|
|
||||||
-- Set color.
|
IncludeTriangles = IncludeTriangles or false
|
||||||
self:SetColor(Color, Alpha)
|
|
||||||
|
|
||||||
FillColor=FillColor or self:GetFillColorRGB()
|
|
||||||
if not FillColor then UTILS.DeepCopy(Color) end
|
|
||||||
FillAlpha=FillAlpha or self:GetFillColorAlpha()
|
|
||||||
if not FillAlpha then FillAlpha=0.15 end
|
|
||||||
|
|
||||||
-- Set fill color.
|
|
||||||
self:SetFillColor(FillColor, FillAlpha)
|
|
||||||
|
|
||||||
if #self._.Polygon==4 then
|
|
||||||
|
|
||||||
local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2])
|
|
||||||
local Coord3=COORDINATE:NewFromVec2(self._.Polygon[3])
|
|
||||||
local Coord4=COORDINATE:NewFromVec2(self._.Polygon[4])
|
|
||||||
|
|
||||||
self.DrawID=coordinate:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
local Coordinates=self:GetVerticiesCoordinates()
|
|
||||||
table.remove(Coordinates, 1)
|
|
||||||
|
|
||||||
self.DrawID=coordinate:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
|
||||||
|
|
||||||
|
-- just draw the triangles, we get the outline for free
|
||||||
|
if IncludeTriangles then
|
||||||
|
for _, triangle in pairs(self._Triangles) do
|
||||||
|
local draw_ids = triangle:Draw()
|
||||||
|
table.combine(self.DrawID, draw_ids)
|
||||||
|
end
|
||||||
|
-- draw outline only
|
||||||
|
else
|
||||||
|
local coords = self:GetVerticiesCoordinates()
|
||||||
|
for i = 1, #coords do
|
||||||
|
local c1 = coords[i]
|
||||||
|
local c2 = coords[i % #coords + 1]
|
||||||
|
table.add(self.DrawID, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the surface area of this polygon
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @return #number Surface area
|
||||||
|
function ZONE_POLYGON_BASE:GetSurfaceArea()
|
||||||
|
return self.SurfaceArea
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--- Get the smallest radius encompassing all points of the polygon zone.
|
--- Get the smallest radius encompassing all points of the polygon zone.
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @return #number Radius of the zone in meters.
|
-- @return #number Radius of the zone in meters.
|
||||||
@ -2488,31 +2730,25 @@ function ZONE_POLYGON_BASE:IsVec3InZone( Vec3 )
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Define a random @{DCS#Vec2} within the zone.
|
--- Define a random @{DCS#Vec2} within the zone.
|
||||||
|
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @return DCS#Vec2 The Vec2 coordinate.
|
-- @return DCS#Vec2 The Vec2 coordinate.
|
||||||
function ZONE_POLYGON_BASE:GetRandomVec2()
|
function ZONE_POLYGON_BASE:GetRandomVec2()
|
||||||
|
-- make sure we assign weights to the triangles based on their surface area, otherwise
|
||||||
-- It is a bit tricky to find a random point within a polygon. Right now i am doing it the dirty and inefficient way...
|
-- we'll be more likely to generate random points in smaller triangles
|
||||||
|
local weights = {}
|
||||||
-- Get the bounding square.
|
for _, triangle in pairs(self._Triangles) do
|
||||||
local BS = self:GetBoundingSquare()
|
weights[triangle] = triangle.SurfaceArea / self.SurfaceArea
|
||||||
|
|
||||||
local Nmax=1000 ; local n=0
|
|
||||||
while n<Nmax do
|
|
||||||
|
|
||||||
-- Random point in the bounding square.
|
|
||||||
local Vec2={x=math.random(BS.x1, BS.x2), y=math.random(BS.y1, BS.y2)}
|
|
||||||
|
|
||||||
-- Check if this is in the polygon.
|
|
||||||
if self:IsVec2InZone(Vec2) then
|
|
||||||
return Vec2
|
|
||||||
end
|
end
|
||||||
|
|
||||||
n=n+1
|
local random_weight = math.random()
|
||||||
end
|
local accumulated_weight = 0
|
||||||
|
for triangle, weight in pairs(weights) do
|
||||||
self:E("Could not find a random point in the polygon zone!")
|
accumulated_weight = accumulated_weight + weight
|
||||||
return nil
|
if accumulated_weight >= random_weight then
|
||||||
|
return triangle:GetRandomVec2()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.
|
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.
|
||||||
@ -2644,12 +2880,17 @@ function ZONE_POLYGON_BASE:Boundary(Coalition, Color, Radius, Alpha, Segments, C
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do -- Zone_Polygon
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @type ZONE_POLYGON
|
-- @type ZONE_POLYGON
|
||||||
-- @extends #ZONE_POLYGON_BASE
|
-- @extends #ZONE_POLYGON_BASE
|
||||||
|
-- @extends #ZONE_BASE
|
||||||
|
|
||||||
|
|
||||||
--- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon.
|
--- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon, OR by drawings made with the Draw tool
|
||||||
|
--- in the Mission Editor
|
||||||
-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties.
|
-- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties.
|
||||||
--
|
--
|
||||||
-- ## Declare a ZONE_POLYGON directly in the DCS mission editor!
|
-- ## Declare a ZONE_POLYGON directly in the DCS mission editor!
|
||||||
@ -2672,6 +2913,12 @@ end
|
|||||||
-- then SetZone would contain the ZONE_POLYGON object `DefenseZone` as part of the zone collection,
|
-- then SetZone would contain the ZONE_POLYGON object `DefenseZone` as part of the zone collection,
|
||||||
-- without much scripting overhead!
|
-- without much scripting overhead!
|
||||||
--
|
--
|
||||||
|
-- This class now also supports drawings made with the Draw tool in the Mission Editor. Any drawing made with Line > Segments > Closed, Polygon > Rect or Polygon > Free can be
|
||||||
|
-- made into a ZONE_POLYGON.
|
||||||
|
--
|
||||||
|
-- This class has been updated to use a accurate way of generating random points inside the polygon without having to use trial and error guesses.
|
||||||
|
-- You can also get the surface area of the polygon now, handy if you want measure which coalition has the largest captured area, for example.
|
||||||
|
--
|
||||||
-- @field #ZONE_POLYGON
|
-- @field #ZONE_POLYGON
|
||||||
ZONE_POLYGON = {
|
ZONE_POLYGON = {
|
||||||
ClassName="ZONE_POLYGON",
|
ClassName="ZONE_POLYGON",
|
||||||
@ -2732,6 +2979,49 @@ function ZONE_POLYGON:NewFromGroupName( GroupName )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Constructor to create a ZONE_POLYGON instance, taking the name of a drawing made with the draw tool in the Mission Editor.
|
||||||
|
-- @param #ZONE_POLYGON self
|
||||||
|
-- @param #string DrawingName The name of the drawing in the Mission Editor
|
||||||
|
-- @return #ZONE_POLYGON self
|
||||||
|
function ZONE_POLYGON:NewFromDrawing(DrawingName)
|
||||||
|
local points = {}
|
||||||
|
for _, layer in pairs(env.mission.drawings.layers) do
|
||||||
|
for _, object in pairs(layer["objects"]) do
|
||||||
|
if object["name"] == DrawingName then
|
||||||
|
if (object["primitiveType"] == "Line" and object["closed"] == true) or (object["polygonMode"] == "free") then
|
||||||
|
-- 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
|
||||||
|
for _, point in UTILS.spairs(object["points"]) do
|
||||||
|
local p = {x = object["mapX"] + point["x"],
|
||||||
|
y = object["mapY"] + point["y"] }
|
||||||
|
table.add(points, p)
|
||||||
|
end
|
||||||
|
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
|
||||||
|
-- we need to rotate the points around the center of the rects by an angle. UTILS.RotatePointAroundPivot was
|
||||||
|
-- committed in an earlier commit
|
||||||
|
local angle = object["angle"]
|
||||||
|
local half_width = object["width"] / 2
|
||||||
|
local half_height = object["height"] / 2
|
||||||
|
|
||||||
|
local center = { x = object["mapX"], y = object["mapY"] }
|
||||||
|
local p1 = UTILS.RotatePointAroundPivot({ x = center.x - half_height, y = center.y + half_width }, center, angle)
|
||||||
|
local p2 = UTILS.RotatePointAroundPivot({ x = center.x + half_height, y = center.y + half_width }, center, angle)
|
||||||
|
local p3 = UTILS.RotatePointAroundPivot({ x = center.x + half_height, y = center.y - half_width }, center, angle)
|
||||||
|
local p4 = UTILS.RotatePointAroundPivot({ x = center.x - half_height, y = center.y - half_width }, center, angle)
|
||||||
|
|
||||||
|
points = {p1, p2, p3, p4}
|
||||||
|
else
|
||||||
|
-- something else that might be added in the future
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local self = BASE:Inherit(self, ZONE_POLYGON_BASE:New(DrawingName, points))
|
||||||
|
_EVENTDISPATCHER:CreateEventNewZone(self)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Find a polygon zone in the _DATABASE using the name of the polygon zone.
|
--- Find a polygon zone in the _DATABASE using the name of the polygon zone.
|
||||||
-- @param #ZONE_POLYGON self
|
-- @param #ZONE_POLYGON self
|
||||||
@ -3093,9 +3383,11 @@ function ZONE_POLYGON:IsNoneInZone()
|
|||||||
return self:CountScannedCoalitions() == 0
|
return self:CountScannedCoalitions() == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
do -- ZONE_ELASTIC
|
do -- ZONE_ELASTIC
|
||||||
|
|
||||||
|
---
|
||||||
-- @type ZONE_ELASTIC
|
-- @type ZONE_ELASTIC
|
||||||
-- @field #table points Points in 2D.
|
-- @field #table points Points in 2D.
|
||||||
-- @field #table setGroups Set of GROUPs.
|
-- @field #table setGroups Set of GROUPs.
|
||||||
@ -3294,8 +3586,238 @@ do -- ZONE_ELASTIC
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- ZONE_OVAL created from a center point, major axis, minor axis, and angle.
|
||||||
|
-- Ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua
|
||||||
|
-- @type ZONE_OVAL
|
||||||
|
-- @extends Core.Zone#ZONE_BASE
|
||||||
|
|
||||||
|
--- ## ZONE_OVAL class, extends @{#ZONE_BASE}
|
||||||
|
--
|
||||||
|
-- The ZONE_OVAL class is defined by a center point, major axis, minor axis, and angle.
|
||||||
|
-- This class implements the inherited functions from @{#ZONE_BASE} taking into account the own zone format and properties.
|
||||||
|
--
|
||||||
|
-- @field #ZONE_OVAL
|
||||||
|
ZONE_OVAL = {
|
||||||
|
ClassName = "OVAL",
|
||||||
|
ZoneName="",
|
||||||
|
MajorAxis = nil,
|
||||||
|
MinorAxis = nil,
|
||||||
|
Angle = 0,
|
||||||
|
DrawPoly = nil -- let's just use a ZONE_POLYGON to draw the ZONE_OVAL on the map
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Creates a new ZONE_OVAL from a center point, major axis, minor axis, and angle.
|
||||||
|
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua
|
||||||
|
-- @param #table vec2 The center point of the oval
|
||||||
|
-- @param #number major_axis The major axis of the oval
|
||||||
|
-- @param #number minor_axis The minor axis of the oval
|
||||||
|
-- @param #number angle The angle of the oval
|
||||||
|
-- @return #ZONE_OVAL The new oval
|
||||||
|
function ZONE_OVAL:New(name, vec2, major_axis, minor_axis, angle)
|
||||||
|
self = BASE:Inherit(self, ZONE_BASE:New())
|
||||||
|
self.ZoneName = name
|
||||||
|
self.CenterVec2 = vec2
|
||||||
|
self.MajorAxis = major_axis
|
||||||
|
self.MinorAxis = minor_axis
|
||||||
|
self.Angle = angle or 0
|
||||||
|
|
||||||
|
_DATABASE:AddZone(name, self)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Constructor to create a ZONE_OVAL instance, taking the name of a drawing made with the draw tool in the Mission Editor.
|
||||||
|
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @param #string DrawingName The name of the drawing in the Mission Editor
|
||||||
|
-- @return #ZONE_OVAL self
|
||||||
|
function ZONE_OVAL:NewFromDrawing(DrawingName)
|
||||||
|
self = BASE:Inherit(self, ZONE_BASE:New(DrawingName))
|
||||||
|
for _, layer in pairs(env.mission.drawings.layers) do
|
||||||
|
for _, object in pairs(layer["objects"]) do
|
||||||
|
if string.find(object["name"], DrawingName, 1, true) then
|
||||||
|
if object["polygonMode"] == "oval" then
|
||||||
|
self.CenterVec2 = { x = object["mapX"], y = object["mapY"] }
|
||||||
|
self.MajorAxis = object["r1"]
|
||||||
|
self.MinorAxis = object["r2"]
|
||||||
|
self.Angle = object["angle"]
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
_DATABASE:AddZone(DrawingName, self)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the major axis of the oval.
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @return #number The major axis of the oval
|
||||||
|
function ZONE_OVAL:GetMajorAxis()
|
||||||
|
return self.MajorAxis
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the minor axis of the oval.
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @return #number The minor axis of the oval
|
||||||
|
function ZONE_OVAL:GetMinorAxis()
|
||||||
|
return self.MinorAxis
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Gets the angle of the oval.
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @return #number The angle of the oval
|
||||||
|
function ZONE_OVAL:GetAngle()
|
||||||
|
return self.Angle
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns a the center point of the oval
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @return #table The center Vec2
|
||||||
|
function ZONE_OVAL:GetVec2()
|
||||||
|
return self.CenterVec2
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Checks if a point is contained within the oval.
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @param #table point The point to check
|
||||||
|
-- @return #bool True if the point is contained, false otherwise
|
||||||
|
function ZONE_OVAL:IsVec2InZone(vec2)
|
||||||
|
local cos, sin = math.cos, math.sin
|
||||||
|
local dx = vec2.x - self.CenterVec2.x
|
||||||
|
local dy = vec2.y - self.CenterVec2.y
|
||||||
|
local rx = dx * cos(self.Angle) + dy * sin(self.Angle)
|
||||||
|
local ry = -dx * sin(self.Angle) + dy * cos(self.Angle)
|
||||||
|
return rx * rx / (self.MajorAxis * self.MajorAxis) + ry * ry / (self.MinorAxis * self.MinorAxis) <= 1
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval.
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @return #table The bounding box of the oval
|
||||||
|
function ZONE_OVAL:GetBoundingSquare()
|
||||||
|
local min_x = self.CenterVec2.x - self.MajorAxis
|
||||||
|
local min_y = self.CenterVec2.y - self.MinorAxis
|
||||||
|
local max_x = self.CenterVec2.x + self.MajorAxis
|
||||||
|
local max_y = self.CenterVec2.y + self.MinorAxis
|
||||||
|
|
||||||
|
return {
|
||||||
|
{x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find points on the edge of the oval
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @param #number num_points How many points should be found. More = smoother shape
|
||||||
|
-- @return #table Points on he edge
|
||||||
|
function ZONE_OVAL:PointsOnEdge(num_points)
|
||||||
|
num_points = num_points or 40
|
||||||
|
local points = {}
|
||||||
|
local dtheta = 2 * math.pi / num_points
|
||||||
|
|
||||||
|
for i = 0, num_points - 1 do
|
||||||
|
local theta = i * dtheta
|
||||||
|
local x = self.CenterVec2.x + self.MajorAxis * math.cos(theta) * math.cos(self.Angle) - self.MinorAxis * math.sin(theta) * math.sin(self.Angle)
|
||||||
|
local y = self.CenterVec2.y + self.MajorAxis * math.cos(theta) * math.sin(self.Angle) + self.MinorAxis * math.sin(theta) * math.cos(self.Angle)
|
||||||
|
table.insert(points, {x = x, y = y})
|
||||||
|
end
|
||||||
|
|
||||||
|
return points
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns a random Vec2 within the oval.
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @return #table The random Vec2
|
||||||
|
function ZONE_OVAL:GetRandomVec2()
|
||||||
|
local theta = math.rad(self.Angle)
|
||||||
|
|
||||||
|
local random_point = math.sqrt(math.random()) --> uniformly
|
||||||
|
--local random_point = math.random() --> more clumped around center
|
||||||
|
local phi = math.random() * 2 * math.pi
|
||||||
|
local x_c = random_point * math.cos(phi)
|
||||||
|
local y_c = random_point * math.sin(phi)
|
||||||
|
local x_e = x_c * self.MajorAxis
|
||||||
|
local y_e = y_c * self.MinorAxis
|
||||||
|
local rx = (x_e * math.cos(theta) - y_e * math.sin(theta)) + self.CenterVec2.x
|
||||||
|
local ry = (x_e * math.sin(theta) + y_e * math.cos(theta)) + self.CenterVec2.y
|
||||||
|
|
||||||
|
return {x=rx, y=ry}
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Define a random @{Core.Point#POINT_VEC2} within the zone.
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
|
||||||
|
function ZONE_OVAL:GetRandomPointVec2()
|
||||||
|
return POINT_VEC2:NewFromVec2(self:GetRandomVec2())
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Define a random @{Core.Point#POINT_VEC2} within the zone.
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
|
||||||
|
function ZONE_OVAL:GetRandomPointVec3()
|
||||||
|
return POINT_VEC2:NewFromVec3(self:GetRandomVec2())
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draw the zone on the F10 map.
|
||||||
|
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Oval.lua
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All.
|
||||||
|
-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red.
|
||||||
|
-- @param #number Alpha Transparency [0,1]. Default 1.
|
||||||
|
-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. -- doesn't seem to work
|
||||||
|
-- @param #number FillAlpha Transparency [0,1]. Default 0.15. -- doesn't seem to work
|
||||||
|
-- @param #number LineType 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 (Optional) Mark is readonly and cannot be removed by users. Default false.
|
||||||
|
-- @return #ZONE_OVAL self
|
||||||
|
function ZONE_OVAL:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType)
|
||||||
|
Coalition = Coalition or self:GetDrawCoalition()
|
||||||
|
|
||||||
|
-- Set draw coalition.
|
||||||
|
self:SetDrawCoalition(Coalition)
|
||||||
|
|
||||||
|
Color = Color or self:GetColorRGB()
|
||||||
|
Alpha = Alpha or 1
|
||||||
|
|
||||||
|
-- Set color.
|
||||||
|
self:SetColor(Color, Alpha)
|
||||||
|
|
||||||
|
FillColor = FillColor or self:GetFillColorRGB()
|
||||||
|
if not FillColor then
|
||||||
|
UTILS.DeepCopy(Color)
|
||||||
|
end
|
||||||
|
FillAlpha = FillAlpha or self:GetFillColorAlpha()
|
||||||
|
if not FillAlpha then
|
||||||
|
FillAlpha = 0.15
|
||||||
|
end
|
||||||
|
|
||||||
|
LineType = LineType or 1
|
||||||
|
|
||||||
|
-- Set fill color -----------> has fill color worked in recent versions of DCS?
|
||||||
|
-- doing something like
|
||||||
|
--
|
||||||
|
-- trigger.action.markupToAll(7, -1, 501, p.Coords[1]:GetVec3(), p.Coords[2]:GetVec3(),p.Coords[3]:GetVec3(),p.Coords[4]:GetVec3(),{1,0,0, 1}, {1,0,0, 1}, 4, false, Text or "")
|
||||||
|
--
|
||||||
|
-- doesn't seem to fill in the shape for an n-sided polygon
|
||||||
|
self:SetFillColor(FillColor, FillAlpha)
|
||||||
|
|
||||||
|
self.DrawPoly = ZONE_POLYGON:NewFromPointsArray(self.ZoneName, self:PointsOnEdge(80))
|
||||||
|
self.DrawPoly:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove drawing from F10 map
|
||||||
|
-- @param #ZONE_OVAL self
|
||||||
|
function ZONE_OVAL:UndrawZone()
|
||||||
|
if self.DrawPoly then
|
||||||
|
self.DrawPoly:UndrawZone()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
do -- ZONE_AIRBASE
|
do -- ZONE_AIRBASE
|
||||||
|
|
||||||
|
---
|
||||||
-- @type ZONE_AIRBASE
|
-- @type ZONE_AIRBASE
|
||||||
-- @field #boolean isShip If `true`, airbase is a ship.
|
-- @field #boolean isShip If `true`, airbase is a ship.
|
||||||
-- @field #boolean isHelipad If `true`, airbase is a helipad.
|
-- @field #boolean isHelipad If `true`, airbase is a helipad.
|
||||||
|
|||||||
@ -39,7 +39,8 @@
|
|||||||
|
|
||||||
do -- DETECTION_BASE
|
do -- DETECTION_BASE
|
||||||
|
|
||||||
--- @type DETECTION_BASE
|
---
|
||||||
|
-- @type DETECTION_BASE
|
||||||
-- @field Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
|
-- @field Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
|
||||||
-- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected.
|
-- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected.
|
||||||
-- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects.
|
-- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects.
|
||||||
@ -91,6 +92,11 @@ do -- DETECTION_BASE
|
|||||||
--
|
--
|
||||||
-- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
|
-- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
|
||||||
--
|
--
|
||||||
|
--
|
||||||
|
-- ## Radar Blur - use to make the radar less exact, e.g. for WWII scenarios
|
||||||
|
--
|
||||||
|
-- * @{DETECTION_BASE.SetRadarBlur}(): Set the radar blur to be used.
|
||||||
|
--
|
||||||
-- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list
|
-- ## **DETECTION_ derived classes** group the detected units into a **DetectedItems[]** list
|
||||||
--
|
--
|
||||||
-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later
|
-- DETECTION_BASE derived classes build a list called DetectedItems[], which is essentially a first later
|
||||||
@ -269,10 +275,12 @@ do -- DETECTION_BASE
|
|||||||
DetectedItemsByIndex = {},
|
DetectedItemsByIndex = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
--- @type DETECTION_BASE.DetectedObjects
|
---
|
||||||
|
-- @type DETECTION_BASE.DetectedObjects
|
||||||
-- @list <#DETECTION_BASE.DetectedObject>
|
-- @list <#DETECTION_BASE.DetectedObject>
|
||||||
|
|
||||||
--- @type DETECTION_BASE.DetectedObject
|
---
|
||||||
|
-- @type DETECTION_BASE.DetectedObject
|
||||||
-- @field #string Name
|
-- @field #string Name
|
||||||
-- @field #boolean IsVisible
|
-- @field #boolean IsVisible
|
||||||
-- @field #boolean KnowType
|
-- @field #boolean KnowType
|
||||||
@ -284,7 +292,8 @@ do -- DETECTION_BASE
|
|||||||
-- @field #boolean LastPos
|
-- @field #boolean LastPos
|
||||||
-- @field #number LastVelocity
|
-- @field #number LastVelocity
|
||||||
|
|
||||||
--- @type DETECTION_BASE.DetectedItems
|
---
|
||||||
|
-- @type DETECTION_BASE.DetectedItems
|
||||||
-- @list <#DETECTION_BASE.DetectedItem>
|
-- @list <#DETECTION_BASE.DetectedItem>
|
||||||
|
|
||||||
--- Detected item data structure.
|
--- Detected item data structure.
|
||||||
@ -522,7 +531,7 @@ do -- DETECTION_BASE
|
|||||||
|
|
||||||
do -- State Transition Handling
|
do -- State Transition Handling
|
||||||
|
|
||||||
--- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @param #string From The From State string.
|
-- @param #string From The From State string.
|
||||||
-- @param #string Event The Event string.
|
-- @param #string Event The Event string.
|
||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
@ -530,7 +539,7 @@ do -- DETECTION_BASE
|
|||||||
self:__Detect( 1 )
|
self:__Detect( 1 )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @param #string From The From State string.
|
-- @param #string From The From State string.
|
||||||
-- @param #string Event The Event string.
|
-- @param #string Event The Event string.
|
||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
@ -570,7 +579,7 @@ do -- DETECTION_BASE
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @param #number The amount of alive recce.
|
-- @param #number The amount of alive recce.
|
||||||
function DETECTION_BASE:CountAliveRecce()
|
function DETECTION_BASE:CountAliveRecce()
|
||||||
|
|
||||||
@ -578,7 +587,7 @@ do -- DETECTION_BASE
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
function DETECTION_BASE:ForEachAliveRecce( IteratorFunction, ... )
|
function DETECTION_BASE:ForEachAliveRecce( IteratorFunction, ... )
|
||||||
self:F2( arg )
|
self:F2( arg )
|
||||||
|
|
||||||
@ -587,7 +596,7 @@ do -- DETECTION_BASE
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #DETECTION_BASE self
|
-- @param #DETECTION_BASE self
|
||||||
-- @param #string From The From State string.
|
-- @param #string From The From State string.
|
||||||
-- @param #string Event The Event string.
|
-- @param #string Event The Event string.
|
||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
@ -712,6 +721,31 @@ do -- DETECTION_BASE
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Calculate radar blur probability
|
||||||
|
|
||||||
|
if self.RadarBlur then
|
||||||
|
MESSAGE:New("Radar Blur",10):ToLogIf(self.debug):ToAllIf(self.verbose)
|
||||||
|
local minheight = self.RadarBlurMinHeight or 250 -- meters
|
||||||
|
local thresheight = self.RadarBlurThresHeight or 90 -- 10% chance to find a low flying group
|
||||||
|
local thresblur = self.RadarBlurThresBlur or 85 -- 25% chance to escape the radar overall
|
||||||
|
local dist = math.floor(Distance)
|
||||||
|
if dist <= self.RadarBlurClosing then
|
||||||
|
thresheight = (((dist*dist)/self.RadarBlurClosingSquare)*thresheight)
|
||||||
|
thresblur = (((dist*dist)/self.RadarBlurClosingSquare)*thresblur)
|
||||||
|
end
|
||||||
|
local fheight = math.floor(math.random(1,10000)/100)
|
||||||
|
local fblur = math.floor(math.random(1,10000)/100)
|
||||||
|
local unit = UNIT:FindByName(DetectedObjectName)
|
||||||
|
if unit and unit:IsAlive() then
|
||||||
|
local AGL = unit:GetAltitude(true)
|
||||||
|
MESSAGE:New("Unit "..DetectedObjectName.." is at "..math.floor(AGL).."m. Distance "..math.floor(Distance).."km.",10):ToLogIf(self.debug):ToAllIf(self.verbose)
|
||||||
|
MESSAGE:New(string.format("fheight = %d/%d | fblur = %d/%d",fheight,thresheight,fblur,thresblur),10):ToLogIf(self.debug):ToAllIf(self.verbose)
|
||||||
|
if fblur > thresblur then DetectionAccepted = false end
|
||||||
|
if AGL <= minheight and fheight < thresheight then DetectionAccepted = false end
|
||||||
|
MESSAGE:New("Detection Accepted = "..tostring(DetectionAccepted),10):ToLogIf(self.debug):ToAllIf(self.verbose)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Calculate additional probabilities
|
-- Calculate additional probabilities
|
||||||
|
|
||||||
if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.DistanceProbability then
|
if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.DistanceProbability then
|
||||||
@ -1012,6 +1046,23 @@ do -- DETECTION_BASE
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Method to make the radar detection less accurate, e.g. for WWII scenarios.
|
||||||
|
-- @param #DETECTION_BASE self
|
||||||
|
-- @param #number minheight Minimum flight height to be detected, in meters AGL (above ground)
|
||||||
|
-- @param #number thresheight Threshold to escape the radar if flying below minheight, defaults to 90 (90% escape chance)
|
||||||
|
-- @param #number thresblur Threshold to be detected by the radar overall, defaults to 85 (85% chance to be found)
|
||||||
|
-- @param #number closing Closing-in in km - the limit of km from which on it becomes increasingly difficult to escape radar detection if flying towards the radar position. Should be about 1/3 of the radar detection radius in kilometers, defaults to 20.
|
||||||
|
-- @return #DETECTION_BASE self
|
||||||
|
function DETECTION_BASE:SetRadarBlur(minheight,thresheight,thresblur,closing)
|
||||||
|
self.RadarBlur = true
|
||||||
|
self.RadarBlurMinHeight = minheight or 250 -- meters
|
||||||
|
self.RadarBlurThresHeight = thresheight or 90 -- 10% chance to find a low flying group
|
||||||
|
self.RadarBlurThresBlur = thresblur or 85 -- 25% chance to escape the radar overall
|
||||||
|
self.RadarBlurClosing = closing or 20 -- 20km
|
||||||
|
self.RadarBlurClosingSquare = self.RadarBlurClosing * self.RadarBlurClosing
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
do
|
||||||
@ -1354,7 +1405,7 @@ do -- DETECTION_BASE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
--- @param DCS#Unit FoundDCSUnit
|
-- @param DCS#Unit FoundDCSUnit
|
||||||
-- @param Wrapper.Group#GROUP ReportGroup
|
-- @param Wrapper.Group#GROUP ReportGroup
|
||||||
-- @param Core.Set#SET_GROUP ReportSetGroup
|
-- @param Core.Set#SET_GROUP ReportSetGroup
|
||||||
local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData )
|
local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData )
|
||||||
@ -1419,7 +1470,7 @@ do -- DETECTION_BASE
|
|||||||
DetectedItem.PlayersNearBy = nil
|
DetectedItem.PlayersNearBy = nil
|
||||||
|
|
||||||
_DATABASE:ForEachPlayer(
|
_DATABASE:ForEachPlayer(
|
||||||
--- @param Wrapper.Unit#UNIT PlayerUnit
|
-- @param Wrapper.Unit#UNIT PlayerUnit
|
||||||
function( PlayerUnitName )
|
function( PlayerUnitName )
|
||||||
local PlayerUnit = UNIT:FindByName( PlayerUnitName )
|
local PlayerUnit = UNIT:FindByName( PlayerUnitName )
|
||||||
|
|
||||||
@ -1976,7 +2027,8 @@ end
|
|||||||
|
|
||||||
do -- DETECTION_UNITS
|
do -- DETECTION_UNITS
|
||||||
|
|
||||||
--- @type DETECTION_UNITS
|
---
|
||||||
|
-- @type DETECTION_UNITS
|
||||||
-- @field DCS#Distance DetectionRange The range till which targets are detected.
|
-- @field DCS#Distance DetectionRange The range till which targets are detected.
|
||||||
-- @extends Functional.Detection#DETECTION_BASE
|
-- @extends Functional.Detection#DETECTION_BASE
|
||||||
|
|
||||||
@ -2232,7 +2284,8 @@ end
|
|||||||
|
|
||||||
do -- DETECTION_TYPES
|
do -- DETECTION_TYPES
|
||||||
|
|
||||||
--- @type DETECTION_TYPES
|
---
|
||||||
|
-- @type DETECTION_TYPES
|
||||||
-- @extends Functional.Detection#DETECTION_BASE
|
-- @extends Functional.Detection#DETECTION_BASE
|
||||||
|
|
||||||
--- Will detect units within the battle zone.
|
--- Will detect units within the battle zone.
|
||||||
@ -2435,7 +2488,8 @@ end
|
|||||||
|
|
||||||
do -- DETECTION_AREAS
|
do -- DETECTION_AREAS
|
||||||
|
|
||||||
--- @type DETECTION_AREAS
|
---
|
||||||
|
-- @type DETECTION_AREAS
|
||||||
-- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target.
|
-- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target.
|
||||||
-- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange.
|
-- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Core.Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange.
|
||||||
-- @extends Functional.Detection#DETECTION_BASE
|
-- @extends Functional.Detection#DETECTION_BASE
|
||||||
@ -2961,7 +3015,7 @@ do -- DETECTION_AREAS
|
|||||||
|
|
||||||
-- DetectedSet:Flush( self )
|
-- DetectedSet:Flush( self )
|
||||||
|
|
||||||
DetectedSet:ForEachUnit( --- @param Wrapper.Unit#UNIT DetectedUnit
|
DetectedSet:ForEachUnit( -- @param Wrapper.Unit#UNIT DetectedUnit
|
||||||
function( DetectedUnit )
|
function( DetectedUnit )
|
||||||
if DetectedUnit:IsAlive() then
|
if DetectedUnit:IsAlive() then
|
||||||
-- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() )
|
-- self:T( "Detected Set #" .. DetectedItem.ID .. ":" .. DetectedUnit:GetName() )
|
||||||
|
|||||||
@ -1207,18 +1207,18 @@ end
|
|||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
|
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
|
||||||
|
|
||||||
if PathToSRS then
|
if PathToSRS or MSRS.path then
|
||||||
|
|
||||||
self.useSRS=true
|
self.useSRS=true
|
||||||
|
|
||||||
self.controlmsrs=MSRS:New(PathToSRS, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0)
|
self.controlmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 256, Modulation or radio.modulation.AM, Volume or 1.0)
|
||||||
self.controlmsrs:SetPort(Port)
|
self.controlmsrs:SetPort(Port or MSRS.port)
|
||||||
self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
self.controlmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
||||||
self.controlmsrs:SetLabel("RANGEC")
|
self.controlmsrs:SetLabel("RANGEC")
|
||||||
self.controlsrsQ = MSRSQUEUE:New("CONTROL")
|
self.controlsrsQ = MSRSQUEUE:New("CONTROL")
|
||||||
|
|
||||||
self.instructmsrs=MSRS:New(PathToSRS, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0)
|
self.instructmsrs=MSRS:New(PathToSRS or MSRS.path, Frequency or 305, Modulation or radio.modulation.AM, Volume or 1.0)
|
||||||
self.instructmsrs:SetPort(Port)
|
self.instructmsrs:SetPort(Port or MSRS.port)
|
||||||
self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
self.instructmsrs:SetCoalition(Coalition or coalition.side.BLUE)
|
||||||
self.instructmsrs:SetLabel("RANGEI")
|
self.instructmsrs:SetLabel("RANGEI")
|
||||||
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
|
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
|
||||||
@ -1234,7 +1234,7 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume,
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (SRS) Set range control frequency and voice.
|
--- (SRS) Set range control frequency and voice. Use `RANGE:SetSRS()` once first before using this function.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param #number frequency Frequency in MHz. Default 256 MHz.
|
-- @param #number frequency Frequency in MHz. Default 256 MHz.
|
||||||
-- @param #number modulation Modulation, defaults to radio.modulation.AM.
|
-- @param #number modulation Modulation, defaults to radio.modulation.AM.
|
||||||
@ -1244,6 +1244,10 @@ end
|
|||||||
-- @param #string relayunitname Name of the unit used for transmission location.
|
-- @param #string relayunitname Name of the unit used for transmission location.
|
||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender, relayunitname )
|
function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender, relayunitname )
|
||||||
|
if not self.instructmsrs then
|
||||||
|
self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeControl!")
|
||||||
|
return self
|
||||||
|
end
|
||||||
self.rangecontrolfreq = frequency or 256
|
self.rangecontrolfreq = frequency or 256
|
||||||
self.controlmsrs:SetFrequencies(self.rangecontrolfreq)
|
self.controlmsrs:SetFrequencies(self.rangecontrolfreq)
|
||||||
self.controlmsrs:SetModulations(modulation or radio.modulation.AM)
|
self.controlmsrs:SetModulations(modulation or radio.modulation.AM)
|
||||||
@ -1259,7 +1263,7 @@ function RANGE:SetSRSRangeControl( frequency, modulation, voice, culture, gender
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- (SRS) Set range instructor frequency and voice.
|
--- (SRS) Set range instructor frequency and voice. Use `RANGE:SetSRS()` once first before using this function.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param #number frequency Frequency in MHz. Default 305 MHz.
|
-- @param #number frequency Frequency in MHz. Default 305 MHz.
|
||||||
-- @param #number modulation Modulation, defaults to radio.modulation.AM.
|
-- @param #number modulation Modulation, defaults to radio.modulation.AM.
|
||||||
@ -1269,6 +1273,10 @@ end
|
|||||||
-- @param #string relayunitname Name of the unit used for transmission location.
|
-- @param #string relayunitname Name of the unit used for transmission location.
|
||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gender, relayunitname )
|
function RANGE:SetSRSRangeInstructor( frequency, modulation, voice, culture, gender, relayunitname )
|
||||||
|
if not self.instructmsrs then
|
||||||
|
self:E(self.lid.."Use myrange:SetSRS() once first before using myrange:SetSRSRangeInstructor!")
|
||||||
|
return self
|
||||||
|
end
|
||||||
self.instructorfreq = frequency or 305
|
self.instructorfreq = frequency or 305
|
||||||
self.instructmsrs:SetFrequencies(self.instructorfreq)
|
self.instructmsrs:SetFrequencies(self.instructorfreq)
|
||||||
self.instructmsrs:SetModulations(modulation or radio.modulation.AM)
|
self.instructmsrs:SetModulations(modulation or radio.modulation.AM)
|
||||||
@ -2179,7 +2187,7 @@ function RANGE:onafterExitRange( From, Event, To, player )
|
|||||||
|
|
||||||
local text = "You left the bombing range zone. "
|
local text = "You left the bombing range zone. "
|
||||||
|
|
||||||
local r=math.random(2)
|
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!"
|
||||||
|
|||||||
@ -7405,6 +7405,8 @@ function WAREHOUSE:_CheckRequestNow(request)
|
|||||||
-- Check if at least one (cargo) asset is available.
|
-- Check if at least one (cargo) asset is available.
|
||||||
if _nassets>0 then
|
if _nassets>0 then
|
||||||
|
|
||||||
|
local asset=_assets[1] --#WAREHOUSE.Assetitem
|
||||||
|
|
||||||
-- Get the attibute of the requested asset.
|
-- Get the attibute of the requested asset.
|
||||||
_assetattribute=_assets[1].attribute
|
_assetattribute=_assets[1].attribute
|
||||||
_assetcategory=_assets[1].category
|
_assetcategory=_assets[1].category
|
||||||
@ -7415,6 +7417,19 @@ function WAREHOUSE:_CheckRequestNow(request)
|
|||||||
|
|
||||||
if self.airbase and self.airbase:GetCoalition()==self:GetCoalition() then
|
if self.airbase and self.airbase:GetCoalition()==self:GetCoalition() then
|
||||||
|
|
||||||
|
-- Check if DCS warehouse of airbase has enough assets
|
||||||
|
if self.airbase.storage then
|
||||||
|
local nS=self.airbase.storage:GetAmount(asset.unittype)
|
||||||
|
local nA=asset.nunits*request.nasset -- Number of units requested
|
||||||
|
if nS<nA then
|
||||||
|
local text=string.format("Warehouse %s: Request denied! DCS Warehouse has only %d assets of type %s ==> NOT enough to spawn the requested %d asset units (%d groups)",
|
||||||
|
self.alias, nS, asset.unittype, nA, request.nasset)
|
||||||
|
self:_InfoMessage(text, 5)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
if self:IsRunwayOperational() or _assetairstart then
|
if self:IsRunwayOperational() or _assetairstart then
|
||||||
|
|
||||||
if _assetairstart then
|
if _assetairstart then
|
||||||
@ -7531,6 +7546,9 @@ function WAREHOUSE:_CheckRequestNow(request)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
elseif _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -316,6 +316,12 @@
|
|||||||
--
|
--
|
||||||
-- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Note that backslashes need to be escaped or simply use slashes (as in linux).
|
-- This uses a male voice with US accent. It requires SRS to be installed in the `D:\DCS\_SRS\` directory. Note that backslashes need to be escaped or simply use slashes (as in linux).
|
||||||
--
|
--
|
||||||
|
-- ### SRS can use multiple frequencies:
|
||||||
|
--
|
||||||
|
-- atis=ATIS:New("Batumi", {305,103.85}, {radio.modulation.AM,radio.modulation.FM})
|
||||||
|
-- atis:SetSRS("D:\\DCS\\_SRS\\", "male", "en-US")
|
||||||
|
-- atis:Start()
|
||||||
|
--
|
||||||
-- ### SRS Localization
|
-- ### SRS Localization
|
||||||
--
|
--
|
||||||
-- You can localize the SRS output, all you need is to provide a table of translations and set the `locale` of your instance. You need to provide the translations in your script **before you instantiate your ATIS**.
|
-- You can localize the SRS output, all you need is to provide a table of translations and set the `locale` of your instance. You need to provide the translations in your script **before you instantiate your ATIS**.
|
||||||
@ -884,13 +890,14 @@ _ATIS = {}
|
|||||||
|
|
||||||
--- ATIS class version.
|
--- ATIS class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ATIS.version = "0.10.3"
|
ATIS.version = "0.10.4"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: Correct fog for elevation.
|
-- TODO: Correct fog for elevation.
|
||||||
|
-- DONE: Option to add multiple frequencies for SRS
|
||||||
-- DONE: Zulu time --> Zulu in output.
|
-- DONE: Zulu time --> Zulu in output.
|
||||||
-- DONE: Fix for AB not having a runway - Helopost like Naqoura
|
-- DONE: Fix for AB not having a runway - Helopost like Naqoura
|
||||||
-- DONE: Add new Normandy airfields.
|
-- DONE: Add new Normandy airfields.
|
||||||
@ -899,7 +906,7 @@ ATIS.version = "0.10.3"
|
|||||||
-- DONE: Visibility reported twice over SRS
|
-- DONE: Visibility reported twice over SRS
|
||||||
-- DONE: Add text report for output.
|
-- DONE: Add text report for output.
|
||||||
-- DONE: Add stop FMS functions.
|
-- DONE: Add stop FMS functions.
|
||||||
-- NOGO: Use local time. Not realisitc!
|
-- NOGO: Use local time. Not realistic!
|
||||||
-- DONE: Dew point. Approx. done.
|
-- DONE: Dew point. Approx. done.
|
||||||
-- DONE: Metric units.
|
-- DONE: Metric units.
|
||||||
-- DONE: Set UTC correction.
|
-- DONE: Set UTC correction.
|
||||||
@ -915,8 +922,8 @@ ATIS.version = "0.10.3"
|
|||||||
--- Create a new ATIS class object for a specific airbase.
|
--- Create a new ATIS class object for a specific airbase.
|
||||||
-- @param #ATIS self
|
-- @param #ATIS self
|
||||||
-- @param #string AirbaseName Name of the airbase.
|
-- @param #string AirbaseName Name of the airbase.
|
||||||
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz.
|
-- @param #number Frequency Radio frequency in MHz. Default 143.00 MHz. When using **SRS** this can be passed as a table of multiple frequencies.
|
||||||
-- @param #number Modulation Radio modulation: 0=AM, 1=FM. Default 0=AM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators.
|
-- @param #number Modulation Radio modulation: 0=AM, 1=FM. Default 0=AM. See `radio.modulation.AM` and `radio.modulation.FM` enumerators. When using **SRS** this can be passed as a table of multiple modulations.
|
||||||
-- @return #ATIS self
|
-- @return #ATIS self
|
||||||
function ATIS:New(AirbaseName, Frequency, Modulation)
|
function ATIS:New(AirbaseName, Frequency, Modulation)
|
||||||
|
|
||||||
@ -1594,8 +1601,16 @@ function ATIS:onafterStart( From, Event, To )
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Info.
|
-- Info.
|
||||||
self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) )
|
if type(self.frequency) == "table" then
|
||||||
|
local frequency = table.concat(self.frequency,"/")
|
||||||
|
local modulation = self.modulation
|
||||||
|
if type(self.modulation) == "table" then
|
||||||
|
modulation = table.concat(self.modulation,"/")
|
||||||
|
end
|
||||||
|
self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %s MHz Modulation=%s", ATIS.version, self.airbasename, frequency, modulation ) )
|
||||||
|
else
|
||||||
|
self:I( self.lid .. string.format( "Starting ATIS v%s for airbase %s on %.3f MHz Modulation=%d", ATIS.version, self.airbasename, self.frequency, self.modulation ) )
|
||||||
|
end
|
||||||
-- Start radio queue.
|
-- Start radio queue.
|
||||||
if not self.useSRS then
|
if not self.useSRS then
|
||||||
self.radioqueue = RADIOQUEUE:New( self.frequency, self.modulation, string.format( "ATIS %s", self.airbasename ) )
|
self.radioqueue = RADIOQUEUE:New( self.frequency, self.modulation, string.format( "ATIS %s", self.airbasename ) )
|
||||||
@ -1653,7 +1668,17 @@ function ATIS:onafterStatus( From, Event, To )
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Info text.
|
-- Info text.
|
||||||
local text = string.format( "State %s: Freq=%.3f MHz %s", fsmstate, self.frequency, UTILS.GetModulationName( self.modulation ) )
|
local text = ""
|
||||||
|
if type(self.frequency) == "table" then
|
||||||
|
local frequency = table.concat(self.frequency,"/")
|
||||||
|
local modulation = self.modulation
|
||||||
|
if type(self.modulation) == "table" then
|
||||||
|
modulation = table.concat(self.modulation,"/")
|
||||||
|
end
|
||||||
|
text = string.format( "State %s: Freq=%s MHz %s", fsmstate, frequency, modulation )
|
||||||
|
else
|
||||||
|
text = string.format( "State %s: Freq=%.3f MHz %s", fsmstate, self.frequency, UTILS.GetModulationName( self.modulation ) )
|
||||||
|
end
|
||||||
if self.useSRS then
|
if self.useSRS then
|
||||||
text = text .. string.format( ", SRS path=%s (%s), gender=%s, culture=%s, voice=%s", tostring( self.msrs.path ), tostring( self.msrs.port ), tostring( self.msrs.gender ), tostring( self.msrs.culture ), tostring( self.msrs.voice ) )
|
text = text .. string.format( ", SRS path=%s (%s), gender=%s, culture=%s, voice=%s", tostring( self.msrs.path ), tostring( self.msrs.port ), tostring( self.msrs.gender ), tostring( self.msrs.culture ), tostring( self.msrs.voice ) )
|
||||||
else
|
else
|
||||||
@ -2919,8 +2944,17 @@ function ATIS:UpdateMarker( information, runact, wind, altimeter, temperature )
|
|||||||
if self.markerid then
|
if self.markerid then
|
||||||
self.airbase:GetCoordinate():RemoveMark( self.markerid )
|
self.airbase:GetCoordinate():RemoveMark( self.markerid )
|
||||||
end
|
end
|
||||||
|
local text = ""
|
||||||
local text = string.format( "ATIS on %.3f %s, %s:\n", self.frequency, UTILS.GetModulationName( self.modulation ), tostring( information ) )
|
if type(self.frequency) == "table" then
|
||||||
|
local frequency = table.concat(self.frequency,"/")
|
||||||
|
local modulation = self.modulation
|
||||||
|
if type(modulation) == "table" then
|
||||||
|
modulation = table.concat(self.modulation,"/")
|
||||||
|
end
|
||||||
|
text = string.format( "ATIS on %s %s, %s:\n", tostring(frequency), tostring(modulation), tostring( information ) )
|
||||||
|
else
|
||||||
|
text = string.format( "ATIS on %.3f %s, %s:\n", self.frequency, UTILS.GetModulationName( self.modulation ), tostring( information ) )
|
||||||
|
end
|
||||||
text = text .. string.format( "%s\n", tostring( runact ) )
|
text = text .. string.format( "%s\n", tostring( runact ) )
|
||||||
text = text .. string.format( "%s\n", tostring( wind ) )
|
text = text .. string.format( "%s\n", tostring( wind ) )
|
||||||
text = text .. string.format( "%s\n", tostring( altimeter ) )
|
text = text .. string.format( "%s\n", tostring( altimeter ) )
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
-- @module Ops.CTLD
|
-- @module Ops.CTLD
|
||||||
-- @image OPS_CTLD.jpg
|
-- @image OPS_CTLD.jpg
|
||||||
|
|
||||||
-- Last Update November 2023
|
-- Last Update December 2023
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|
||||||
@ -1228,7 +1228,7 @@ CTLD.UnitTypeCapabilities = {
|
|||||||
|
|
||||||
--- CTLD class version.
|
--- CTLD class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
CTLD.version="1.0.43"
|
CTLD.version="1.0.44"
|
||||||
|
|
||||||
--- Instantiate a new CTLD.
|
--- Instantiate a new CTLD.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
@ -2561,6 +2561,40 @@ function CTLD:_ListCratesNearby( _group, _unit)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- (Internal) Function to find and Remove nearby crates.
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param Wrapper.Group#GROUP Group
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit
|
||||||
|
-- @return #CTLD self
|
||||||
|
function CTLD:_RemoveCratesNearby( _group, _unit)
|
||||||
|
self:T(self.lid .. " _RemoveCratesNearby")
|
||||||
|
local finddist = self.CrateDistance or 35
|
||||||
|
local crates,number = self:_FindCratesNearby(_group,_unit, finddist,true) -- #table
|
||||||
|
if number > 0 then
|
||||||
|
local text = REPORT:New("Removing Crates Found Nearby:")
|
||||||
|
text:Add("------------------------------------------------------------")
|
||||||
|
for _,_entry in pairs (crates) do
|
||||||
|
local entry = _entry -- #CTLD_CARGO
|
||||||
|
local name = entry:GetName() --#string
|
||||||
|
local dropped = entry:WasDropped()
|
||||||
|
if dropped then
|
||||||
|
text:Add(string.format("Crate for %s, %dkg removed",name, entry.PerCrateMass))
|
||||||
|
else
|
||||||
|
text:Add(string.format("Crate for %s, %dkg removed",name, entry.PerCrateMass))
|
||||||
|
end
|
||||||
|
entry:GetPositionable():Destroy(false)
|
||||||
|
end
|
||||||
|
if text:GetCount() == 1 then
|
||||||
|
text:Add(" N O N E")
|
||||||
|
end
|
||||||
|
text:Add("------------------------------------------------------------")
|
||||||
|
self:_SendMessage(text:Text(), 30, true, _group)
|
||||||
|
else
|
||||||
|
self:_SendMessage(string.format("No (loadable) crates within %d meters!",finddist), 10, false, _group)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Return distance in meters between two coordinates.
|
--- (Internal) Return distance in meters between two coordinates.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
-- @param Core.Point#COORDINATE _point1 Coordinate one
|
-- @param Core.Point#COORDINATE _point1 Coordinate one
|
||||||
@ -2976,6 +3010,35 @@ function CTLD:IsHercules(Unit)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- (Internal) Function to set troops positions of a template to a nice circle
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Start coordinate to use
|
||||||
|
-- @param #number Radius Radius to be used
|
||||||
|
-- @param #number Heading Heading starting with
|
||||||
|
-- @param #string Template The group template name
|
||||||
|
-- @return #table Positions The positions table
|
||||||
|
function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template)
|
||||||
|
local Positions = {}
|
||||||
|
local template = _DATABASE:GetGroupTemplate(Template)
|
||||||
|
UTILS.PrintTableToLog(template)
|
||||||
|
local numbertroops = #template.units
|
||||||
|
local newcenter = Coordinate:Translate(Radius,((Heading+270)%360))
|
||||||
|
for i=1,360,math.floor(360/numbertroops) do
|
||||||
|
local phead = ((Heading+270+i)%360)
|
||||||
|
local post = newcenter:Translate(Radius,phead)
|
||||||
|
local pos1 = post:GetVec2()
|
||||||
|
local p1t = {
|
||||||
|
x = pos1.x,
|
||||||
|
y = pos1.y,
|
||||||
|
heading = phead,
|
||||||
|
}
|
||||||
|
table.insert(Positions,p1t)
|
||||||
|
end
|
||||||
|
UTILS.PrintTableToLog(Positions)
|
||||||
|
return Positions
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Function to unload troops from heli.
|
--- (Internal) Function to unload troops from heli.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
-- @param Wrapper.Group#GROUP Group
|
-- @param Wrapper.Group#GROUP Group
|
||||||
@ -3027,14 +3090,29 @@ function CTLD:_UnloadTroops(Group, Unit)
|
|||||||
zoneradius = Unit:GetVelocityMPS() or 100
|
zoneradius = Unit:GetVelocityMPS() or 100
|
||||||
end
|
end
|
||||||
local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor)
|
local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname),Group,zoneradius*factor)
|
||||||
local randomcoord = zone:GetRandomCoordinate(10,30*factor):GetVec2()
|
local randomcoord = zone:GetRandomCoordinate(10,30*factor) --:GetVec2()
|
||||||
|
local heading = Group:GetHeading() or 0
|
||||||
|
-- Spawn troops left from us, closer when hovering, further off when landed
|
||||||
|
if hoverunload or grounded then
|
||||||
|
randomcoord = Group:GetCoordinate()
|
||||||
|
-- slightly left from us
|
||||||
|
local Angle = (heading+270)%360
|
||||||
|
local offset = hoverunload and 1.5 or 5
|
||||||
|
randomcoord:Translate(offset,Angle,nil,true)
|
||||||
|
end
|
||||||
|
local tempcount = 0
|
||||||
for _,_template in pairs(temptable) do
|
for _,_template in pairs(temptable) do
|
||||||
self.TroopCounter = self.TroopCounter + 1
|
self.TroopCounter = self.TroopCounter + 1
|
||||||
|
tempcount = tempcount+1
|
||||||
local alias = string.format("%s-%d", _template, math.random(1,100000))
|
local alias = string.format("%s-%d", _template, math.random(1,100000))
|
||||||
|
local rad = 2.5+tempcount
|
||||||
|
local Positions = self:_GetUnitPositions(randomcoord,rad,heading,_template)
|
||||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
||||||
:InitRandomizeUnits(true,20,2)
|
--:InitRandomizeUnits(true,20,2)
|
||||||
|
--:InitHeading(heading)
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
:SpawnFromVec2(randomcoord)
|
:InitSetUnitAbsolutePositions(Positions)
|
||||||
|
:SpawnFromVec2(randomcoord:GetVec2())
|
||||||
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type)
|
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter],type)
|
||||||
end -- template loop
|
end -- template loop
|
||||||
cargo:SetWasDropped(true)
|
cargo:SetWasDropped(true)
|
||||||
@ -3637,6 +3715,7 @@ function CTLD:_RefreshF10Menus()
|
|||||||
local loadmenu = MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates, self._LoadCratesNearby, self, _group, _unit)
|
local loadmenu = MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates, self._LoadCratesNearby, self, _group, _unit)
|
||||||
local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates)
|
local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates)
|
||||||
local packmenu = MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit)
|
local packmenu = MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit)
|
||||||
|
local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates)
|
||||||
|
|
||||||
if self.usesubcats then
|
if self.usesubcats then
|
||||||
local subcatmenus = {}
|
local subcatmenus = {}
|
||||||
@ -3672,6 +3751,7 @@ function CTLD:_RefreshF10Menus()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit)
|
listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit)
|
||||||
|
removecrates = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu, self._RemoveCratesNearby, self, _group, _unit)
|
||||||
local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit)
|
local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit)
|
||||||
if not self.nobuildmenu then
|
if not self.nobuildmenu then
|
||||||
local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit)
|
local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit)
|
||||||
|
|||||||
@ -137,7 +137,7 @@ do -- UserSound
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Play the usersound to the given @{Wrapper.Unit}.
|
--- Play the usersound to the given @{Wrapper.Unit}.
|
||||||
-- @param #USERSOUND self
|
-- @param #USERSOUND self
|
||||||
-- @param Wrapper.Unit#UNIT Unit The @{Wrapper.Unit} to play the usersound to.
|
-- @param Wrapper.Unit#UNIT Unit The @{Wrapper.Unit} to play the usersound to.
|
||||||
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
|
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
|
||||||
@ -159,4 +159,24 @@ do -- UserSound
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Play the usersound to the given @{Wrapper.Client}.
|
||||||
|
-- @param #USERSOUND self
|
||||||
|
-- @param Wrapper.Client#CLIENT The @{Wrapper.Client} to play the usersound to.
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds, before the sound is played. Default 0.
|
||||||
|
-- @return #USERSOUND The usersound instance.
|
||||||
|
-- @usage
|
||||||
|
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||||
|
-- local PlayerUnit = CLIENT:FindByPlayerName("Karl Heinz")-- Search for the active client with playername "Karl Heinz", a human player.
|
||||||
|
-- BlueVictory:ToClient( PlayerUnit ) -- Play the victory sound to the player unit.
|
||||||
|
--
|
||||||
|
function USERSOUND:ToClient( Client, Delay )
|
||||||
|
Delay=Delay or 0
|
||||||
|
if Delay>0 then
|
||||||
|
SCHEDULER:New(nil, USERSOUND.ToClient,{self, Client}, Delay)
|
||||||
|
else
|
||||||
|
trigger.action.outSoundForUnit( Client:GetID(), self.UserSoundFileName )
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
end
|
end
|
||||||
@ -29,7 +29,6 @@ ENUMS = {}
|
|||||||
--- Suppress the error box
|
--- Suppress the error box
|
||||||
env.setErrorMessageBoxEnabled( false )
|
env.setErrorMessageBoxEnabled( false )
|
||||||
|
|
||||||
|
|
||||||
--- Rules of Engagement.
|
--- Rules of Engagement.
|
||||||
-- @type ENUMS.ROE
|
-- @type ENUMS.ROE
|
||||||
-- @field #number WeaponFree [AIR] AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target.
|
-- @field #number WeaponFree [AIR] AI will engage any enemy group it detects. Target prioritization is based based on the threat of the target.
|
||||||
@ -567,6 +566,14 @@ ENUMS.ReportingName =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- Enums for Link16 transmit power
|
||||||
|
-- @type ENUMS.Link16Power
|
||||||
|
ENUMS.Link16Power = {
|
||||||
|
none = 0,
|
||||||
|
low = 1,
|
||||||
|
medium = 2,
|
||||||
|
high = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
--- Enums for the STORAGE class for stores - which need to be in ""
|
--- Enums for the STORAGE class for stores - which need to be in ""
|
||||||
|
|||||||
@ -441,21 +441,37 @@ UTILS.BasicSerialize = function(s)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Print a table to log in a nice format
|
||||||
|
-- @param #table table The table to print
|
||||||
|
-- @param #number indent Number of indents
|
||||||
|
-- @return #string text Text created on the fly of the log output
|
||||||
function UTILS.PrintTableToLog(table, indent)
|
function UTILS.PrintTableToLog(table, indent)
|
||||||
|
local text = "\n"
|
||||||
if not table then
|
if not table then
|
||||||
BASE:E("No table passed!")
|
env.warning("No table passed!")
|
||||||
return
|
return nil
|
||||||
end
|
end
|
||||||
if not indent then indent = 0 end
|
if not indent then indent = 0 end
|
||||||
for k, v in pairs(table) do
|
for k, v in pairs(table) do
|
||||||
|
if string.find(k," ") then k='"'..k..'"'end
|
||||||
if type(v) == "table" then
|
if type(v) == "table" then
|
||||||
BASE:I(string.rep(" ", indent) .. tostring(k) .. " = {")
|
env.info(string.rep(" ", indent) .. tostring(k) .. " = {")
|
||||||
UTILS.PrintTableToLog(v, indent + 1)
|
text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n"
|
||||||
BASE:I(string.rep(" ", indent) .. "}")
|
text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n"
|
||||||
|
env.info(string.rep(" ", indent) .. "},")
|
||||||
|
text = text .. string.rep(" ", indent) .. "},\n"
|
||||||
else
|
else
|
||||||
BASE:I(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(v))
|
local value
|
||||||
|
if tostring(v) == "true" or tostring(v) == "false" or tonumber(v) ~= nil then
|
||||||
|
value=v
|
||||||
|
else
|
||||||
|
value = '"'..tostring(v)..'"'
|
||||||
|
end
|
||||||
|
env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n")
|
||||||
|
text = text .. string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return text
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns table in a easy readable string representation.
|
--- Returns table in a easy readable string representation.
|
||||||
@ -3325,7 +3341,7 @@ function UTILS.GetZoneProperties(zone_name)
|
|||||||
for _, property in pairs(zone["properties"]) do
|
for _, property in pairs(zone["properties"]) do
|
||||||
return_table[property["key"]] = property["value"]
|
return_table[property["key"]] = property["value"]
|
||||||
end
|
end
|
||||||
return return_table
|
return return_table
|
||||||
else
|
else
|
||||||
BASE:I(string.format("%s doesn't have any properties", zone_name))
|
BASE:I(string.format("%s doesn't have any properties", zone_name))
|
||||||
return {}
|
return {}
|
||||||
@ -3599,3 +3615,30 @@ function table.find_key_value_pair(tbl, key, value)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Convert a decimal to octal
|
||||||
|
-- @param #number Number the number to convert
|
||||||
|
-- @return #number Octal
|
||||||
|
function UTILS.DecimalToOctal(Number)
|
||||||
|
if Number < 8 then return Number end
|
||||||
|
local number = tonumber(Number)
|
||||||
|
local octal = ""
|
||||||
|
local n=1
|
||||||
|
while number > 7 do
|
||||||
|
local number1 = number%8
|
||||||
|
octal = string.format("%d",number1)..octal
|
||||||
|
local number2 = math.abs(number/8)
|
||||||
|
if number2 < 8 then
|
||||||
|
octal = string.format("%d",number2)..octal
|
||||||
|
end
|
||||||
|
number = number2
|
||||||
|
n=n+1
|
||||||
|
end
|
||||||
|
return tonumber(octal)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert an octal to decimal
|
||||||
|
-- @param #number Number the number to convert
|
||||||
|
-- @return #number Decimal
|
||||||
|
function UTILS.OctalToDecimal(Number)
|
||||||
|
return tonumber(Number,8)
|
||||||
|
end
|
||||||
|
|||||||
@ -239,6 +239,13 @@ AIRBASE.Nevada = {
|
|||||||
-- * AIRBASE.Normandy.Broglie
|
-- * AIRBASE.Normandy.Broglie
|
||||||
-- * AIRBASE.Normandy.Bernay_Saint_Martin
|
-- * AIRBASE.Normandy.Bernay_Saint_Martin
|
||||||
-- * AIRBASE.Normandy.Saint_Andre_de_lEure
|
-- * AIRBASE.Normandy.Saint_Andre_de_lEure
|
||||||
|
-- * AIRBASE.Normandy.Biggin_Hill
|
||||||
|
-- * AIRBASE.Normandy.Manston
|
||||||
|
-- * AIRBASE.Normandy.Detling
|
||||||
|
-- * AIRBASE.Normandy.Lympne
|
||||||
|
-- * AIRBASE.Normandy.Abbeville_Drucat
|
||||||
|
-- * AIRBASE.Normandy.Merville_Calonne
|
||||||
|
-- * AIRBASE.Normandy.Saint_Omer_Wizernes
|
||||||
--
|
--
|
||||||
-- @field Normandy
|
-- @field Normandy
|
||||||
AIRBASE.Normandy = {
|
AIRBASE.Normandy = {
|
||||||
@ -312,6 +319,13 @@ AIRBASE.Normandy = {
|
|||||||
["Broglie"] = "Broglie",
|
["Broglie"] = "Broglie",
|
||||||
["Bernay_Saint_Martin"] = "Bernay Saint Martin",
|
["Bernay_Saint_Martin"] = "Bernay Saint Martin",
|
||||||
["Saint_Andre_de_lEure"] = "Saint-Andre-de-lEure",
|
["Saint_Andre_de_lEure"] = "Saint-Andre-de-lEure",
|
||||||
|
["Biggin_Hill"] = "Biggin Hill",
|
||||||
|
["Manston"] = "Manston",
|
||||||
|
["Detling"] = "Detling",
|
||||||
|
["Lympne"] = "Lympne",
|
||||||
|
["Abbeville_Drucat"] = "Abbeville Drucat",
|
||||||
|
["Merville_Calonne"] = "Merville Calonne",
|
||||||
|
["Saint_Omer_Wizernes"] = "Saint-Omer Wizernes",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Airbases of the Persion Gulf Map:
|
--- Airbases of the Persion Gulf Map:
|
||||||
|
|||||||
@ -3974,7 +3974,7 @@ function CONTROLLABLE:OptionAAAttackRange( range )
|
|||||||
local Controller = self:_GetController()
|
local Controller = self:_GetController()
|
||||||
if Controller then
|
if Controller then
|
||||||
if self:IsAir() then
|
if self:IsAir() then
|
||||||
self:SetOption( AI.Option.Air.val.MISSILE_ATTACK, range )
|
self:SetOption( AI.Option.Air.id.MISSILE_ATTACK, range )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
|
|||||||
@ -1157,6 +1157,7 @@ function GROUP:GetAverageCoordinate()
|
|||||||
local coord = COORDINATE:NewFromVec3(vec3)
|
local coord = COORDINATE:NewFromVec3(vec3)
|
||||||
local Heading = self:GetHeading()
|
local Heading = self:GetHeading()
|
||||||
coord.Heading = Heading
|
coord.Heading = Heading
|
||||||
|
return coord
|
||||||
else
|
else
|
||||||
BASE:E( { "Cannot GetAverageCoordinate", Group = self, Alive = self:IsAlive() } )
|
BASE:E( { "Cannot GetAverageCoordinate", Group = self, Alive = self:IsAlive() } )
|
||||||
return nil
|
return nil
|
||||||
@ -2922,7 +2923,7 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
|||||||
return callsign
|
return callsign
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
--- Set a GROUP to act as recovery tanker
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @param Wrapper.Group#GROUP CarrierGroup.
|
-- @param Wrapper.Group#GROUP CarrierGroup.
|
||||||
-- @param #number Speed Speed in knots.
|
-- @param #number Speed Speed in knots.
|
||||||
@ -2948,3 +2949,37 @@ function GROUP:SetAsRecoveryTanker(CarrierGroup,Speed,ToKIAS,Altitude,Delay,Last
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get a list of Link16 S/TN data from a GROUP. Can (as of Nov 2023) be obtained from F-18, F-16, F-15E (not the user flyable one) and A-10C-II groups.
|
||||||
|
-- @param #GROUP self
|
||||||
|
-- @return #table Table of data entries, indexed by unit name, each entry is a table containing STN, VCL (voice call label), VCN (voice call number), and Lead (#boolean, if true it's the flight lead)
|
||||||
|
-- @return #string Report Formatted report of all data
|
||||||
|
function GROUP:GetGroupSTN()
|
||||||
|
local tSTN = {} -- table
|
||||||
|
local units = self:GetUnits()
|
||||||
|
local gname = self:GetName()
|
||||||
|
gname = string.gsub(gname,"(#%d+)$","")
|
||||||
|
local report = REPORT:New()
|
||||||
|
report:Add("Link16 S/TN Report")
|
||||||
|
report:Add("Group: "..gname)
|
||||||
|
report:Add("==================")
|
||||||
|
for _,_unit in pairs(units) do
|
||||||
|
local unit = _unit -- Wrapper.Unit#UNIT
|
||||||
|
if unit and unit:IsAlive() then
|
||||||
|
local STN, VCL, VCN, Lead = unit:GetSTN()
|
||||||
|
local name = unit:GetName()
|
||||||
|
tSTN[name] = {
|
||||||
|
STN=STN,
|
||||||
|
VCL=VCL,
|
||||||
|
VCN=VCN,
|
||||||
|
Lead=Lead,
|
||||||
|
}
|
||||||
|
local lead = Lead == true and "(*)" or ""
|
||||||
|
report:Add(string.format("| %s%s %s %s",tostring(VCL),tostring(VCN),tostring(STN),lead))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
report:Add("==================")
|
||||||
|
local text = report:Text()
|
||||||
|
return tSTN,text
|
||||||
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1659,3 +1659,36 @@ function UNIT:GetSkill()
|
|||||||
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
local skill = _DATABASE.Templates.Units[name].Template.skill or "Random"
|
||||||
return skill
|
return skill
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get Link16 STN or SADL TN and other datalink info from Unit, if any.
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @return #string STN STN or TN Octal as string, or nil if not set/capable.
|
||||||
|
-- @return #string VCL Voice Callsign Label or nil if not set/capable.
|
||||||
|
-- @return #string VCN Voice Callsign Number or nil if not set/capable.
|
||||||
|
-- @return #string Lead If true, unit is Flight Lead, else false or nil.
|
||||||
|
function UNIT:GetSTN()
|
||||||
|
self:F2(self.UnitName)
|
||||||
|
local STN = nil -- STN/TN
|
||||||
|
local VCL = nil -- VoiceCallsignLabel
|
||||||
|
local VCN = nil -- VoiceCallsignNumber
|
||||||
|
local FGL = false -- FlightGroupLeader
|
||||||
|
local template = self:GetTemplate()
|
||||||
|
if template.AddPropAircraft then
|
||||||
|
if template.AddPropAircraft.STN_L16 then
|
||||||
|
STN = template.AddPropAircraft.STN_L16
|
||||||
|
elseif template.AddPropAircraft.SADL_TN then
|
||||||
|
STN = template.AddPropAircraft.SADL_TN
|
||||||
|
end
|
||||||
|
VCN = template.AddPropAircraft.VoiceCallsignNumber
|
||||||
|
VCL = template.AddPropAircraft.VoiceCallsignLabel
|
||||||
|
end
|
||||||
|
if template.datalinks and template.datalinks.Link16 and template.datalinks.Link16.settings then
|
||||||
|
FGL = template.datalinks.Link16.settings.flightLead
|
||||||
|
end
|
||||||
|
-- A10CII
|
||||||
|
if template.datalinks and template.datalinks.SADL and template.datalinks.SADL.settings then
|
||||||
|
FGL = template.datalinks.SADL.settings.flightLead
|
||||||
|
end
|
||||||
|
|
||||||
|
return STN, VCL, VCN, FGL
|
||||||
|
end
|
||||||
|
|||||||
13
docs/beginner/demo-missions.md
Normal file
13
docs/beginner/demo-missions.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
parent: Beginner
|
||||||
|
nav_order: 04
|
||||||
|
---
|
||||||
|
|
||||||
|
# Demo missions
|
||||||
|
{: .no_toc }
|
||||||
|
|
||||||
|
1. Table of contents
|
||||||
|
{:toc}
|
||||||
|
|
||||||
|
{: .warning }
|
||||||
|
> THIS DOCUMENT IS STILL WORK IN PROGRESS!
|
||||||
@ -4,6 +4,171 @@ nav_order: 03
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Create your own Hello world
|
# Create your own Hello world
|
||||||
|
{: .no_toc }
|
||||||
|
|
||||||
{: .warning }
|
1. Table of contents
|
||||||
> THIS DOCUMENT IS STILL WORK IN PROGRESS!
|
{:toc}
|
||||||
|
|
||||||
|
This page will lead you step by step through the process of creating a mission
|
||||||
|
with MOOSE. This time we include a simple mission script, which sends only
|
||||||
|
a "Hello world" message to all players. But the steps are the same to add
|
||||||
|
another mission script, which will do whatever class(es) you want to use.
|
||||||
|
|
||||||
|
## Create Mission script
|
||||||
|
|
||||||
|
At first we will create the mission script. It is a simple text file and can be
|
||||||
|
changed with a lot of different tools. Theoretically even the Microsoft Notepad
|
||||||
|
editor can be used. But it lacks a lot of features, which helps you to avoid
|
||||||
|
errors.
|
||||||
|
|
||||||
|
For this guide we suggest you to download, install and use [Notepad++]{:target="_blank"}.
|
||||||
|
|
||||||
|
{: .important }
|
||||||
|
> Windows hides filename extensions by default. So when you create a text file
|
||||||
|
> and name it `hello-world.lua` it's name is `hello-world.lua.txt` in reality.
|
||||||
|
> You must activate the display of the file name extension.
|
||||||
|
> Open a `File Explorer`, switch to menu `View` and find the option
|
||||||
|
> `File name extensions` in the section `Show/hide`. Activate it.
|
||||||
|
|
||||||
|
- Open a File Explorer.
|
||||||
|
- Go to the subfolder `Missions` of your [Saved Games folder]{:target="_blank"}.
|
||||||
|
- Create a new text file and name it `hello-world.lua`.
|
||||||
|
- Add the following content and save the file:
|
||||||
|
|
||||||
|
`MESSAGE:New( "Hello World! This messages is printed by MOOSE", 35, "INFO" ):ToAll()`
|
||||||
|
|
||||||
|
## Get Moose
|
||||||
|
|
||||||
|
To download Moose click on the following link:
|
||||||
|
|
||||||
|
- [Moose_.lua from develop branch]{:target="_blank"}
|
||||||
|
|
||||||
|
Press `Ctrl + S` to save the file on your hard disk next to your mission script.
|
||||||
|
|
||||||
|
## Create the mission
|
||||||
|
|
||||||
|
- Start DCS.
|
||||||
|
- In the main menu choose `MISSION EDITOR`.
|
||||||
|
- Click on `create new mission`.
|
||||||
|
- In the dialog `NEW MISSION SETTINGS`:
|
||||||
|
- Choose map `Caucasus`.
|
||||||
|
- In the drop box upper left choose `Modern` as coalition preset.
|
||||||
|
- Click on `OK`.
|
||||||
|
- The mission editor will load with a fresh new and empty mission.
|
||||||
|
- Click on `File` in the menu bar and `SAVE` or Press `Ctrl + S`.
|
||||||
|
- Open `My Missions` and save the file with the name `hello-world.miz`.
|
||||||
|
|
||||||
|
## Add Moose to the mission
|
||||||
|
|
||||||
|
- On the left side activate `TRIGGERS`:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- On the right side the `TRIGGERS` dialog opens with a lot of options.
|
||||||
|
- Click on `NEW`, choose `4 MISSION START` as **TYPE**.
|
||||||
|
- Give it the `Load MOOSE` as **NAME**.
|
||||||
|
- Leave the **EVENT** option set to `NO EVENT`.
|
||||||
|
- Optional: Choose a color for easy recognition (e.g. yellow).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- In the middle part the `CONDITIONS` will be shown.
|
||||||
|
For this trigger we do not configure any conditions.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
{: .important }
|
||||||
|
> The trigger type `4 MISSION START` does not support `CONDITIONS`. <br />
|
||||||
|
> So `CONDITIONS` must left blank when using it. <br />
|
||||||
|
> **If you add a condition the trigger will never be executed!**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- On the right side `ACTIONS` is shown.
|
||||||
|
- We need to click on `NEW`.
|
||||||
|
- Choose **ACTION** `Do SCRIPT FILE` and ignore all other actions.
|
||||||
|
- Click **OPEN** and navigate to the downloaded `Moose_.lua` file.
|
||||||
|
- The result should look like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Add the mission script
|
||||||
|
|
||||||
|
- Click on `NEW`, choose `1 ONCE` as **TYPE**.
|
||||||
|
- Give it the `Load Mission Script` as **NAME**.
|
||||||
|
- Leave the **EVENT** option set to `NO EVENT`.
|
||||||
|
- Optional: Choose a color for easy recognition (e.g. green).
|
||||||
|
- The result should look like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Switch to the middle part, the `CONDITIONS` section. <br />
|
||||||
|
For this trigger we add one condition:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- The combination of `1 ONCE` with `TIME MORE(1)` will ensure, that the mission
|
||||||
|
script is executed 1 second after the mission is started.
|
||||||
|
|
||||||
|
- On the right side under `ACTIONS` you need to add the script:
|
||||||
|
- Click on `NEW`.
|
||||||
|
- Choose **ACTION** `Do SCRIPT FILE`.
|
||||||
|
- Click **OPEN** and navigate to the created `hello-world.lua` file.
|
||||||
|
|
||||||
|
{: .important }
|
||||||
|
> Most important is the fact, that the mission script (`hello-world.lua`)
|
||||||
|
> is executed **after** `Moose_.lua`, because the mission script needs the
|
||||||
|
> classes defined in `Moose_.lua`. And they are only available when `Moose_.lua`
|
||||||
|
> is executed before the mission script.
|
||||||
|
|
||||||
|
## Test the mission
|
||||||
|
|
||||||
|
- Save the mission again.
|
||||||
|
- Click on the green **Fly mission** cirlce on the left tool side bar.
|
||||||
|
- It is an empty mission, so skip `BRIEFING` with `START` and then `FLY`.
|
||||||
|
- You spawn as a spectator. After some seconds you will see this message in
|
||||||
|
the upper right corner:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This is the same result as already seen in the last chapter, but this time you
|
||||||
|
have create everything on your own.
|
||||||
|
|
||||||
|
{: .note }
|
||||||
|
> You can use this mission as a template for your own missions. So you don't
|
||||||
|
> need to do alle these steps again and again.
|
||||||
|
|
||||||
|
## Update mission script
|
||||||
|
|
||||||
|
- Open the `hello-world.lua` with Notepad++ again.
|
||||||
|
- Change the text a little bit, like `Hello Dude! ...` and save the file.
|
||||||
|
- Run the mission again.
|
||||||
|
- The text will not be changed in the mission. Why?
|
||||||
|
The mission editor copies the script into the mission file when you add it.
|
||||||
|
Ever change on the script file on your hard disk is not recognized by mission editor.
|
||||||
|
You have to add the file after each change again.
|
||||||
|
|
||||||
|
- On the left side of the `TRIGGERS` dialog click on `Load Mission Script`.
|
||||||
|
- On the right side under `ACTIONS` you need to add the script again:
|
||||||
|
- Click **OPEN** and navigate to the created `hello-world.lua` file.
|
||||||
|
- Save the mission and test it again.
|
||||||
|
- Now the new text should be shown.
|
||||||
|
|
||||||
|
## Update Moose
|
||||||
|
|
||||||
|
Moose is constantly being developed so that new functionallity is added or
|
||||||
|
existing errors are corrected. Also from time to time changes of the DCS
|
||||||
|
scripting engine comes with a new DCS version. It may therefore be useful or
|
||||||
|
necessary to update Moose.
|
||||||
|
|
||||||
|
- To update Moose download it again and add it again in the same way you did
|
||||||
|
with the mission script in the last step.
|
||||||
|
|
||||||
|
## Next step
|
||||||
|
|
||||||
|
Let's move on to the [demo missions].
|
||||||
|
|
||||||
|
[Notepad++]: https://notepad-plus-plus.org/downloads/
|
||||||
|
[Saved Games folder]: tipps-and-tricks.md#find-the-saved-games-folder
|
||||||
|
[Moose_.lua from develop branch]: https://raw.githubusercontent.com/FlightControl-Master/MOOSE_INCLUDE/develop/Moose_Include_Static/Moose_.lua
|
||||||
|
[demo missions]: demo-missions.md
|
||||||
|
|||||||
BIN
docs/images/beginner/dcs-triggers-once-conditions-conf.png
Normal file
BIN
docs/images/beginner/dcs-triggers-once-conditions-conf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Loading…
x
Reference in New Issue
Block a user