diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 1b2603f00..864b56bd1 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1,11 +1,11 @@ --- **Core** - Defines an extensive API to manage 3D points in the DCS World 3D simulation space. -- -- ## Features: --- +-- -- * Provides a COORDINATE class, which allows to manage points in 3D space and perform various operations on it. -- * Provides a POINT\_VEC2 class, which is derived from COORDINATE, and allows to manage points in 3D space, but from a Lat/Lon and Altitude perspective. -- * Provides a POINT\_VEC3 class, which is derived from COORDINATE, and allows to manage points in 3D space, but from a X, Z and Y vector perspective. --- +-- -- === -- -- # Demo Missions @@ -40,8 +40,8 @@ do -- COORDINATE --- @type COORDINATE -- @extends Core.Base#BASE - - + + --- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. -- -- # 1) Create a COORDINATE object. @@ -84,17 +84,17 @@ do -- COORDINATE -- -- -- # 3) Create markings on the map. - -- - -- Place markers (text boxes with clarifications for briefings, target locations or any other reference point) + -- + -- Place markers (text boxes with clarifications for briefings, target locations or any other reference point) -- on the map for all players, coalitions or specific groups: - -- + -- -- * @{#COORDINATE.MarkToAll}(): Place a mark to all players. -- * @{#COORDINATE.MarkToCoalition}(): Place a mark to a coalition. -- * @{#COORDINATE.MarkToCoalitionRed}(): Place a mark to the red coalition. -- * @{#COORDINATE.MarkToCoalitionBlue}(): Place a mark to the blue coalition. -- * @{#COORDINATE.MarkToGroup}(): Place a mark to a group (needs to have a client in it or a CA group (CA group is bugged)). -- * @{#COORDINATE.RemoveMark}(): Removes a mark from the map. - -- + -- -- # 4) Coordinate calculation methods. -- -- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method: @@ -124,37 +124,37 @@ do -- COORDINATE -- -- * @{#COORDINATE.GetRandomVec2InRadius}(): Provides a random 2D vector around the current 3D point, in the given inner to outer band. -- * @{#COORDINATE.GetRandomVec3InRadius}(): Provides a random 3D vector around the current 3D point, in the given inner to outer band. - -- + -- -- ## 4.6) LOS between coordinates. - -- + -- -- Calculate if the coordinate has Line of Sight (LOS) with the other given coordinate. -- Mountains, trees and other objects can be positioned between the two 3D points, preventing visibilty in a straight continuous line. -- The method @{#COORDINATE.IsLOS}() returns if the two coodinates have LOS. - -- + -- -- ## 4.7) Check the coordinate position. - -- + -- -- Various methods are available that allow to check if a coordinate is: - -- + -- -- * @{#COORDINATE.IsInRadius}(): in a give radius. -- * @{#COORDINATE.IsInSphere}(): is in a given sphere. -- * @{#COORDINATE.IsAtCoordinate2D}(): is in a given coordinate within a specific precision. - -- - -- + -- + -- -- -- # 5) Measure the simulation environment at the coordinate. - -- + -- -- ## 5.1) Weather specific. - -- + -- -- Within the DCS simulator, a coordinate has specific environmental properties, like wind, temperature, humidity etc. - -- + -- -- * @{#COORDINATE.GetWind}(): Retrieve the wind at the specific coordinate within the DCS simulator. -- * @{#COORDINATE.GetTemperature}(): Retrieve the temperature at the specific height within the DCS simulator. -- * @{#COORDINATE.GetPressure}(): Retrieve the pressure at the specific height within the DCS simulator. - -- + -- -- ## 5.2) Surface specific. - -- + -- -- Within the DCS simulator, the surface can have various objects placed at the coordinate, and the surface height will vary. - -- + -- -- * @{#COORDINATE.GetLandHeight}(): Retrieve the height of the surface (on the ground) within the DCS simulator. -- * @{#COORDINATE.GetSurfaceType}(): Retrieve the surface type (on the ground) within the DCS simulator. -- @@ -168,13 +168,13 @@ do -- COORDINATE -- Route points can be used in the Route methods of the @{Wrapper.Group#GROUP} class. -- -- ## 7) Manage the roads. - -- + -- -- Important for ground vehicle transportation and movement, the method @{#COORDINATE.GetClosestPointToRoad}() will calculate -- the closest point on the nearest road. - -- + -- -- In order to use the most optimal road system to transport vehicles, the method @{#COORDINATE.GetPathOnRoad}() will calculate -- the most optimal path following the road between two coordinates. - -- + -- -- ## 8) Metric or imperial system -- -- * @{#COORDINATE.IsMetric}(): Returns if the 3D point is Metric or Nautical Miles. @@ -182,7 +182,7 @@ do -- COORDINATE -- -- -- ## 9) Coordinate text generation - -- + -- -- -- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance. -- * @{#COORDINATE.ToStringLL}(): Generates a Latutude & Longutude text. @@ -192,13 +192,13 @@ do -- COORDINATE ClassName = "COORDINATE", } - --- @field COORDINATE.WaypointAltType + --- @field COORDINATE.WaypointAltType COORDINATE.WaypointAltType = { BARO = "BARO", RADIO = "RADIO", } - - --- @field COORDINATE.WaypointAction + + --- @field COORDINATE.WaypointAction COORDINATE.WaypointAction = { TurningPoint = "Turning Point", FlyoverPoint = "Fly Over Point", @@ -209,7 +209,7 @@ do -- COORDINATE LandingReFuAr = "LandingReFuAr", } - --- @field COORDINATE.WaypointType + --- @field COORDINATE.WaypointType COORDINATE.WaypointType = { TakeOffParking = "TakeOffParking", TakeOffParkingHot = "TakeOffParkingHot", @@ -226,14 +226,14 @@ do -- COORDINATE -- @param DCS#Distance y The y coordinate of the Vec3 point, pointing to the Right. -- @param DCS#Distance z The z coordinate of the Vec3 point, pointing to the Right. -- @return #COORDINATE - function COORDINATE:New( x, y, z ) + function COORDINATE:New( x, y, z ) --env.info("FF COORDINATE New") local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE self.x = x self.y = y self.z = z - + return self end @@ -241,13 +241,13 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #COORDINATE Coordinate. -- @return #COORDINATE - function COORDINATE:NewFromCoordinate( Coordinate ) + function COORDINATE:NewFromCoordinate( Coordinate ) local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE self.x = Coordinate.x self.y = Coordinate.y self.z = Coordinate.z - + return self end @@ -256,10 +256,10 @@ do -- COORDINATE -- @param DCS#Vec2 Vec2 The Vec2 point. -- @param DCS#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. -- @return #COORDINATE - function COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) + function COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) local LandHeight = land.getHeight( Vec2 ) - + LandHeightAdd = LandHeightAdd or 0 LandHeight = LandHeight + LandHeightAdd @@ -275,7 +275,7 @@ do -- COORDINATE -- @param #COORDINATE self -- @param DCS#Vec3 Vec3 The Vec3 point. -- @return #COORDINATE - function COORDINATE:NewFromVec3( Vec3 ) + function COORDINATE:NewFromVec3( Vec3 ) local self = self:New( Vec3.x, Vec3.y, Vec3.z ) -- #COORDINATE @@ -289,7 +289,7 @@ do -- COORDINATE -- @return #COORDINATE self function COORDINATE:GetCoordinate() return self - end + end --- Return the coordinates of the COORDINATE in Vec3 format. -- @param #COORDINATE self @@ -311,39 +311,39 @@ do -- COORDINATE -- @param DCS#Vec3 Vec3 The 3D vector with x,y,z components. -- @return #COORDINATE The modified COORDINATE itself. function COORDINATE:UpdateFromVec3(Vec3) - + self.x=Vec3.x self.y=Vec3.y self.z=Vec3.z - + return self end - + --- Update x,y,z coordinates from another given COORDINATE. -- @param #COORDINATE self -- @param #COORDINATE Coordinate The coordinate with the new x,y,z positions. -- @return #COORDINATE The modified COORDINATE itself. function COORDINATE:UpdateFromCoordinate(Coordinate) - + self.x=Coordinate.x self.y=Coordinate.y self.z=Coordinate.z - + return self - end + end --- Update x and z coordinates from a given 2D vector. -- @param #COORDINATE self -- @param DCS#Vec2 Vec2 The 2D vector with x,y components. x is overwriting COORDINATE.x while y is overwriting COORDINATE.z. -- @return #COORDINATE The modified COORDINATE itself. function COORDINATE:UpdateFromVec2(Vec2) - + self.x=Vec2.x self.z=Vec2.y - + return self end - + --- Returns the coordinate from the latitude and longitude given in decimal degrees. -- @param #COORDINATE self @@ -352,13 +352,13 @@ do -- COORDINATE -- @param #number altitude (Optional) Altitude in meters. Default is the land height at the coordinate. -- @return #COORDINATE function COORDINATE:NewFromLLDD( latitude, longitude, altitude) - + -- Returns a point from latitude and longitude in the vec3 format. local vec3=coord.LLtoLO(latitude, longitude) - + -- Convert vec3 to coordinate object. local _coord=self:NewFromVec3(vec3) - + -- Adjust height if altitude==nil then _coord.y=altitude @@ -369,23 +369,23 @@ do -- COORDINATE return _coord end - + --- Returns if the 2 coordinates are at the same 2D position. -- @param #COORDINATE self -- @param #COORDINATE Coordinate -- @param #number Precision -- @return #boolean true if at the same position. function COORDINATE:IsAtCoordinate2D( Coordinate, Precision ) - + self:F( { Coordinate = Coordinate:GetVec2() } ) self:F( { self = self:GetVec2() } ) - + local x = Coordinate.x local z = Coordinate.z - - return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z + + return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z end - + --- Scan/find objects (units, statics, scenery) within a certain radius around the coordinate using the world.searchObjects() DCS API function. -- @param #COORDINATE self -- @param #number radius (Optional) Scan radius in meters. Default 100 m. @@ -420,7 +420,7 @@ do -- COORDINATE if scanscenery==nil then scanscenery=false end - + --{Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY} local scanobjects={} if scanunits then @@ -432,7 +432,7 @@ do -- COORDINATE if scanscenery then table.insert(scanobjects, Object.Category.SCENERY) end - + -- Found stuff. local Units = {} local Statics = {} @@ -440,40 +440,40 @@ do -- COORDINATE local gotstatics=false local gotunits=false local gotscenery=false - + local function EvaluateZone(ZoneObject) - + if ZoneObject then - + -- Get category of scanned object. local ObjectCategory = ZoneObject:getCategory() - + -- Check for unit or static objects if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist() then - + table.insert(Units, UNIT:Find(ZoneObject)) gotunits=true - + elseif ObjectCategory==Object.Category.STATIC and ZoneObject:isExist() then - + table.insert(Statics, ZoneObject) gotstatics=true - + elseif ObjectCategory==Object.Category.SCENERY then - + table.insert(Scenery, ZoneObject) gotscenery=true - + end - + end - + return true end - + -- Search the world. world.searchObjects(scanobjects, SphereSearch, EvaluateZone) - + for _,unit in pairs(Units) do self:T(string.format("Scan found unit %s", unit:GetName())) end @@ -485,35 +485,35 @@ do -- COORDINATE self:T(string.format("Scan found scenery %s typename=%s", scenery:getName(), scenery:getTypeName())) --SCENERY:Register(scenery:getName(), scenery) end - + return gotunits, gotstatics, gotscenery, Units, Statics, Scenery end - + --- Scan/find UNITS within a certain radius around the coordinate using the world.searchObjects() DCS API function. -- @param #COORDINATE self -- @param #number radius (Optional) Scan radius in meters. Default 100 m. -- @return Core.Set#SET_UNIT Set of units. function COORDINATE:ScanUnits(radius) - + local _,_,_,units=self:ScanObjects(radius, true, false, false) - + local set=SET_UNIT:New() - + for _,unit in pairs(units) do set:AddUnit(unit) end - + return set end - - --- Find the closest unit to the COORDINATE within a certain radius. + + --- Find the closest unit to the COORDINATE within a certain radius. -- @param #COORDINATE self -- @param #number radius Scan radius in meters. Default 100 m. -- @return Wrapper.Unit#UNIT The closest unit or #nil if no unit is inside the given radius. function COORDINATE:FindClosestUnit(radius) - + local units=self:ScanUnits(radius) - + local umin=nil --Wrapper.Unit#UNIT local dmin=math.huge for _,_unit in pairs(units.Set) do @@ -524,41 +524,41 @@ do -- COORDINATE dmin=d umin=unit end - end - + end + return umin - end + end --- Scan/find SCENERY objects within a certain radius around the coordinate using the world.searchObjects() DCS API function. -- @param #COORDINATE self -- @param #number radius (Optional) Scan radius in meters. Default 100 m. -- @return table Set of scenery objects. function COORDINATE:ScanScenery(radius) - + local _,_,_,_,_,scenerys=self:ScanObjects(radius, false, false, true) - + local set={} - + for _,_scenery in pairs(scenerys) do local scenery=_scenery --DCS#Object - + local name=scenery:getName() local s=SCENERY:Register(name, scenery) table.insert(set, s) end - - return set - end - --- Find the closest scenery to the COORDINATE within a certain radius. + return set + end + + --- Find the closest scenery to the COORDINATE within a certain radius. -- @param #COORDINATE self -- @param #number radius Scan radius in meters. Default 100 m. -- @return Wrapper.Scenery#SCENERY The closest scenery or #nil if no object is inside the given radius. function COORDINATE:FindClosestScenery(radius) - + local sceneries=self:ScanScenery(radius) - + local umin=nil --Wrapper.Scenery#SCENERY local dmin=math.huge for _,_scenery in pairs(sceneries) do @@ -569,11 +569,11 @@ do -- COORDINATE dmin=d umin=scenery end - end - + end + return umin - end - + end + --- Calculate the distance from a reference @{#COORDINATE}. -- @param #COORDINATE self -- @param #COORDINATE PointVec2Reference The reference @{#COORDINATE}. @@ -596,14 +596,14 @@ do -- COORDINATE -- @return #COORDINATE The new calculated COORDINATE. function COORDINATE:Translate( Distance, Angle, Keepalt, Overwrite ) - -- Angle in rad. + -- Angle in rad. local alpha = math.rad((Angle or 0)) - + local x = Distance * math.cos(alpha) + self.x -- New x local z = Distance * math.sin(alpha) + self.z -- New z - + local y=Keepalt and self.y or land.getHeight({x=x, y=z}) - + if Overwrite then self.x=x self.y=y @@ -614,7 +614,7 @@ do -- COORDINATE local coord=COORDINATE:New(x, y, z) return coord end - + end --- Rotate coordinate in 2D (x,z) space. @@ -622,25 +622,25 @@ do -- COORDINATE -- @param DCS#Angle Angle Angle of rotation in degrees. -- @return Core.Point#COORDINATE The rotated coordinate. function COORDINATE:Rotate2D(Angle) - + if not Angle then return self end local phi=math.rad(Angle) - + local X=self.z local Y=self.x - + --slocal R=math.sqrt(X*X+Y*Y) - + local x=X*math.cos(phi)-Y*math.sin(phi) local y=X*math.sin(phi)+Y*math.cos(phi) -- Coordinate assignment looks bit strange but is correct. return COORDINATE:NewFromVec3({x=y, y=self.y, z=x}) end - + --- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. -- @param #COORDINATE self -- @param DCS#Distance OuterRadius @@ -698,7 +698,7 @@ do -- COORDINATE return RandomVec3 end - + --- Return the height of the land at the coordinate. -- @param #COORDINATE self -- @return #number Land height (ASL) in meters. @@ -713,8 +713,8 @@ do -- COORDINATE function COORDINATE:SetHeading( Heading ) self.Heading = Heading end - - + + --- Get the heading of the coordinate, if applicable. -- @param #COORDINATE self -- @return #number or nil @@ -722,7 +722,7 @@ do -- COORDINATE return self.Heading end - + --- Set the velocity of the COORDINATE. -- @param #COORDINATE self -- @param #string Velocity Velocity in meters per second. @@ -730,7 +730,7 @@ do -- COORDINATE self.Velocity = Velocity end - + --- Return the velocity of the COORDINATE. -- @param #COORDINATE self -- @return #number Velocity in meters per second. @@ -739,14 +739,14 @@ do -- COORDINATE return Velocity or 0 end - --- Return the "name" of the COORDINATE. Obviously, a coordinate does not have a name like a unit, static or group. So here we take the MGRS coordinates of the position. + --- Return the "name" of the COORDINATE. Obviously, a coordinate does not have a name like a unit, static or group. So here we take the MGRS coordinates of the position. -- @param #COORDINATE self -- @return #string MGRS coordinates. function COORDINATE:GetName() local name=self:ToStringMGRS() return name end - + --- Return velocity text of the COORDINATE. -- @param #COORDINATE self -- @return #string @@ -805,20 +805,20 @@ do -- COORDINATE -- @param #number Fraction The fraction (0,1) where the new coordinate is created. Default 0.5, i.e. in the middle. -- @return #COORDINATE Coordinate between this and the other coordinate. function COORDINATE:GetIntermediateCoordinate( ToCoordinate, Fraction ) - + local f=Fraction or 0.5 - -- Get the vector from A to B + -- Get the vector from A to B local vec=UTILS.VecSubstract(ToCoordinate, self) - + -- Scale the vector. vec.x=f*vec.x vec.y=f*vec.y vec.z=f*vec.z - + -- Move the vector to start at the end of A. vec=UTILS.VecAdd(self, vec) - + local coord=COORDINATE:New(vec.x,vec.y,vec.z) return coord end @@ -827,14 +827,14 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3. -- @return DCS#Distance Distance The distance in meters. - function COORDINATE:Get2DDistance( TargetCoordinate ) + function COORDINATE:Get2DDistance(TargetCoordinate) local a={x=TargetCoordinate.x-self.x, y=0, z=TargetCoordinate.z-self.z} - + local norm=UTILS.VecNorm(a) return norm end - + --- Returns the temperature in Degrees Celsius. -- @param #COORDINATE self -- @param height (Optional) parameter specifying the height ASL. @@ -851,22 +851,22 @@ do -- COORDINATE --- Returns a text of the temperature according the measurement system @{Settings}. -- The text will reflect the temperature like this: - -- + -- -- - For Russian and European aircraft using the metric system - Degrees Celcius (°C) -- - For Americain aircraft we link to the imperial system - Degrees Farenheit (°F) - -- - -- A text containing a pressure will look like this: - -- - -- - `Temperature: %n.d °C` + -- + -- A text containing a pressure will look like this: + -- + -- - `Temperature: %n.d °C` -- - `Temperature: %n.d °F` - -- + -- -- @param #COORDINATE self -- @param height (Optional) parameter specifying the height ASL. -- @return #string Temperature according the measurement system @{Settings}. function COORDINATE:GetTemperatureText( height, Settings ) - + local DegreesCelcius = self:GetTemperature( height ) - + local Settings = Settings or _SETTINGS if DegreesCelcius then @@ -878,7 +878,7 @@ do -- COORDINATE else return " no temperature" end - + return nil end @@ -894,18 +894,18 @@ do -- COORDINATE -- Return Pressure in hPa. return P/100 end - + --- Returns a text of the pressure according the measurement system @{Settings}. -- The text will contain always the pressure in hPa and: - -- + -- -- - For Russian and European aircraft using the metric system - hPa and mmHg -- - For Americain and European aircraft we link to the imperial system - hPa and inHg - -- - -- A text containing a pressure will look like this: - -- - -- - `QFE: x hPa (y mmHg)` + -- + -- A text containing a pressure will look like this: + -- + -- - `QFE: x hPa (y mmHg)` -- - `QFE: x hPa (y inHg)` - -- + -- -- @param #COORDINATE self -- @param height (Optional) parameter specifying the height ASL. E.g. set height=0 for QNH. -- @return #string Pressure in hPa and mmHg or inHg depending on the measurement system @{Settings}. @@ -914,7 +914,7 @@ do -- COORDINATE local Pressure_hPa = self:GetPressure( height ) local Pressure_mmHg = Pressure_hPa * 0.7500615613030 local Pressure_inHg = Pressure_hPa * 0.0295299830714 - + local Settings = Settings or _SETTINGS if Pressure_hPa then @@ -926,14 +926,14 @@ do -- COORDINATE else return " no pressure" end - + return nil end - + --- Returns the heading from this to another coordinate. -- @param #COORDINATE self -- @param #COORDINATE ToCoordinate - -- @return #number Heading in degrees. + -- @return #number Heading in degrees. function COORDINATE:HeadingTo(ToCoordinate) local dz=ToCoordinate.z-self.z local dx=ToCoordinate.x-self.x @@ -943,7 +943,7 @@ do -- COORDINATE end return heading end - + --- Returns the wind direction (from) and strength. -- @param #COORDINATE self -- @param height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground. @@ -953,12 +953,12 @@ do -- COORDINATE local landheight=self:GetLandHeight()+0.1 -- we at 0.1 meters to be sure to be above ground since wind is zero below ground level. local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z} -- get wind velocity vector - local wind = atmosphere.getWind(point) + local wind = atmosphere.getWind(point) local direction = math.deg(math.atan2(wind.z, wind.x)) if direction < 0 then direction = 360 + direction end - -- Convert to direction to from direction + -- Convert to direction to from direction if direction > 180 then direction = direction-180 else @@ -968,37 +968,37 @@ do -- COORDINATE -- Return wind direction and strength km/h. return direction, strength end - + --- Returns the wind direction (from) and strength. -- @param #COORDINATE self -- @param height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground. -- @return Direction the wind is blowing from in degrees. function COORDINATE:GetWindWithTurbulenceVec3(height) - - -- AGL height if + + -- AGL height if local landheight=self:GetLandHeight()+0.1 -- we at 0.1 meters to be sure to be above ground since wind is zero below ground level. - - -- Point at which the wind is evaluated. + + -- Point at which the wind is evaluated. local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z} - + -- Get wind velocity vector including turbulences. local vec3 = atmosphere.getWindWithTurbulence(point) - + return vec3 - end + end --- Returns a text documenting the wind direction (from) and strength according the measurement system @{Settings}. -- The text will reflect the wind like this: - -- + -- -- - For Russian and European aircraft using the metric system - Wind direction in degrees (°) and wind speed in meters per second (mps). -- - For Americain aircraft we link to the imperial system - Wind direction in degrees (°) and wind speed in knots per second (kps). - -- - -- A text containing a pressure will look like this: - -- - -- - `Wind: %n ° at n.d mps` + -- + -- A text containing a pressure will look like this: + -- + -- - `Wind: %n ° at n.d mps` -- - `Wind: %n ° at n.d kps` - -- + -- -- @param #COORDINATE self -- @param height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground. -- @return #string Wind direction and strength according the measurement system @{Settings}. @@ -1017,7 +1017,7 @@ do -- COORDINATE else return " no wind" end - + return nil end @@ -1043,9 +1043,9 @@ do -- COORDINATE local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS local AngleDegrees = UTILS.Round( UTILS.ToDegree( AngleRadians ), Precision ) - - local s = string.format( '%03d°', AngleDegrees ) - + + local s = string.format( '%03d°', AngleDegrees ) + return s end @@ -1074,7 +1074,7 @@ do -- COORDINATE DistanceText = " за " .. UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) .. " миль" end end - + return DistanceText end @@ -1085,7 +1085,7 @@ do -- COORDINATE local Altitude = self.y local Settings = Settings or _SETTINGS local Language = Language or "EN" - + if Altitude ~= 0 then if Settings:IsMetric() then if Language == "EN" then @@ -1150,7 +1150,7 @@ do -- COORDINATE local BearingText = self:GetBearingText( AngleRadians, 0, Settings, Language ) local DistanceText = self:GetDistanceText( Distance, Settings, Language ) - + local BRText = BearingText .. DistanceText return BRText @@ -1206,44 +1206,44 @@ do -- COORDINATE -- @return #table The route point. function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked, airbase, DCSTasks, description, timeReFuAr ) self:F2( { AltType, Type, Action, Speed, SpeedLocked } ) - + -- Set alttype or "RADIO" which is AGL. AltType=AltType or "RADIO" - + -- Speedlocked by default if SpeedLocked==nil then SpeedLocked=true end - + -- Speed or default 500 km/h. Speed=Speed or 500 - + -- Waypoint array. local RoutePoint = {} - + -- Coordinates. RoutePoint.x = self.x RoutePoint.y = self.z - + -- Altitude. RoutePoint.alt = self.y RoutePoint.alt_type = AltType - + -- Waypoint type. RoutePoint.type = Type or nil - RoutePoint.action = Action or nil - + RoutePoint.action = Action or nil + -- Speed. RoutePoint.speed = Speed/3.6 RoutePoint.speed_locked = SpeedLocked - + -- ETA. RoutePoint.ETA=0 RoutePoint.ETA_locked=true - + -- Waypoint description. RoutePoint.name=description - + -- Airbase parameters for takeoff and landing points. if airbase then local AirbaseID = airbase:GetID() @@ -1257,26 +1257,26 @@ do -- COORDINATE self:E("ERROR: Unknown airbase category in COORDINATE:WaypointAir()!") end end - - -- Time in minutes to stay at the airbase before resuming route. + + -- Time in minutes to stay at the airbase before resuming route. if Type==COORDINATE.WaypointType.LandingReFuAr then RoutePoint.timeReFuAr=timeReFuAr or 10 end - + -- Waypoint tasks. RoutePoint.task = {} RoutePoint.task.id = "ComboTask" RoutePoint.task.params = {} RoutePoint.task.params.tasks = DCSTasks or {} - + --RoutePoint.properties={} --RoutePoint.properties.addopt={} - + --RoutePoint.formation_template="" -- Debug. self:T({RoutePoint=RoutePoint}) - + -- Return waypoint. return RoutePoint end @@ -1293,7 +1293,7 @@ do -- COORDINATE return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true, nil, DCSTasks, description ) end - + --- Build a Waypoint Air "Fly Over Point". -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. @@ -1302,8 +1302,8 @@ do -- COORDINATE function COORDINATE:WaypointAirFlyOverPoint( AltType, Speed ) return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.FlyoverPoint, Speed ) end - - + + --- Build a Waypoint Air "Take Off Parking Hot". -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. @@ -1312,7 +1312,7 @@ do -- COORDINATE function COORDINATE:WaypointAirTakeOffParkingHot( AltType, Speed ) return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOffParkingHot, COORDINATE.WaypointAction.FromParkingAreaHot, Speed ) end - + --- Build a Waypoint Air "Take Off Parking". -- @param #COORDINATE self @@ -1322,8 +1322,8 @@ do -- COORDINATE function COORDINATE:WaypointAirTakeOffParking( AltType, Speed ) return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, Speed ) end - - + + --- Build a Waypoint Air "Take Off Runway". -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. @@ -1332,8 +1332,8 @@ do -- COORDINATE function COORDINATE:WaypointAirTakeOffRunway( AltType, Speed ) return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOff, COORDINATE.WaypointAction.FromRunway, Speed ) end - - + + --- Build a Waypoint Air "Landing". -- @param #COORDINATE self -- @param DCS#Speed Speed Airspeed in km/h. @@ -1342,17 +1342,17 @@ do -- COORDINATE -- @param #string description A text description of the waypoint, which will be shown on the F10 map. -- @return #table The route point. -- @usage - -- + -- -- LandingZone = ZONE:New( "LandingZone" ) -- LandingCoord = LandingZone:GetCoordinate() -- LandingWaypoint = LandingCoord:WaypointAirLanding( 60 ) -- HeliGroup:Route( { LandWaypoint }, 1 ) -- Start landing the helicopter in one second. - -- + -- function COORDINATE:WaypointAirLanding( Speed, airbase, DCSTasks, description ) return self:WaypointAir(nil, COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, Speed, false, airbase, DCSTasks, description) end - - --- Build a Waypoint Air "LandingReFuAr". Mimics the aircraft ReFueling and ReArming. + + --- Build a Waypoint Air "LandingReFuAr". Mimics the aircraft ReFueling and ReArming. -- @param #COORDINATE self -- @param DCS#Speed Speed Airspeed in km/h. -- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points. @@ -1362,9 +1362,9 @@ do -- COORDINATE -- @return #table The route point. function COORDINATE:WaypointAirLandingReFu( Speed, airbase, timeReFuAr, DCSTasks, description ) return self:WaypointAir(nil, COORDINATE.WaypointType.LandingReFuAr, COORDINATE.WaypointAction.LandingReFuAr, Speed, false, airbase, DCSTasks, description, timeReFuAr or 10) - end - - + end + + --- Build an ground type route point. -- @param #COORDINATE self -- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h. @@ -1375,18 +1375,18 @@ do -- COORDINATE self:F2( { Speed, Formation, DCSTasks } ) local RoutePoint = {} - + RoutePoint.x = self.x RoutePoint.y = self.z - + RoutePoint.alt = self:GetLandHeight()+1 RoutePoint.alt_type = COORDINATE.WaypointAltType.BARO - + RoutePoint.type = "Turning Point" - + RoutePoint.action = Formation or "Off Road" RoutePoint.formation_template="" - + RoutePoint.ETA=0 RoutePoint.ETA_locked=true @@ -1397,10 +1397,10 @@ do -- COORDINATE RoutePoint.task.id = "ComboTask" RoutePoint.task.params = {} RoutePoint.task.params.tasks = DCSTasks or {} - + return RoutePoint end - + --- Build route waypoint point for Naval units. -- @param #COORDINATE self -- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h. @@ -1411,10 +1411,10 @@ do -- COORDINATE self:F2( { Speed, Depth, DCSTasks } ) local RoutePoint = {} - + RoutePoint.x = self.x RoutePoint.y = self.z - + RoutePoint.alt = Depth or self.y -- Depth is for submarines only. Ships should have alt=0. RoutePoint.alt_type = "BARO" @@ -1443,10 +1443,10 @@ do -- COORDINATE -- @return Wrapper.Airbase#AIRBASE Closest Airbase to the given coordinate. -- @return #number Distance to the closest airbase in meters. function COORDINATE:GetClosestAirbase2(Category, Coalition) - + -- Get all airbases of the map. local airbases=AIRBASE.GetAllAirbases(Coalition) - + local closest=nil local distmin=nil -- Loop over all airbases. @@ -1456,9 +1456,9 @@ do -- COORDINATE local category=airbase:GetAirbaseCategory() if Category and Category==category or Category==nil then - -- Distance to airbase. + -- Distance to airbase. local dist=self:Get2DDistance(airbase:GetCoordinate()) - + if closest==nil then distmin=dist closest=airbase @@ -1466,13 +1466,13 @@ do -- COORDINATE if dist=2 then for i=1,#Path-1 do @@ -1684,8 +1684,8 @@ do -- COORDINATE else -- There are cases where no path on road can be found. return nil,nil,false - end - + end + return Path, Way, GotPath end @@ -1838,7 +1838,7 @@ do -- COORDINATE density=density or 0.5 self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire, density) end - + --- Large smoke and fire at the coordinate. -- @param #COORDINATE self -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. @@ -1856,7 +1856,7 @@ do -- COORDINATE density=density or 0.5 self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire, density) end - + --- Small smoke at the coordinate. -- @param #COORDINATE self -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. @@ -1865,7 +1865,7 @@ do -- COORDINATE density=density or 0.5 self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke, density) end - + --- Medium smoke at the coordinate. -- @param #COORDINATE self -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. @@ -1883,7 +1883,7 @@ do -- COORDINATE density=density or 0.5 self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke, density) end - + --- Huge smoke at the coordinate. -- @param #COORDINATE self -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. @@ -1891,7 +1891,7 @@ do -- COORDINATE self:F2( { density=density } ) density=density or 0.5 self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke, density) - end + end --- Flares the point in a color. -- @param #COORDINATE self @@ -1932,9 +1932,9 @@ do -- COORDINATE self:F2( Azimuth ) self:Flare( FLARECOLOR.Red, Azimuth ) end - + do -- Markings - + --- Mark to All -- @param #COORDINATE self -- @param #string MarkText Free format text that shows the marking clarification. @@ -2020,7 +2020,7 @@ do -- COORDINATE trigger.action.markToGroup( MarkID, MarkText, self:GetVec3(), MarkGroup:GetID(), ReadOnly, text ) return MarkID end - + --- Remove a mark -- @param #COORDINATE self -- @param #number MarkID The ID of the mark to be removed. @@ -2033,9 +2033,9 @@ do -- COORDINATE function COORDINATE:RemoveMark( MarkID ) trigger.action.removeMark( MarkID ) end - + end -- Markings - + --- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate. -- @param #COORDINATE self @@ -2043,7 +2043,7 @@ do -- COORDINATE -- @param #number Offset Height offset in meters. Default 2 m. -- @return #boolean true If the ToCoordinate has LOS with the Coordinate, otherwise false. function COORDINATE:IsLOS( ToCoordinate, Offset ) - + Offset=Offset or 2 -- Measurement of visibility should not be from the ground, so Adding a hypotethical 2 meters to each Coordinate. @@ -2068,7 +2068,7 @@ do -- COORDINATE local InVec2 = self:GetVec2() local Vec2 = Coordinate:GetVec2() - + local InRadius = UTILS.IsInRadius( InVec2, Vec2, Radius) return InRadius @@ -2085,7 +2085,7 @@ do -- COORDINATE local InVec3 = self:GetVec3() local Vec3 = Coordinate:GetVec3() - + local InSphere = UTILS.IsInSphere( InVec3, Vec3, Radius) return InSphere @@ -2096,17 +2096,17 @@ do -- COORDINATE -- @param #number Day The day. -- @param #number Month The month. -- @param #number Year The year. - -- @param #boolean InSeconds If true, return the sun rise time in seconds. + -- @param #boolean InSeconds If true, return the sun rise time in seconds. -- @return #string Sunrise time, e.g. "05:41". function COORDINATE:GetSunriseAtDate(Day, Month, Year, InSeconds) - - -- Day of the year. + + -- Day of the year. local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day) - + local Latitude, Longitude=self:GetLLDDM() - + local Tdiff=UTILS.GMTToLocalTimeDifference() - + local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff) if InSeconds then @@ -2114,20 +2114,20 @@ do -- COORDINATE else return UTILS.SecondsToClock(sunrise, true) end - + end - + --- Get sun rise time for a specific day of the year at the coordinate. -- @param #COORDINATE self -- @param #number DayOfYear The day of the year. - -- @param #boolean InSeconds If true, return the sun rise time in seconds. + -- @param #boolean InSeconds If true, return the sun rise time in seconds. -- @return #string Sunrise time, e.g. "05:41". function COORDINATE:GetSunriseAtDayOfYear(DayOfYear, InSeconds) - + local Latitude, Longitude=self:GetLLDDM() - + local Tdiff=UTILS.GMTToLocalTimeDifference() - + local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff) if InSeconds then @@ -2135,38 +2135,38 @@ do -- COORDINATE else return UTILS.SecondsToClock(sunrise, true) end - + end - + --- Get todays sun rise time. -- @param #COORDINATE self - -- @param #boolean InSeconds If true, return the sun rise time in seconds. + -- @param #boolean InSeconds If true, return the sun rise time in seconds. -- @return #string Sunrise time, e.g. "05:41". function COORDINATE:GetSunrise(InSeconds) - - -- Get current day of the year. + + -- Get current day of the year. local DayOfYear=UTILS.GetMissionDayOfYear() - + -- Lat and long at this point. local Latitude, Longitude=self:GetLLDDM() - + -- GMT time diff. local Tdiff=UTILS.GMTToLocalTimeDifference() - + -- Sunrise in seconds of the day. local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff) - + local date=UTILS.GetDCSMissionDate() - + -- Debug output. --self:I(string.format("Sun rise at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%d sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), sunrise, Tdiff)) - + if InSeconds then return sunrise else return UTILS.SecondsToClock(sunrise, true) end - + end --- Get minutes until the next sun rise at this coordinate. @@ -2174,103 +2174,103 @@ do -- COORDINATE -- @param OnlyToday If true, only calculate the sun rise of today. If sun has already risen, the time in negative minutes since sunrise is reported. -- @return #number Minutes to the next sunrise. function COORDINATE:GetMinutesToSunrise(OnlyToday) - + -- Seconds of today local time=UTILS.SecondsOfToday() -- Next Sunrise in seconds. local sunrise=nil - + -- Time to sunrise. local delta=nil - + if OnlyToday then - + --- -- Sunrise of today --- - + sunrise=self:GetSunrise(true) - + delta=sunrise-time - + else --- -- Sunrise of tomorrow --- - + -- Tomorrows day of the year. local DayOfYear=UTILS.GetMissionDayOfYear()+1 local Latitude, Longitude=self:GetLLDDM() - + local Tdiff=UTILS.GMTToLocalTimeDifference() - + sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff) - + delta=sunrise+UTILS.SecondsToMidnight() end return delta/60 end - + --- Check if it is day, i.e. if the sun has risen about the horizon at this coordinate. -- @param #COORDINATE self -- @param #string Clock (Optional) Time in format "HH:MM:SS+D", e.g. "05:40:00+3" to check if is day at 5:40 at third day after mission start. Default is to check right now. -- @return #boolean If true, it is day. If false, it is night time. function COORDINATE:IsDay(Clock) - + if Clock then - + local Time=UTILS.ClockToSeconds(Clock) - + local clock=UTILS.Split(Clock, "+")[1] - + -- Tomorrows day of the year. local DayOfYear=UTILS.GetMissionDayOfYear(Time) local Latitude, Longitude=self:GetLLDDM() - + local Tdiff=UTILS.GMTToLocalTimeDifference() - + local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, true, Tdiff) local sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff) - + local time=UTILS.ClockToSeconds(clock) - - -- Check if time is between sunrise and sunset. - if time>sunrise and time<=sunset then - return true - else - return false - end - - else - - -- Todays sun rise in sec. - local sunrise=self:GetSunrise(true) - - -- Todays sun set in sec. - local sunset=self:GetSunset(true) - - -- Seconds passed since midnight. - local time=UTILS.SecondsOfToday() - + -- Check if time is between sunrise and sunset. if time>sunrise and time<=sunset then return true else return false end - - end - + + else + + -- Todays sun rise in sec. + local sunrise=self:GetSunrise(true) + + -- Todays sun set in sec. + local sunset=self:GetSunset(true) + + -- Seconds passed since midnight. + local time=UTILS.SecondsOfToday() + + -- Check if time is between sunrise and sunset. + if time>sunrise and time<=sunset then + return true + else + return false + end + + end + end - + --- Check if it is night, i.e. if the sun has set below the horizon at this coordinate. - -- @param #COORDINATE self + -- @param #COORDINATE self -- @param #string Clock (Optional) Time in format "HH:MM:SS+D", e.g. "05:40:00+3" to check if is night at 5:40 at third day after mission start. Default is to check right now. -- @return #boolean If true, it is night. If false, it is day time. function COORDINATE:IsNight(Clock) @@ -2282,17 +2282,17 @@ do -- COORDINATE -- @param #number Day The day. -- @param #number Month The month. -- @param #number Year The year. - -- @param #boolean InSeconds If true, return the sun rise time in seconds. + -- @param #boolean InSeconds If true, return the sun rise time in seconds. -- @return #string Sunset time, e.g. "20:41". function COORDINATE:GetSunsetAtDate(Day, Month, Year, InSeconds) - - -- Day of the year. + + -- Day of the year. local DayOfYear=UTILS.GetDayOfYear(Year, Month, Day) - + local Latitude, Longitude=self:GetLLDDM() - + local Tdiff=UTILS.GMTToLocalTimeDifference() - + local sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff) if InSeconds then @@ -2300,80 +2300,80 @@ do -- COORDINATE else return UTILS.SecondsToClock(sunset, true) end - + end --- Get todays sun set time. -- @param #COORDINATE self - -- @param #boolean InSeconds If true, return the sun set time in seconds. + -- @param #boolean InSeconds If true, return the sun set time in seconds. -- @return #string Sunrise time, e.g. "20:41". function COORDINATE:GetSunset(InSeconds) - - -- Get current day of the year. + + -- Get current day of the year. local DayOfYear=UTILS.GetMissionDayOfYear() - + -- Lat and long at this point. local Latitude, Longitude=self:GetLLDDM() - + -- GMT time diff. local Tdiff=UTILS.GMTToLocalTimeDifference() - + -- Sunrise in seconds of the day. local sunrise=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff) - + local date=UTILS.GetDCSMissionDate() - + -- Debug output. --self:I(string.format("Sun set at lat=%.3f long=%.3f on %s (DayOfYear=%d): %s (%d sec of the day) (GMT %d)", Latitude, Longitude, date, DayOfYear, tostring(UTILS.SecondsToClock(sunrise)), sunrise, Tdiff)) - + if InSeconds then return sunrise else return UTILS.SecondsToClock(sunrise, true) end - + end - + --- Get minutes until the next sun set at this coordinate. -- @param #COORDINATE self -- @param OnlyToday If true, only calculate the sun set of today. If sun has already set, the time in negative minutes since sunset is reported. -- @return #number Minutes to the next sunrise. function COORDINATE:GetMinutesToSunset(OnlyToday) - + -- Seconds of today local time=UTILS.SecondsOfToday() -- Next Sunset in seconds. local sunset=nil - + -- Time to sunrise. local delta=nil - + if OnlyToday then - + --- -- Sunset of today --- - + sunset=self:GetSunset(true) - + delta=sunset-time - + else --- -- Sunset of tomorrow --- - + -- Tomorrows day of the year. local DayOfYear=UTILS.GetMissionDayOfYear()+1 local Latitude, Longitude=self:GetLLDDM() - + local Tdiff=UTILS.GMTToLocalTimeDifference() - + sunset=UTILS.GetSunRiseAndSet(DayOfYear, Latitude, Longitude, false, Tdiff) - + delta=sunset+UTILS.SecondsToMidnight() end @@ -2429,7 +2429,7 @@ do -- COORDINATE local Heading = self.Heading local DirectionVec3 = self:GetDirectionVec3( TargetCoordinate ) local Angle = self:GetAngleDegrees( DirectionVec3 ) - + if Heading then local Aspect = Angle - Heading if Aspect > -135 and Aspect <= -45 then @@ -2452,7 +2452,7 @@ do -- COORDINATE -- @param #COORDINATE self -- @return #number Latitude in DDM. -- @return #number Lontitude in DDM. - function COORDINATE:GetLLDDM() + function COORDINATE:GetLLDDM() return coord.LOtoLL( self:GetVec3() ) end @@ -2460,7 +2460,7 @@ do -- COORDINATE -- @param #COORDINATE self -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @return #string The LL DMS Text - function COORDINATE:ToStringLLDMS( Settings ) + function COORDINATE:ToStringLLDMS( Settings ) local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy local lat, lon = coord.LOtoLL( self:GetVec3() ) @@ -2500,11 +2500,11 @@ do -- COORDINATE -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @return #string The coordinate Text in the configured coordinate system. function COORDINATE:ToStringFromRP( ReferenceCoord, ReferenceName, Controllable, Settings ) - + self:F2( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS - + local IsAir = Controllable and Controllable:IsAirPlane() or false if IsAir then @@ -2518,7 +2518,7 @@ do -- COORDINATE local Distance = self:Get2DDistance( ReferenceCoord ) return "Target are located " .. self:GetBRText( AngleRadians, Distance, Settings ) .. " from " .. ReferenceName end - + return nil end @@ -2528,8 +2528,8 @@ do -- COORDINATE -- @param Wrapper.Controllable#CONTROLLABLE Controllable -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @return #string The coordinate Text in the configured coordinate system. - function COORDINATE:ToStringA2G( Controllable, Settings ) - + function COORDINATE:ToStringA2G( Controllable, Settings ) + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -2564,7 +2564,7 @@ do -- COORDINATE -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @return #string The coordinate Text in the configured coordinate system. function COORDINATE:ToStringA2A( Controllable, Settings, Language ) -- R2.2 - + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -2572,7 +2572,7 @@ do -- COORDINATE if Settings:IsA2A_BRAA() then if Controllable then local Coordinate = Controllable:GetCoordinate() - return self:ToStringBRA( Coordinate, Settings, Language ) + return self:ToStringBRA( Coordinate, Settings, Language ) else return self:ToStringMGRS( Settings, Language ) end @@ -2604,13 +2604,13 @@ do -- COORDINATE -- @param Tasking.Task#TASK Task The task for which coordinates need to be calculated. -- @return #string The coordinate Text in the configured coordinate system. function COORDINATE:ToString( Controllable, Settings, Task ) - + -- self:E( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS local ModeA2A = nil - + if Task then if Task:IsInstanceOf( TASK_A2A ) then ModeA2A = true @@ -2627,8 +2627,8 @@ do -- COORDINATE end end end - - + + if ModeA2A == nil then local IsAir = Controllable and ( Controllable:IsAirPlane() or Controllable:IsHelicopter() ) or false if IsAir then @@ -2637,14 +2637,14 @@ do -- COORDINATE ModeA2A = false end end - + if ModeA2A == true then return self:ToStringA2A( Controllable, Settings ) else return self:ToStringA2G( Controllable, Settings ) end - + return nil end @@ -2657,7 +2657,7 @@ do -- COORDINATE -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @return #string The pressure text in the configured measurement system. function COORDINATE:ToStringPressure( Controllable, Settings ) -- R2.3 - + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -2673,7 +2673,7 @@ do -- COORDINATE -- @param Core.Settings#SETTINGS Settings (optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object. -- @return #string The wind text in the configured measurement system. function COORDINATE:ToStringWind( Controllable, Settings ) - + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -2686,10 +2686,10 @@ do -- COORDINATE -- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default. -- @param #COORDINATE self -- @param Wrapper.Controllable#CONTROLLABLE Controllable - -- @param Core.Settings#SETTINGS + -- @param Core.Settings#SETTINGS -- @return #string The temperature text in the configured measurement system. function COORDINATE:ToStringTemperature( Controllable, Settings ) - + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -2712,8 +2712,8 @@ do -- POINT_VEC3 -- @field #POINT_VEC3.RoutePointType RoutePointType -- @field #POINT_VEC3.RoutePointAction RoutePointAction -- @extends #COORDINATE - - + + --- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. -- -- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. @@ -2799,7 +2799,7 @@ do -- POINT_VEC3 local self = BASE:Inherit( self, COORDINATE:New( x, y, z ) ) -- Core.Point#POINT_VEC3 self:F2( self ) - + return self end @@ -2825,7 +2825,7 @@ do -- POINT_VEC3 local self = BASE:Inherit( self, COORDINATE:NewFromVec3( Vec3 ) ) -- Core.Point#POINT_VEC3 self:F2( self ) - + return self end @@ -2924,7 +2924,7 @@ do -- POINT_VEC2 -- @field DCS#Distance x The x coordinate in meters. -- @field DCS#Distance y the y coordinate in meters. -- @extends Core.Point#COORDINATE - + --- Defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. -- -- ## POINT_VEC2 constructor @@ -2952,7 +2952,7 @@ do -- POINT_VEC2 POINT_VEC2 = { ClassName = "POINT_VEC2", } - + --- POINT_VEC2 constructor. @@ -3137,5 +3137,3 @@ do -- POINT_VEC2 end end - - diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 0f4b164fe..fc850743c 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -53,6 +53,7 @@ -- @type RANGE -- @field #string ClassName Name of the Class. -- @field #boolean Debug If true, debug info is send as messages on the screen. +-- @field #boolean verbose Verbosity level. Higher means more output to DCS log file. -- @field #string id String id of range for output in DCS log. -- @field #string rangename Name of the range. -- @field Core.Point#COORDINATE location Coordinate of the range location. @@ -289,6 +290,7 @@ RANGE={ ClassName = "RANGE", Debug = false, + verbose = 0, id = nil, rangename = nil, location = nil, @@ -518,7 +520,7 @@ RANGE.MenuF10Root=nil --- Range script version. -- @field #string version -RANGE.version="2.2.3" +RANGE.version="2.3.0" --TODO list: --TODO: Verbosity level for messages. @@ -556,7 +558,6 @@ function RANGE:New(rangename) -- Debug info. local text=string.format("Script version %s - creating new RANGE object %s.", RANGE.version, self.rangename) self:I(self.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) -- Defaults self:SetDefaultPlayerSmokeBomb() @@ -723,7 +724,6 @@ function RANGE:onafterStart() -- Starting range. local text=string.format("Starting RANGE %s. Number of strafe targets = %d. Number of bomb targets = %d.", self.rangename, self.nstrafetargets, self.nbombtargets) self:I(self.id..text) - MESSAGE:New(text,10):ToAllIf(self.Debug) -- Event handling. if self.eventmoose then @@ -757,6 +757,7 @@ function RANGE:onafterStart() -- Radio queue. self.rangecontrol=RADIOQUEUE:New(self.rangecontrolfreq, nil, self.rangename) + self.rangecontrol.schedonce=true -- Init numbers. self.rangecontrol:SetDigit(0, RANGE.Sound.RC0.filename, RANGE.Sound.RC0.duration, self.soundpath) @@ -781,7 +782,8 @@ function RANGE:onafterStart() if self.instructorfreq then -- Radio queue. - self.instructor=RADIOQUEUE:New(self.instructorfreq, nil, self.rangename) + self.instructor=RADIOQUEUE:New(self.instructorfreq, nil, self.rangename) + self.instructor.schedonce=true -- Init numbers. self.instructor:SetDigit(0, RANGE.Sound.IR0.filename, RANGE.Sound.IR0.duration, self.soundpath) @@ -1148,7 +1150,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe -- Neither unit nor static object with this name could be found. local text=string.format("ERROR! Could not find ANY strafe target object with name %s.", _name) self:E(self.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) end @@ -1168,7 +1169,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe if ntargets==0 then local text=string.format("ERROR! No strafe target could be found when calling RANGE:AddStrafePit() for range %s", self.rangename) self:E(self.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) return end @@ -1238,7 +1238,6 @@ function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inversehe -- Debug info local text=string.format("Adding new strafe target %s with %d targets: heading = %03d, box_L = %.1f, box_W = %.1f, goodpass = %d, foul line = %.1f", _name, ntargets, heading, l, w, goodpass, foulline) self:T(self.id..text) - MESSAGE:New(text, 5):ToAllIf(self.Debug) return self end @@ -1567,13 +1566,13 @@ function RANGE:OnEventBirth(EventData) -- Debug output. local text=string.format("Player %s, callsign %s entered unit %s (UID %d) of group %s (GID %d)", _playername, _callsign, _unitName, _uid, _group:GetName(), _gid) self:T(self.id..text) - MESSAGE:New(text, 5):ToAllIf(self.Debug) -- Reset current strafe status. self.strafeStatus[_uid] = nil -- Add Menu commands after a delay of 0.1 seconds. - SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1) + --SCHEDULER:New(nil, self._AddF10Commands, {self,_unitName}, 0.1) + self:ScheduleOnce(0.1, self._AddF10Commands, self, _unitName) -- By default, some bomb impact points and do not flare each hit on target. self.PlayerSettings[_playername]={} --#RANGE.PlayerData @@ -1591,7 +1590,8 @@ function RANGE:OnEventBirth(EventData) -- Start check in zone timer. if self.planes[_uid] ~= true then - SCHEDULER:New(nil, self._CheckInZone, {self, EventData.IniUnitName}, 1, 1) + --SCHEDULER:New(nil, self._CheckInZone, {self, EventData.IniUnitName}, 1, 1) + self.timerCheckZone=TIMER:New(self._CheckInZone, self, EventData.IniUnitName):Start(1, 1) self.planes[_uid] = true end @@ -1674,15 +1674,12 @@ function RANGE:OnEventHit(EventData) if _unit and _playername then - -- Position of target. - local targetPos = _target:GetCoordinate() - - -- Message to player. - --local text=string.format("%s, direct hit on target %s.", self:_myname(_unitName), targetname) - --self:DisplayMessageToGroup(_unit, text, 10, true) - -- Flare target. if self.PlayerSettings[_playername].flaredirecthits then + + -- Position of target. + local targetPos = _target:GetCoordinate() + targetPos:Flare(self.PlayerSettings[_playername].flarecolor) end @@ -1724,9 +1721,6 @@ function RANGE:OnEventShot(EventData) self:T(self.id.."EVENT SHOT: Weapon name = ".._weaponName) self:T(self.id.."EVENT SHOT: Weapon cate = "..weaponcategory) - -- Special cases: - --local _viggen=string.match(_weapon, "ROBOT") or string.match(_weapon, "RB75") or string.match(_weapon, "BK90") or string.match(_weapon, "RB15") or string.match(_weapon, "RB04") - -- Tracking conditions for bombs, rockets and missiles. local _bombs = weaponcategory==Weapon.Category.BOMB --string.match(_weapon, "weapons.bombs") local _rockets = weaponcategory==Weapon.Category.ROCKET --string.match(_weapon, "weapons.nurs") @@ -1760,7 +1754,7 @@ function RANGE:OnEventShot(EventData) self:T(self.id..string.format("RANGE %s: Tracking %s - %s.", self.rangename, _weapon, EventData.weapon:getName())) -- Init bomb position. - local _lastBombPos = {x=0,y=0,z=0} + local _lastBombPos = {x=0,y=0,z=0} --DCS#Vec3 -- Function monitoring the position of a bomb until impact. local function trackBomb(_ordnance) @@ -1916,35 +1910,39 @@ end -- @param #string To To state. function RANGE:onafterStatus(From, Event, To) - local fsmstate=self:GetState() - - local text=string.format("Range status: %s", fsmstate) - - if self.instructor then - local alive="N/A" - if self.instructorrelayname then - local relay=UNIT:FindByName(self.instructorrelayname) - if relay then - alive=tostring(relay:IsAlive()) - end - end - text=text..string.format(", Instructor %.3f MHz (Relay=%s alive=%s)", self.instructorfreq, tostring(self.instructorrelayname), alive) - end - - if self.rangecontrol then - local alive="N/A" - if self.rangecontrolrelayname then - local relay=UNIT:FindByName(self.rangecontrolrelayname) - if relay then - alive=tostring(relay:IsAlive()) - end - end - text=text..string.format(", Control %.3f MHz (Relay=%s alive=%s)", self.rangecontrolfreq, tostring(self.rangecontrolrelayname), alive) - end + if self.verbose>0 then - - -- Check range status. - self:I(self.id..text) + local fsmstate=self:GetState() + + local text=string.format("Range status: %s", fsmstate) + + if self.instructor then + local alive="N/A" + if self.instructorrelayname then + local relay=UNIT:FindByName(self.instructorrelayname) + if relay then + alive=tostring(relay:IsAlive()) + end + end + text=text..string.format(", Instructor %.3f MHz (Relay=%s alive=%s)", self.instructorfreq, tostring(self.instructorrelayname), alive) + end + + if self.rangecontrol then + local alive="N/A" + if self.rangecontrolrelayname then + local relay=UNIT:FindByName(self.rangecontrolrelayname) + if relay then + alive=tostring(relay:IsAlive()) + end + end + text=text..string.format(", Control %.3f MHz (Relay=%s alive=%s)", self.rangecontrolfreq, tostring(self.rangecontrolrelayname), alive) + end + + + -- Check range status. + self:I(self.id..text) + + end -- Check player status. self:_CheckPlayers() @@ -2736,6 +2734,32 @@ function RANGE:_CheckInZone(_unitName) if _unit and _playername then + --- Function to check if unit is in zone and facing in the right direction and is below the max alt. + local function checkme(targetheading, _zone) + local zone=_zone --Core.Zone#ZONE + + -- Heading check. + local unitheading = _unit:GetHeading() + local pitheading = targetheading-180 + local deltaheading = unitheading-pitheading + local towardspit = math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90 + + if towardspit then + + local vec3=_unit:GetVec3() + local vec2={x=vec3.x, y=vec3.z} --DCS#Vec2 + local landheight=land.getHeight(vec2) + local unitalt=vec3.y-landheight + + if unitalt<=self.strafemaxalt then + local unitinzone=zone:IsVec2InZone(vec2) + return unitinzone + end + end + + return false + end + -- Current position of player unit. local _unitID = _unit:GetID() @@ -2747,18 +2771,8 @@ function RANGE:_CheckInZone(_unitName) -- Get the current approach zone and check if player is inside. local zone=_currentStrafeRun.zone.polygon --Core.Zone#ZONE_POLYGON_BASE - local unitheading = _unit:GetHeading() - local pitheading = _currentStrafeRun.zone.heading - 180 - local deltaheading = unitheading-pitheading - local towardspit = math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90 - local unitalt=_unit:GetHeight()-_unit:GetCoordinate():GetLandHeight() - - -- Check if unit is inside zone and below max height AGL. - local unitinzone=_unit:IsInZone(zone) and unitalt <= self.strafemaxalt and towardspit - - -- Debug output - local text=string.format("Checking still in zone. Unit = %s, player = %s in zone = %s. alt = %d, delta heading = %d", _unitName, _playername, tostring(unitinzone), unitalt, deltaheading) - self:T2(self.id..text) + -- Check if unit in zone and facing the right direction. + local unitinzone=checkme(_currentStrafeRun.zone.heading, zone) -- Check if player is in strafe zone and below max alt. if unitinzone then @@ -2858,22 +2872,10 @@ function RANGE:_CheckInZone(_unitName) for _,_targetZone in pairs(self.strafeTargets) do -- Get the current approach zone and check if player is inside. - local zonenname=_targetZone.name local zone=_targetZone.polygon --Core.Zone#ZONE_POLYGON_BASE - -- Check if player is in zone and below max alt and flying towards the target. - local unitheading = _unit:GetHeading() - local pitheading = _targetZone.heading - 180 - local deltaheading = unitheading-pitheading - local towardspit = math.abs(deltaheading)<=90 or math.abs(deltaheading-360)<=90 - local unitalt =_unit:GetHeight()-_unit:GetCoordinate():GetLandHeight() - - -- Check if unit is inside zone and below max height AGL. - local unitinzone=_unit:IsInZone(zone) and unitalt <= self.strafemaxalt and towardspit - - -- Debug info. - local text=string.format("Checking zone %s. Unit = %s, player = %s in zone = %s. alt = %d, delta heading = %d", _targetZone.name, _unitName, _playername, tostring(unitinzone), unitalt, deltaheading) - self:T2(self.id..text) + -- Check if unit in zone and facing the right direction. + local unitinzone=checkme(_targetZone.heading, zone) -- Player is inside zone. if unitinzone then @@ -2882,7 +2884,7 @@ function RANGE:_CheckInZone(_unitName) local _ammo=self:_GetAmmo(_unitName) -- Init strafe status for this player. - self.strafeStatus[_unitID] = {hits = 0, zone = _targetZone, time = 1, ammo=_ammo, pastfoulline=false } + self.strafeStatus[_unitID] = {hits = 0, zone = _targetZone, time = 1, ammo=_ammo, pastfoulline=false} -- Rolling in! local _msg=string.format("%s, rolling in on strafe pit %s.", self:_myname(_unitName), _targetZone.name) @@ -3089,11 +3091,9 @@ function RANGE:_GetAmmo(unitname) local text=string.format("Player %s has %d rounds ammo of type %s", playername, Nammo, Tammo) self:T(self.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) else local text=string.format("Player %s has %d ammo of type %s", playername, Nammo, Tammo) self:T(self.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) end end end diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 7efa0962d..7cd79f453 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -881,6 +881,9 @@ do -- Group Assignment local Mission = self:GetMission() local CommandCenter = Mission:GetCommandCenter() CommandCenter:SetMenu() + + self:MenuFlashTaskStatus( TaskGroup, false ) -- stop message flashing, if any #1383 & #1312 + end end @@ -1252,7 +1255,7 @@ function TASK:MenuFlashTaskStatus( TaskGroup, Flash ) self.FlashTaskStatus = Flash if self.FlashTaskStatus then - self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60 ) + self.FlashTaskScheduler, self.FlashTaskScheduleID = SCHEDULER:New( self, self.MenuTaskStatus, { TaskGroup }, 0, 60) --Issue #1383 never ending flash messages else if self.FlashTaskScheduler then self.FlashTaskScheduler:Stop( self.FlashTaskScheduleID ) diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 5ece5f005..8d825e309 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -344,7 +344,9 @@ function TASKINFO:Report( Report, Detail, ReportGroup, Task ) Text = DataText else local DataText = Data.Data -- #string - Text = DataText + if type(DataText) == "string" then --Issue #1388 - don't just assume this is a string + Text = DataText + end end if Line < math.floor( Data.Order / 10 ) then diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 1b305e6e9..d7f64e6aa 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -590,26 +590,29 @@ end --- Returns the POSITIONABLE heading in degrees. -- @param Wrapper.Positionable#POSITIONABLE self --- @return #number The POSITIONABLE heading --- @return #nil The POSITIONABLE is not existing or alive. +-- @return #number The POSITIONABLE heading in degrees or `nil` if not existing or alive. function POSITIONABLE:GetHeading() + local DCSPositionable = self:GetDCSObject() if DCSPositionable then local PositionablePosition = DCSPositionable:getPosition() + if PositionablePosition then local PositionableHeading = math.atan2( PositionablePosition.x.z, PositionablePosition.x.x ) + if PositionableHeading < 0 then PositionableHeading = PositionableHeading + 2 * math.pi end + PositionableHeading = PositionableHeading * 180 / math.pi - self:T2( PositionableHeading ) + return PositionableHeading end end - BASE:E( { "Cannot GetHeading", Positionable = self, Alive = self:IsAlive() } ) + self:E({"Cannot GetHeading", Positionable = self, Alive = self:IsAlive()}) return nil end