mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'develop' into FF/Ops
This commit is contained in:
commit
cfca4fdc46
@ -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()
|
||||||
|
|||||||
@ -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)
|
||||||
@ -920,7 +904,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 +917,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 )
|
||||||
@ -3021,6 +3007,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.
|
||||||
|
|||||||
@ -1065,6 +1065,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 +1983,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)
|
||||||
|
|||||||
@ -3324,7 +3324,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
|||||||
-- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1)
|
-- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].callsign,1)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Ruskis
|
-- Russkis
|
||||||
for UnitID = 1, #SpawnTemplate.units do
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
SpawnTemplate.units[UnitID].callsign = math.random(1,999)
|
SpawnTemplate.units[UnitID].callsign = math.random(1,999)
|
||||||
end
|
end
|
||||||
@ -3402,8 +3402,25 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
|
|||||||
-- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1)
|
-- UTILS.PrintTableToLog(SpawnTemplate.units[UnitID].datalinks,1)
|
||||||
end
|
end
|
||||||
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
|
||||||
|
|
||||||
self:T3( { "Template:", SpawnTemplate } )
|
self:T3( { "Template:", SpawnTemplate } )
|
||||||
|
--UTILS.PrintTableToLog(SpawnTemplate,1)
|
||||||
return SpawnTemplate
|
return SpawnTemplate
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@ -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 isdefined 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
|
||||||
|
if type(self.DrawID) ~= "table" then
|
||||||
UTILS.RemoveMark(self.DrawID)
|
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,323 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer )
|
|||||||
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
|
||||||
|
|
||||||
|
|
||||||
|
--- 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 #BASE
|
||||||
|
_ZONE_TRIANGLE = {
|
||||||
|
ClassName="ZONE_TRIANGLE",
|
||||||
|
Points={},
|
||||||
|
Coords={},
|
||||||
|
CenterVec2={x=0, y=0},
|
||||||
|
SurfaceArea=0,
|
||||||
|
DrawIDs={}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 #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 #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
|
||||||
|
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 +2345,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
|
||||||
@ -2055,9 +2382,101 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray )
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
--- Triangulates the polygon.
|
||||||
|
--- ported from https://github.com/nielsvaes/CCMOOSE/blob/master/Moose%20Development/Moose/Shapes/Polygon.lua
|
||||||
|
-- @return #table The #_TRIANGLE list that make up
|
||||||
|
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 +2491,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 +2512,24 @@ 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
|
||||||
|
-- @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 +2671,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
|
||||||
if self._.Polygon and #self._.Polygon>=3 then
|
Coalition = Coalition or self:GetDrawCoalition()
|
||||||
|
|
||||||
local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1])
|
|
||||||
|
|
||||||
Coalition=Coalition or self:GetDrawCoalition()
|
|
||||||
|
|
||||||
-- Set draw coalition.
|
-- Set draw coalition.
|
||||||
self:SetDrawCoalition(Coalition)
|
self:SetDrawCoalition(Coalition)
|
||||||
|
|
||||||
Color=Color or self:GetColorRGB()
|
Color = Color or self:GetColorRGB()
|
||||||
Alpha=Alpha or 1
|
Alpha = Alpha or 1
|
||||||
|
|
||||||
-- Set color.
|
-- Set color.
|
||||||
self:SetColor(Color, Alpha)
|
self:SetColor(Color, Alpha)
|
||||||
|
|
||||||
FillColor=FillColor or self:GetFillColorRGB()
|
FillColor = FillColor or self:GetFillColorRGB()
|
||||||
if not FillColor then UTILS.DeepCopy(Color) end
|
if not FillColor then
|
||||||
FillAlpha=FillAlpha or self:GetFillColorAlpha()
|
UTILS.DeepCopy(Color)
|
||||||
if not FillAlpha then FillAlpha=0.15 end
|
end
|
||||||
|
FillAlpha = FillAlpha or self:GetFillColorAlpha()
|
||||||
|
if not FillAlpha then
|
||||||
|
FillAlpha = 0.15
|
||||||
|
end
|
||||||
|
|
||||||
-- Set fill color.
|
-- 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:SetFillColor(FillColor, FillAlpha)
|
||||||
|
|
||||||
if #self._.Polygon==4 then
|
IncludeTriangles = IncludeTriangles or false
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
-- 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
|
else
|
||||||
|
local coords = self:GetVerticiesCoordinates()
|
||||||
local Coordinates=self:GetVerticiesCoordinates()
|
for i = 1, #coords do
|
||||||
table.remove(Coordinates, 1)
|
local c1 = coords[i]
|
||||||
|
local c2 = coords[i % #coords + 1]
|
||||||
self.DrawID=coordinate:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly)
|
table.add(self.DrawID, c1:LineToAll(c2, Coalition, Color, Alpha, LineType, ReadOnly))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
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 +2940,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()
|
||||||
|
local accumulated_weight = 0
|
||||||
|
for triangle, weight in pairs(weights) do
|
||||||
|
accumulated_weight = accumulated_weight + weight
|
||||||
|
if accumulated_weight >= random_weight then
|
||||||
|
return triangle:GetRandomVec2()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:E("Could not find a random point in the polygon zone!")
|
|
||||||
return nil
|
|
||||||
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.
|
||||||
@ -2649,7 +3095,8 @@ end
|
|||||||
-- @extends #ZONE_POLYGON_BASE
|
-- @extends #ZONE_POLYGON_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 +3119,13 @@ 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 +3186,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
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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.
|
||||||
|
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 ) )
|
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 ) )
|
||||||
|
|||||||
@ -2811,7 +2811,11 @@ function FLIGHTCONTROL:_PlayerInfoATIS(groupname)
|
|||||||
|
|
||||||
-- Radio message.
|
-- Radio message.
|
||||||
self:TransmissionPilot(rtext, flight)
|
self:TransmissionPilot(rtext, flight)
|
||||||
|
if self.atis then
|
||||||
self:TransmissionTower(srstxt,flight,10)
|
self:TransmissionTower(srstxt,flight,10)
|
||||||
|
else
|
||||||
|
self:TransmissionTower(text,flight,10)
|
||||||
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E(self.lid..string.format("Cannot find flight group %s.", tostring(groupname)))
|
self:E(self.lid..string.format("Cannot find flight group %s.", tostring(groupname)))
|
||||||
|
|||||||
@ -2863,9 +2863,12 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime)
|
|||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
-- Check if not parking (could be on ALERT5 and just spawned (current mission=nil)
|
||||||
|
if not self:IsParking() then
|
||||||
self:T(self.lid..string.format("Passed Final WP but Tasks=%d or Missions=%d left in the queue. Wait!", nTasks, nMissions))
|
self:T(self.lid..string.format("Passed Final WP but Tasks=%d or Missions=%d left in the queue. Wait!", nTasks, nMissions))
|
||||||
self:__Wait(-1)
|
self:__Wait(-1)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self:T(self.lid..string.format("Passed Final WP but still have current Task (#%s) or Mission (#%s) left to do", tostring(self.taskcurrent), tostring(self.currentmission)))
|
self:T(self.lid..string.format("Passed Final WP but still have current Task (#%s) or Mission (#%s) left to do", tostring(self.taskcurrent), tostring(self.currentmission)))
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1483,11 +1483,11 @@ function OPSZONE:_GetZoneColor()
|
|||||||
local color={0,0,0}
|
local color={0,0,0}
|
||||||
|
|
||||||
if self.ownerCurrent==coalition.side.NEUTRAL then
|
if self.ownerCurrent==coalition.side.NEUTRAL then
|
||||||
color={1, 1, 1}
|
color=self.ZoneOwnerNeutral or {1, 1, 1}
|
||||||
elseif self.ownerCurrent==coalition.side.BLUE then
|
elseif self.ownerCurrent==coalition.side.BLUE then
|
||||||
color={0, 0, 1}
|
color=self.ZoneOwnerBlue or {0, 0, 1}
|
||||||
elseif self.ownerCurrent==coalition.side.RED then
|
elseif self.ownerCurrent==coalition.side.RED then
|
||||||
color={1, 0, 0}
|
color=self.ZoneOwnerRed or {1, 0, 0}
|
||||||
else
|
else
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -1495,6 +1495,21 @@ function OPSZONE:_GetZoneColor()
|
|||||||
return color
|
return color
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set custom RGB color of zone depending on current owner.
|
||||||
|
-- @param #OPSZONE self
|
||||||
|
-- @param #table Neutral Color is a table of RGB values 0..1 for Red, Green, and Blue respectively, e.g. {1,0,0} for red.
|
||||||
|
-- @param #table Blue Color is a table of RGB values 0..1 for Red, Green, and Blue respectively, e.g. {0,1,0} for green.
|
||||||
|
-- @param #table Red Color is a table of RGB values 0..1 for Red, Green, and Blue respectively, e.g. {0,0,1} for blue.
|
||||||
|
-- @return #OPSZONE self
|
||||||
|
function OPSZONE:SetZoneColor(Neutral, Blue, Red)
|
||||||
|
|
||||||
|
self.ZoneOwnerNeutral = Neutral or {1, 1, 1}
|
||||||
|
self.ZoneOwnerBlue = Blue or {0, 0, 1}
|
||||||
|
self.ZoneOwnerRed = Red or {1, 0, 0}
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Update marker on the F10 map.
|
--- Update marker on the F10 map.
|
||||||
-- @param #OPSZONE self
|
-- @param #OPSZONE self
|
||||||
function OPSZONE:_UpdateMarker()
|
function OPSZONE:_UpdateMarker()
|
||||||
|
|||||||
@ -104,7 +104,7 @@ PLAYERRECCE = {
|
|||||||
ClassName = "PLAYERRECCE",
|
ClassName = "PLAYERRECCE",
|
||||||
verbose = true,
|
verbose = true,
|
||||||
lid = nil,
|
lid = nil,
|
||||||
version = "0.0.21",
|
version = "0.0.22",
|
||||||
ViewZone = {},
|
ViewZone = {},
|
||||||
ViewZoneVisual = {},
|
ViewZoneVisual = {},
|
||||||
ViewZoneLaser = {},
|
ViewZoneLaser = {},
|
||||||
@ -469,8 +469,10 @@ function PLAYERRECCE:_GetClockDirection(unit, target)
|
|||||||
local _playerPosition = unit:GetCoordinate() -- get position of helicopter
|
local _playerPosition = unit:GetCoordinate() -- get position of helicopter
|
||||||
local _targetpostions = target:GetCoordinate() -- get position of downed pilot
|
local _targetpostions = target:GetCoordinate() -- get position of downed pilot
|
||||||
local _heading = unit:GetHeading() -- heading
|
local _heading = unit:GetHeading() -- heading
|
||||||
|
--self:I("Heading = ".._heading)
|
||||||
local DirectionVec3 = _playerPosition:GetDirectionVec3( _targetpostions )
|
local DirectionVec3 = _playerPosition:GetDirectionVec3( _targetpostions )
|
||||||
local Angle = _playerPosition:GetAngleDegrees( DirectionVec3 )
|
local Angle = _playerPosition:GetAngleDegrees( DirectionVec3 )
|
||||||
|
--self:I("Angle = "..Angle)
|
||||||
local clock = 12
|
local clock = 12
|
||||||
local hours = 0
|
local hours = 0
|
||||||
if _heading and Angle then
|
if _heading and Angle then
|
||||||
@ -478,10 +480,13 @@ function PLAYERRECCE:_GetClockDirection(unit, target)
|
|||||||
--if angle == 0 then angle = 360 end
|
--if angle == 0 then angle = 360 end
|
||||||
clock = _heading-Angle
|
clock = _heading-Angle
|
||||||
hours = (clock/30)*-1
|
hours = (clock/30)*-1
|
||||||
|
--self:I("hours = "..hours)
|
||||||
clock = 12+hours
|
clock = 12+hours
|
||||||
clock = UTILS.Round(clock,0)
|
clock = UTILS.Round(clock,0)
|
||||||
if clock > 12 then clock = clock-12 end
|
if clock > 12 then clock = clock-12 end
|
||||||
|
if clock == 0 then clock = 12 end
|
||||||
end
|
end
|
||||||
|
--self:I("Clock ="..clock)
|
||||||
return clock
|
return clock
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -709,8 +714,8 @@ function PLAYERRECCE:_GetViewZone(unit, vheading, minview, maxview, angle, camon
|
|||||||
local heading2 = (vheading-90)%360
|
local heading2 = (vheading-90)%360
|
||||||
self:T({heading1,heading2})
|
self:T({heading1,heading2})
|
||||||
local startpos = startp:Translate(minview,vheading)
|
local startpos = startp:Translate(minview,vheading)
|
||||||
local pos1 = startpos:Translate(10,heading1)
|
local pos1 = startpos:Translate(12.5,heading1)
|
||||||
local pos2 = startpos:Translate(10,heading2)
|
local pos2 = startpos:Translate(12.5,heading2)
|
||||||
local pos3 = pos1:Translate(maxview,vheading)
|
local pos3 = pos1:Translate(maxview,vheading)
|
||||||
local pos4 = pos2:Translate(maxview,vheading)
|
local pos4 = pos2:Translate(maxview,vheading)
|
||||||
local array = {}
|
local array = {}
|
||||||
@ -912,32 +917,41 @@ function PLAYERRECCE:_LaseTarget(client,targetset)
|
|||||||
else
|
else
|
||||||
laser = self.LaserSpots[playername]
|
laser = self.LaserSpots[playername]
|
||||||
end
|
end
|
||||||
|
-- old target
|
||||||
if self.LaserTarget[playername] then
|
if self.LaserTarget[playername] then
|
||||||
-- still looking at target?
|
-- still looking at target?
|
||||||
local target=self.LaserTarget[playername] -- Ops.Target#TARGET
|
local target=self.LaserTarget[playername] -- Ops.Target#TARGET
|
||||||
local oldtarget = target:GetObject() --or laser.Target
|
local oldtarget = target:GetObject() --or laser.Target
|
||||||
--self:I("Targetstate: "..target:GetState())
|
self:T("Targetstate: "..target:GetState())
|
||||||
--self:I("Laser State: "..tostring(laser:IsLasing()))
|
self:T("Laser State: "..tostring(laser:IsLasing()))
|
||||||
if not oldtarget or targetset:IsNotInSet(oldtarget) or target:IsDead() or target:IsDestroyed() then
|
if (not oldtarget) or targetset:IsNotInSet(oldtarget) or target:IsDead() or target:IsDestroyed() then
|
||||||
-- lost LOS or dead
|
-- lost LOS or dead
|
||||||
laser:LaseOff()
|
laser:LaseOff()
|
||||||
if target:IsDead() or target:IsDestroyed() or target:GetLife() < 2 then
|
if target:IsDead() or target:IsDestroyed() or target:GetLife() < 2 then
|
||||||
self:__Shack(-1,client,oldtarget)
|
self:__Shack(-1,client,oldtarget)
|
||||||
self.LaserTarget[playername] = nil
|
--self.LaserTarget[playername] = nil
|
||||||
else
|
else
|
||||||
self:__TargetLOSLost(-1,client,oldtarget)
|
self:__TargetLOSLost(-1,client,oldtarget)
|
||||||
|
--self.LaserTarget[playername] = nil
|
||||||
|
end
|
||||||
self.LaserTarget[playername] = nil
|
self.LaserTarget[playername] = nil
|
||||||
end
|
oldtarget = nil
|
||||||
end
|
self.LaserSpots[playername] = nil
|
||||||
if oldtarget and (not laser:IsLasing()) then
|
elseif oldtarget and laser and (not laser:IsLasing()) then
|
||||||
--self:I("Switching laser back on ..")
|
--laser:LaseOff()
|
||||||
|
self:T("Switching laser back on ..")
|
||||||
local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688
|
local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688
|
||||||
local lasingtime = self.lasingtime or 60
|
local lasingtime = self.lasingtime or 60
|
||||||
--local targettype = target:GetTypeName()
|
--local targettype = target:GetTypeName()
|
||||||
laser:LaseOn(oldtarget,lasercode,lasingtime)
|
laser:LaseOn(oldtarget,lasercode,lasingtime)
|
||||||
--self:__TargetLasing(-1,client,oldtarget,lasercode,lasingtime)
|
--self:__TargetLasing(-1,client,oldtarget,lasercode,lasingtime)
|
||||||
|
else
|
||||||
|
-- we should not be here...
|
||||||
|
self:T("Target alive and laser is on!")
|
||||||
|
--self.LaserSpots[playername] = nil
|
||||||
end
|
end
|
||||||
elseif not laser:IsLasing() and target then
|
-- new target
|
||||||
|
elseif (not laser:IsLasing()) and target then
|
||||||
local relativecam = self.LaserRelativePos[client:GetTypeName()]
|
local relativecam = self.LaserRelativePos[client:GetTypeName()]
|
||||||
laser:SetRelativeStartPosition(relativecam)
|
laser:SetRelativeStartPosition(relativecam)
|
||||||
local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688
|
local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688
|
||||||
@ -945,7 +959,7 @@ function PLAYERRECCE:_LaseTarget(client,targetset)
|
|||||||
--local targettype = target:GetTypeName()
|
--local targettype = target:GetTypeName()
|
||||||
laser:LaseOn(target,lasercode,lasingtime)
|
laser:LaseOn(target,lasercode,lasingtime)
|
||||||
self.LaserTarget[playername] = TARGET:New(target)
|
self.LaserTarget[playername] = TARGET:New(target)
|
||||||
self.LaserTarget[playername].TStatus = 9
|
--self.LaserTarget[playername].TStatus = 9
|
||||||
self:__TargetLasing(-1,client,target,lasercode,lasingtime)
|
self:__TargetLasing(-1,client,target,lasercode,lasingtime)
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
@ -1027,6 +1041,13 @@ function PLAYERRECCE:_SwitchLasing(client,group,playername)
|
|||||||
MESSAGE:New("Lasing is now ON",10,self.Name or "FACA"):ToClient(client)
|
MESSAGE:New("Lasing is now ON",10,self.Name or "FACA"):ToClient(client)
|
||||||
else
|
else
|
||||||
self.AutoLase[playername] = false
|
self.AutoLase[playername] = false
|
||||||
|
if self.LaserSpots[playername] then
|
||||||
|
local laser = self.LaserSpots[playername] -- Core.Spot#SPOT
|
||||||
|
if laser:IsLasing() then
|
||||||
|
laser:LaseOff()
|
||||||
|
end
|
||||||
|
self.LaserSpots[playername] = nil
|
||||||
|
end
|
||||||
MESSAGE:New("Lasing is now OFF",10,self.Name or "FACA"):ToClient(client)
|
MESSAGE:New("Lasing is now OFF",10,self.Name or "FACA"):ToClient(client)
|
||||||
end
|
end
|
||||||
if self.ClientMenus[playername] then
|
if self.ClientMenus[playername] then
|
||||||
@ -1681,7 +1702,7 @@ function PLAYERRECCE:onafterRecceOnStation(From, Event, To, Client, Playername)
|
|||||||
local text2tts = string.format("All stations, FACA %s on station at %s!",callsign, coordtext)
|
local text2tts = string.format("All stations, FACA %s on station at %s!",callsign, coordtext)
|
||||||
text2tts = self:_GetTextForSpeech(text2tts)
|
text2tts = self:_GetTextForSpeech(text2tts)
|
||||||
if self.debug then
|
if self.debug then
|
||||||
self:I(text2.."\n"..text2tts)
|
self:T(text2.."\n"..text2tts)
|
||||||
end
|
end
|
||||||
if self.UseSRS then
|
if self.UseSRS then
|
||||||
local grp = Client:GetGroup()
|
local grp = Client:GetGroup()
|
||||||
@ -1720,7 +1741,7 @@ function PLAYERRECCE:onafterRecceOffStation(From, Event, To, Client, Playername)
|
|||||||
local texttts = string.format("All stations, FACA %s leaving station at %s, good bye!",callsign, coordtext)
|
local texttts = string.format("All stations, FACA %s leaving station at %s, good bye!",callsign, coordtext)
|
||||||
texttts = self:_GetTextForSpeech(texttts)
|
texttts = self:_GetTextForSpeech(texttts)
|
||||||
if self.debug then
|
if self.debug then
|
||||||
self:I(text.."\n"..texttts)
|
self:T(text.."\n"..texttts)
|
||||||
end
|
end
|
||||||
local text1 = "Going home!"
|
local text1 = "Going home!"
|
||||||
if self.UseSRS then
|
if self.UseSRS then
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -2924,7 +2924,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.
|
||||||
@ -2950,3 +2950,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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user