From e406fb0c8864897800243e006caf0de47d7f41c7 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 29 Dec 2024 21:02:56 +0100 Subject: [PATCH 01/31] Update Brigade.lua --- Moose Development/Moose/Ops/Brigade.lua | 75 ++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/Brigade.lua b/Moose Development/Moose/Ops/Brigade.lua index b8382f136..5734e0f9c 100644 --- a/Moose Development/Moose/Ops/Brigade.lua +++ b/Moose Development/Moose/Ops/Brigade.lua @@ -599,7 +599,80 @@ function BRIGADE:onafterStatus(From, Event, To) text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned)) end self:I(self.lid..text) - end + end + + if self.verbose>=3 then + + -- Count numbers + local Ntotal=0 + local Nspawned=0 + local Nrequested=0 + local Nreserved=0 + local Nstock=0 + + local text="\n===========================================\n" + text=text.."Assets:" + local legion=self --Ops.Legion#LEGION + + for _,_cohort in pairs(legion.cohorts) do + local cohort=_cohort --Ops.Cohort#COHORT + + for _,_asset in pairs(cohort.assets) do + local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem + + local state="In Stock" + if asset.flightgroup then + state=asset.flightgroup:GetState() + local mission=legion:GetAssetCurrentMission(asset) + if mission then + state=state..string.format(", Mission \"%s\" [%s]", mission:GetName(), mission:GetType()) + end + else + if asset.spawned then + env.info("FF ERROR: asset has opsgroup but is NOT spawned!") + end + if asset.requested and asset.isReserved then + env.info("FF ERROR: asset is requested and reserved. Should not be both!") + state="Reserved+Requested!" + elseif asset.isReserved then + state="Reserved" + elseif asset.requested then + state="Requested" + end + end + + -- Text. + text=text..string.format("\n[UID=%03d] %s Legion=%s [%s]: State=%s [RID=%s]", + asset.uid, asset.spawngroupname, legion.alias, cohort.name, state, tostring(asset.rid)) + + + if asset.spawned then + Nspawned=Nspawned+1 + end + if asset.requested then + Nrequested=Nrequested+1 + end + if asset.isReserved then + Nreserved=Nreserved+1 + end + if not (asset.spawned or asset.requested or asset.isReserved) then + Nstock=Nstock+1 + end + + Ntotal=Ntotal+1 + + end + + end + text=text.."\n-------------------------------------------" + text=text..string.format("\nNstock = %d", Nstock) + text=text..string.format("\nNreserved = %d", Nreserved) + text=text..string.format("\nNrequested = %d", Nrequested) + text=text..string.format("\nNspawned = %d", Nspawned) + text=text..string.format("\nNtotal = %d (=%d)", Ntotal, Nstock+Nspawned+Nrequested+Nreserved) + text=text.."\n===========================================" + self:I(self.lid..text) + end end From 7d3bffcfef264adab5551b32b69be7e0e8d322ea Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 9 Apr 2025 22:36:26 +0200 Subject: [PATCH 02/31] Naviation - Added beacons class - Added navpoints class - Added vector class --- Moose Development/Moose/Core/Vector.lua | 1200 +++++++++++++++++ Moose Development/Moose/Modules.lua | 4 + Moose Development/Moose/Modules_local.lua | 4 + .../Moose/Navigation/Beacons.lua | 139 ++ Moose Development/Moose/Navigation/Point.lua | 587 ++++++++ 5 files changed, 1934 insertions(+) create mode 100644 Moose Development/Moose/Core/Vector.lua create mode 100644 Moose Development/Moose/Navigation/Beacons.lua create mode 100644 Moose Development/Moose/Navigation/Point.lua diff --git a/Moose Development/Moose/Core/Vector.lua b/Moose Development/Moose/Core/Vector.lua new file mode 100644 index 000000000..3ee2cc1e7 --- /dev/null +++ b/Moose Development/Moose/Core/Vector.lua @@ -0,0 +1,1200 @@ +--- **CORE** - Vector algebra. +-- +-- **Main Features:** +-- +-- * Easy vector algebra function +-- * Redefinition of `+`, `-`, `*`, `/`, `%` operators to be compatible with vectors +-- * Interface to DCS API functions +-- * Reduced confusion of DCS coordinate system +-- * Better performance than related classes (COORDINATE, POINT_VEC) +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Core%20-%20Vector). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Core.Vector +-- @image CORE_Vector.png + + +--- VECTOR class. +-- @type VECTOR +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #number x Component pointing North if > 0 and South if < 0. +-- @field #number y Component pointing up if >0 and down if < 0. This describes the altitude above main sea level. +-- @field #number z Component pointing East if > 0 and West if < 0. + +--- *Mathematics knows no races or geographic boundaries; for mathematics, the cultural world is one country.* --David Hilbert +-- +-- === +-- +-- # The VECTOR Concept +-- +-- The VECTOR class has a great concept! +-- +-- https://github.com/automattf/vector.lua/blob/master/vector.lua +-- +-- # The DCS Coordinate System +-- +-- DCS has a rather unconventional way to define the coordinate system. The definition even depends whether you work with a 2D vector or a 3D vector. +-- The good think is, that you usually do not need to worry about this unless you directly call the DCS API functions. Plus, this class tries to +-- hide the differences between 2D and 3D conventions as good as possible with internal if-cases. +-- +-- Still, it is important to unterstand the differences. So let us explain: +-- +-- Usually, we draw a coordinate system in 2D and label the horizonal axis (pointing right) as the x-axis. The vertical axis (poining up on a piece of paper) +-- +-- ## 3D +-- +-- The x-axis points North. The z-axis points East. The y-axis points upwards and defines the altitue with respect to the mean sea level. +-- +-- ## 2D +-- +-- The x-axis points North (just like in the 3D) case. The y-axis points East. +-- +-- # Constructors +-- +-- There are different ways to create a new instance of a VECTOR. All methods start with `New`. +-- +-- ## From Components +-- +-- ## From 2D or 3D Vectors +-- +-- ## From Polar Coordinates +-- +-- ## From Spherical Coordinates +-- +-- +-- # Operators +-- +-- ## Addition [MATH] `+` +-- +-- ## Subtraction [MATH] `-` +-- +-- ## Multiplication [MATH] `*` +-- +-- ## Devision [MATH] `/` +-- +-- ## Modulo [MATH] `%` +-- +-- +-- # DCS API Interface +-- +-- This class offers easy and convenient ways to access all vector related DCS API functions. +-- +-- ## Land and Surface +-- +-- ## Atmosphere +-- +-- ## Effects and Actions +-- +-- ## Map Markings +-- +-- ## Coordinates +-- +-- # Inferface to other MOOSE Classes +-- +-- Of course, this class is interfaced with other MOOSE classes in the sense that you can obtain and work with VECTOR instances from MOOSE objects, from which a position or direction +-- vector can be derived. +-- +-- ## GROUP +-- +-- ## UNIT +-- +-- ## STATIC +-- +-- ## SCENERY +-- +-- ## ZONE +-- +-- # Examples +-- +-- A new `VECTOR` object can be created with the @{#VECTOR.New} function. +-- Here we create two vectors, a and b, and add them to create a new vector c. +-- +-- local a=VECTOR:New(1, 0) +-- local b=VECTOR:New(0, 1) +-- local c=a+b +-- +-- This is how it works. +-- +-- @field #VECTOR +VECTOR = { + ClassName = "VECTOR", + verbose = 0, +} + +--- VECTOR class version. +-- @field #string version +VECTOR.version="0.0.2" + +--- VECTOR private index. +-- @field #VECTOR __index +VECTOR.__index = VECTOR + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: 3D rotation +-- TODO: Markers +-- TODO: Documentation + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new VECTOR class instance from given cartesian coordinates `x`, `y` and `z`, where `z` is optional. +-- **Note** that whether the `z` component is passed or nil has great impact on the interpretation of the `y` component. +-- If `z` is not given (`nil`), then `y` is used as `z` (the coordinate pointing East) because we always constuct a 3D vector. +-- If `z` is given (not `nil`), then `y` is used as coordinate pointing up (out of plane) as for all 3D DCS vectors. +-- @param #VECTOR self +-- @param #number x Component of vector along x-axis (pointing North in both 2D and 3D). +-- @param #number y Component of vector along y-axis (pointing East in 2D and Up in 3D). +-- @param #number z (Optional) Component of the z-axis (pointing East in 3D). +-- @return #VECTOR self +function VECTOR:New(x, y, z) + + if z==nil then + -- z-component is not given ==> 2D ==> we take y as z and set y=0. + self=setmetatable({x=x or 0, y=0, z=y or 0}, VECTOR) + else + self=setmetatable({x=x or 0, y=y or 0, z=z or 0}, VECTOR) + end + + return self +end + +--- Create a new VECTOR class instance from given 2D or 3D vector object. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec Vector object with `x`, `y` and optionally `z` components. Can be a DCS#Vec2, DCS#Vec3, `COORDINATE`, `VECTOR` object. +-- @return #VECTOR self +function VECTOR:NewFromVec(Vec) + + -- The :New function takes care whether this is a 2D or 3D vector. + local vector=VECTOR:New(Vec.x, Vec.y, Vec.z) + + return vector +end + +--- Create a new VECTOR class instance from polar coordinates (r, phi). +-- @param #VECTOR self +-- @param #number r Distance. +-- @param #number phi Angle in Degrees. Note that 0° corresponds to North, 90° East etc. +-- @return #VECTOR self +function VECTOR:NewFromPolar(r, phi) + + -- Convert deg to rad. + local Phi=math.rad(phi) + + -- Polar coordinates. What we want in DCS is: + -- North: Phi=0 ==> x= 1, y= 0 + -- East : Phi=90 ==> x= 0, y= 1 + -- South: Phi=180 ==> x=-1, y= 0 + -- West : Phi=270 ==> x= 0, y=-1 + + -- sin(0)=0 sin(90)=1, sin(180)=0, sin(270)=-1 ==> y + -- cos(0)=1 cos(90)=0, cos(180)=-1, cos(270)=0 ==> x + + local x=r*math.cos(phi) + local y=r*math.sin(phi) + + -- Create new vector. As z is nil, the 2D character is taken care of. + local v=VECTOR:New(x, y) + + return self +end + +--- Create a new VECTOR class instance from spherical coordinates (r, theta, phi). +-- @param #VECTOR self +-- @param #number r Distance in meters with r>=0. +-- @param #number theta Polar angle in Degrees measured from a fixed polar axis or zenith direction. This angle is in [0°, 180°]. +-- @param #number phi Azimuthal angle in Degrees. This angle is in [0°, 360°). +-- @return #VECTOR self +function VECTOR:NewFromSpherical(r, theta, phi) + + local sinPhi=math.sin(math.rad(phi)) + local cosPhi=math.cos(math.rad(phi)) + local sinTheta=math.sin(math.rad(theta)) + local cosTheta=math.cos(math.rad(theta)) + + --TODO: Check x,y,z convention for DCS. + local x=r*sinTheta*cosPhi + local y=r*sinTheta*sinPhi + local z=r*cosTheta + + local v=VECTOR:New(x, y, z) + + return self +end + +--- Get the directional vector that points from a given vector `a` to another given vector `b`. +-- The vector is `c=-a+b=b-a`. +-- @param #VECTOR self +-- @param #VECTOR a Vector a. This can also be given as any table with x, y and z components (z optional). +-- @param #VECTOR b Vector b. This can also be given as any table with x, y and z components (z optional). +-- @return #VECTOR Directional vector from a to b. +function VECTOR:NewDirectionalVector(a, b) + + local x=b.x-a.x + local y + local z + + if a.z and b.z then + -- Both given vectors are 3D + y=b.y-a.y + z=b.z-a.z + elseif b.z then + -- a is 2D and b is 3D + y=b.y-0 + z=b.z-a.y + elseif a.z then + -- a is 3D and b is 2D + y=0-a.y + z=b.y-a.z + else + -- a is 2D and b is 2D + y=b.y-a.y + z=nil --We leave z=nil, so the New function takes care of the 2D character. + end + + local c=VECTOR:New(x, y, z) + + return c +end + + +--- Creates a new VECTOR instance from given the latitude and longitude in decimal degrees (DD). +-- @param #VECTOR self +-- @param #number Latitude Latitude in decimal degrees. +-- @param #number Longitude Longitude in decimal degrees. +-- @param #number altitude (Optional) Altitude in meters. Default is the land height at the 2D position. +-- @return #VECTOR self +function VECTOR: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. + self=VECTOR:NewFromVec(vec3) + +-- -- Adjust height +-- if Altitude==nil then +-- self.y=self:GetSurfaceHeight() +-- else +-- self.y=Altitude +-- end + + return self +end + +--- Creates a new VECTOR instance from given latitude and longitude in degrees, minutes and seconds (DMS). +-- **Note** that latitude and longitude are passed as strings and the characters `°`, `'` and `"` are important. +-- @param #VECTOR self +-- @param #string Latitude Latitude in DMS as string, e.g. "`42° 24' 14.3"`". +-- @param #string Longitude Longitude in DMS as string, e.g. "`42° 24' 14.3"`". +-- @param #number Altitude (Optional) Altitude in meters. Default is the land height at the coordinate. +-- @return #VECTOR +function VECTOR:NewFromLLDMS(Latitude, Longitude, Altitude) + + local lat=UTILS.LLDMSstringToDD(Latitude) + local lon=UTILS.LLDMSstringToDD(Longitude) + + self=VECTOR:NewFromLLDD(lat, lon, Altitude) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get 2D vector as simple table. +-- @param #VECTOR self +-- @return DCS#Vec2 2D array {x, y}. +function VECTOR:GetVec2() + + local vec={x=self.x, y=self.z} + + return vec +end + +--- Get 3D vector as simple table. +-- @param #VECTOR self +-- @param #boolean OnSurface If `true`, the `y` component is set the land height [m] of the 2D position. +-- @return DCS#Vec3 3D array {x=x, y=y, z=z}. +function VECTOR:GetVec3(OnSurface) + + local x=self.x + local y=OnSurface and land.getHeight({x=self.x, y=self.z}) or self.y + local z=self.z + + local vec={x=x, y=y, z=z} --DCS#Vec3 + + return vec +end + +--- Get a COORDINATE object. +-- @param #VECTOR self +-- @param #boolean OnSurface If `true`, the `y` component is set the land height [m] of the 2D position. +-- @return Core.Point#COORDINATE The COORDINATE object. +function VECTOR:GetCoordinate(OnSurface) + + local vec3=self:GetVec3(OnSurface) + + local coordinate=COORDINATE:NewFromVec3(vec3) + + return coordinate +end + +--- Get the distance from this vector to another vector. +-- @param #VECTOR self +-- @param #VECTOR Vector Vector to which the distance is requested. +-- @param #boolean Only2D If `true`, calculate only the projected 2D distance. +-- @return #number Distance in meters. +function VECTOR:GetDistance(Vector, Only2D) + + local dx=self.x-Vector.x + local dy=0 + local dz=0 + + if Vector.z then + if not Only2D then + dy=self.y-Vector.y + end + dz=self.z-Vector.z + else + -- Given vector is 2D. + dy=0 + dz=self.z-Vector.y + end + + -- Calculate the distance. + local dist=math.sqrt( dx*dx + dy*dy + dz*dz ) + + return dist +end + +--- Get the directional vector that points from this VECTOR to another VECTOR `a`. +-- @param #VECTOR self +-- @param #VECTOR a Vector `a`. This can also be given as any table with `x`, `y` and `z` components, where `z` is optional. +-- @return #VECTOR Directional vector from this vector to `a`. +function VECTOR:GetDirectionalVectorTo(a) + + local x=a.x-self.x + local y=0 + local z=nil + + if a.z then + -- a is 3D + y=a.y-self.y + z=a.z-self.z + else + -- a is 2D + y=a.y-self.z + z=nil --We leave z=nil, so the New function takes care of the 2D character. + end + + local c=VECTOR:New(x, y, z) + + return c +end + +--- Get the directional vector that points from another VECTOR `a` to this VECTOR. +-- @param #VECTOR self +-- @param #VECTOR a Vector a. This can also be given as any table with x,y and z components (z optional). +-- @return #VECTOR Directional vector from self to a. +function VECTOR:GetDirectionalVectorFrom(a) + + local x=self.x-a.x + local y + local z + + if a.z then + -- a is 3D + y=self.y-a.y + z=self.z-a.z + else + -- a is 2D ==> we work in 2D and take z component of self + y=self.z-a.y + z=nil --We leave z=nil, so the New function takes care of the 2D character. + end + + local c=VECTOR:New(x, y, z) + + return c +end + +--- Get length/norm/magnitude of this vector. +-- @param #VECTOR self +-- @return #number Length of vector. +function VECTOR:GetLength() + + local l=math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z) + + return l +end + +--- Get heading of vector. +-- Note that a heading of +-- +-- * 000° = North +-- * 090° = East +-- * 180° = South +-- * 270° = West. +-- +-- @param #VECTOR self +-- @param #boolean To360 If `true` or `nil`, adjust heading to [0,360) range. If `false`, headings not in this range can occur. +-- @return #number Heading in degrees. +function VECTOR:GetHeading(To360) + + -- Get heading. + local heading=math.atan2(self.z, self.x) + + -- Convert to degrees. + heading=math.deg(heading) + + + if To360==nil or To360==true then + -- Adjust heading so it is in [0,360). + heading=UTILS.AdjustHeading360(heading) + else + -- Just make sure 360 is returned a 0. + if heading==360.0 then + heading=0.0 + end + end + + return heading +end + +--- Get the heading from this vector to another given vector. +-- @param #VECTOR self +-- @param #VECTOR Vector Vector to which the heading is requested. +-- @return #number Heading from this vector to the other vector in degrees. +function VECTOR:GetHeadingTo(Vector) + + -- Get directional vector from given vector to this vector. + local a=self:GetDirectionalVectorTo(Vector) + + -- Get heading of directional vector. + local heading=math.deg(math.atan2(a.z, a.x)) + + -- Adjust heading so it is in [0,360). + heading=UTILS.AdjustHeading360(heading) + + return heading +end + +--- Get the heading from a given vector to this vector. +-- @param #VECTOR self +-- @param #VECTOR Vector Vector from which the heading is requested. +-- @return #number Heading from the other vector to this vector in degrees. +function VECTOR:GetHeadingFrom(Vector) + + -- Get directional vector from given vector to this vector. + local a=self:GetDirectionalVectorFrom(Vector) + + -- Get heading of directional vector. + local heading=math.deg(math.atan2(a.z, a.x)) + + -- Adjust heading so it is in [0,360). + heading=UTILS.AdjustHeading360(heading) + + return heading +end + + +--- Get latitude and longitude of this vector. +-- @param #VECTOR self +-- @return #number Latitude in decimal degrees (DD). +-- @return #number Longitude in decimal degrees (DD). +function VECTOR:GetLatitudeLongitude() + + local vec3=self:GetVec3() + + local latitude, longitude, altitude=coord.LOtoLL(vec3) + + return latitude, longitude +end + + +--- Get MGRS coordinates of this vector. +-- @param #VECTOR self +-- @param #VECTOR Vector Vector from which the heading is requested. +-- @return #number Latitude +-- @return #number Longitude +function VECTOR:GetMGRS() + + local lat, long=self:GetLatitudeLongitude() + + local mrgs=coord.LLtoMGRS(lat, long) + + return mrgs.Easing, mrgs.Northing +end + +--- Get the difference of the heading of this vector w. +-- +-- **Example 1:** +-- This vector has a heading of 90° (pointing East) and the other vector has a heading of 225° (pointing South-West), +-- we would optain a delta of 225°-90°=135°. +-- +-- **Example 2:** +-- This vector has a heading of 180 (pointing South) and the other vector has a heading of 90° (pointing East), +-- we would optain a delta of 90°-180°=-90°. +-- +-- @param #VECTOR self +-- @param #VECTOR Vector Vector to which the heading is requested. +-- @return #number Heading from this vector to the other vector in degrees. +function VECTOR:GetHeadingDelta(Vector) + + local h1=self:GetHeading(false) + + local h2=Vector:GetHeading(false) + + local delta=h2-h1 + + return delta +end + +--- Return an intermediate VECTOR between this and another given vector. +-- @param #VECTOR self +-- @param #VECTOR Vector The destination vector. +-- @param #number Fraction The fraction (0,1) where the new vector is created. Default 0.5, *i.e.* in the middle. +-- @return #VECTOR Vector between this and the other vector. +function VECTOR:GetIntermediateCoordinate(Vector, Fraction) + + local f=Fraction or 0.5 + + -- Get the directional vector to the given Vector. + local vec=self:GetDirectionalVectorTo(Vector) + + -- Get the length of the vector. + local length=vec:GetLength() + + -- Set/scale the length. + vec:SetLength(f*length) + + --TODO: Not sure what this was supposed to do?! +-- if f>1 then +-- local norm=UTILS.VecNorm(vec) +-- f=Fraction/norm +-- end + + -- Get to the desired position. + vec=self+vec + + return vec +end + + + +--- Set length/norm/magnitude of this vector. +-- @param #VECTOR self +-- @param #number Length Desired length of vector. +function VECTOR:SetLength(Length) + + -- Normalize this vector to a length of 1. + self:Normalize() + + -- Scale the vector to the desired length. + local v=self*Length + + -- Replace with scaled version. + self:Replace(v) + + return self +end + +--- Set x-component of vector. The x-axis points to the North. +-- @param #VECTOR self +-- @param #number x Value of x. Default 0. +-- @return #VECTOR self +function VECTOR:SetX(x) + self.x=x or 0 + return self +end + +--- Set y-component of vector. The y-axis points to the upwards and describes the altitude above mean sea level. +-- @param #VECTOR self +-- @param #number y Value of y. Default land/surface height at this point. +-- @return #VECTOR self +function VECTOR:SetY(y) + + if y==nil then + y=self:GetSurfaceHeight() + end + self.y=y + return self +end + +--- Set z-component of vector. The z-axis points to the East. +-- @param #VECTOR self +-- @param #number z Value of z. Default 0. +-- @param #VECTOR self +function VECTOR:SetZ(z) + self.z=z or 0 + return self +end + + + +--- Add a vector to this. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. +-- Note that if you want to add a VECTOR, you can also simply use the `+` operator. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec Vector to add. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. +-- @return #VECTOR self +function VECTOR:AddVec(Vec) + + self.x=self.x+Vec.x + + if Vec.z then + self.y=self.y+Vec.y + self.z=self.z+Vec.z + else + -- Vec is 2D ==> we take its y-component. + self.z=self.z+Vec.y + end + + return self +end + +--- Substract a vector from this one. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. +-- Note that if you want to add a VECTOR, you can also simply use the `-` operator. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec Vector to substract. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. +-- @return #VECTOR self +function VECTOR:SubVec(Vec) + + self.x=self.x-Vec.x + + if Vec.z then + self.y=self.y-Vec.y + self.z=self.z-Vec.z + else + -- Vec is 2D ==> we take its y-component. + self.z=self.z-Vec.y + end + + return self +end + +--- Calculate the dot product of this VECTOR with another vector. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec The other vector. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. +-- @return #number Dot product Sum_i(a[i]*b[i]). Note that this is a **scalar** and not a vector any more! +function VECTOR:Dot(Vec) + + local dot=self.x*Vec.x + if Vec.z then + dot=dot+self.y*Vec.y+self.z*Vec.z + else + -- Vec is 2D ==> we take its y-component for z. + dot=dot+self.z*Vec.y + end + + return dot +end + +--- Calculate the rotation or cross product of this VECTOR with another vector. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. +-- @param #VECTOR self +-- @param DCS#Vec3 Vec The other vector. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. +-- @return #VECTOR The cross product vector. +function VECTOR:Rot(Vec) + + -- TODO: + local dot=self.x*Vec.x + if Vec.z then + dot=dot+self.y*Vec.y+self.z*Vec.z + else + -- Vec is 2D ==> we take its y-component for z. + dot=dot+self.z*Vec.y + end + + return dot +end + + +--- Get a clone (deep copy) of this vector. +-- @param #VECTOR self +-- @return #VECTOR Copy of the vector. +function VECTOR:Copy() + + local c=VECTOR:New(self.x, self.y, self.z) + + return c +end + +--- Replace this vector with another one. +-- If the given vector is 2D, we +-- @param #VECTOR self +-- @param #VECTOR Vector The vector that is used to replace this vector. +-- @param #boolean Project2D If `true` and the given vector is 2D, we project the updated vector to 2D (`y=0`). Otherwise, we leave `y` untouched. +-- @return #VECTOR self updated +function VECTOR:Replace(Vector, Project2D) + + self.x=Vector.x + + if Vector.z then + -- Given vector is 3D + self.y=Vector.y + self.z=Vector.z + else + -- Given vector is 2D + if Project2D then + self.y=0 + end + self.z=Vector.y + end + + return self +end + +--- Normalize this vector, so that has a length of 1. +-- @param #VECTOR self +-- @return #VECTOR self +function VECTOR:Normalize() + + local l=self:GetLength() + + if l~=0 then + self:Replace(self/l) + end + + return self +end + +--- Translate the vector by a given distance and angle. +-- @param #VECTOR self +-- @param #number Distance Distance in meters. Default 1000 meters. +-- @param #number Heading Heading angle in degrees. Default 0° = North. +-- @param #boolean Copy Create a copy of the VECTOR so the original stays unchanged. +-- @return #VECTOR The translated vector or a copy of it. +function VECTOR:Translate(Distance, Heading, Copy) + + -- Set default distance if not passed. + Distance=Distance or 1000 + + -- Angle in rad. + local alpha = math.rad(Heading or 0) + + -- Create a copy if requested. + local vector=Copy and self:Copy() or self + + -- Set new coordinates. + vector.x = Distance * math.cos(alpha) + vector.x -- New x + vector.z = Distance * math.sin(alpha) + vector.z -- New z + + return vector +end + +--- Rotate the VECTOR clockwise in the 2D (x,z) plane. +-- @param #VECTOR self +-- @param #number Angle Rotation angle in degrees). Default 0. +-- @param #boolean Copy Create a copy of the VECTOR so the original stays unchanged. +-- @return #VECTOR The translated vector or a copy of it. +function VECTOR:Rotate2D(Angle, Copy) + + -- Angle in rad. + local phi = -math.rad(Angle or 0) + + -- Sin/Cos of angle. + local sinPhi = math.sin(phi) + local cosPhi = math.cos(phi) + + -- Get more convenient notation. + local X=self.z + local Y=self.x + + -- Apply rotation matrix. + local z = X*cosPhi - Y*sinPhi + local x = X*sinPhi + Y*cosPhi + + -- Create new vector. + -- TODO: Copy argument + local vector=VECTOR:New(x, self.y, z) + + return vector +end + + + +--- Provides an MGRS string. +-- @param #VECTOR 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 MGRS text. +function VECTOR:ToStringMGRS(Settings) + + -- Get Accuracy. + local MGRS_Accuracy = Settings and Settings.MGRS_Accuracy or _SETTINGS.MGRS_Accuracy + + local lat, lon = coord.LOtoLL( self:GetVec3() ) + + local MGRS = coord.LLtoMGRS( lat, lon ) + + local text="MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy ) + + return text +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- DCS API Wrapper Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get the surface type at the vector. +-- +-- * LAND = 1 +-- * SHALLOW_WATER = 2 +-- * WATER = 3 +-- * ROAD = 4 +-- * RUNWAY = 5 +-- +-- @param #VECTOR self +-- @return #number Surface Type +function VECTOR:GetSurfaceType() + + local vec2=self:GetVec2() + + local s=land.getSurfaceType(vec2) + + return s +end + +--- Check if a given vector has line of sight with this vector. +-- @param #VECTOR self +-- @param #VECTOR Vec The other vector. +-- @return #number Surface Type +function VECTOR:IsVisible(Vec) + + local vec1=self:GetVec3() + + local vec2={x=Vec.x, Vec.y, Vec.z} + + local los=land.isVisible(vec1, vec2) + + return los +end + +--- Get a vector on the closest road. +-- @param #VECTOR self +-- @param #VECTOR Vec The other vector. +-- @return #VECTOR Closest vector on a road. +function VECTOR:GetClosestRoad() + + local vec2=self:GetVec2() + + local x,y=land.getClosestPointOnRoads('roads', vec2.x, vec2.y) + + local road=nil + if x and y then + road=VECTOR:New(x, y) + end + + return road +end + + +--- Get the path on road from this vector to a given other vector. +-- @param #VECTOR self +-- @param #VECTOR Vec The destination vector. +-- @return Core.Path#PATHLINE Pathline with points on road. +function VECTOR:GetPathOnRoad(Vec) + + local vec2=self:GetVec2() + + local path=nil + + local vec2points=land.findPathOnRoads("roads", vec2.x , vec2.y, vec2.x, vec2.y) + + if vec2points then + path=PATHLINE:NewFromVec2Array("Road", vec2points) + end + + return path +end + + +--- Get profile of the land between the two passed points. +-- @param #VECTOR self +-- @param #VECTOR Vec3 The 3D destination vector. If a 2D vector is passed, `y` is set to the land height. +-- @return Core.Path#PATHLINE Pathline with points of the profile. +function VECTOR:GetProfile(Vec3) + + local vec3=self:GetVec3() + + -- Get profile + local vec3s=land.profile(vec3, Vec3) + + local profile=nil + if vec3s then + + profile=PATHLINE:NewFromVec3Array("Profile", vec3s) + + end + + return profile +end + +--- Returns an intercept point at which a ray drawn from the this vector in the passed normalized direction for a specified distance. +-- @param #VECTOR self +-- @param DCS#Vec3 DirectionVector Directional vector. +-- @param #number Distance Distance in meters. Default 1000 m. +-- @return #VECTOR Intercept vector. Can be `nil` if no intercept point is found. +function VECTOR:GetInterceptPoint(DirectionVector, Distance) + + local vec3=self:GetVec3() + + local ip3=land.getIP(vec3, DirectionVector, Distance or 1000) --DCS#Vec3 + + local ipvector=nil + + if ip3 then + ipvector=VECTOR:New(ip3.x, ip3.y , ip3.z) + end + + return ipvector +end + +--- Returns the distance from sea level at this vector. +-- @param #VECTOR self +-- @return #number Distance above sea leavel in meters. +function VECTOR:GetSurfaceHeight() + + local vec2=self:GetVec2() + + local h=land.getHeight(vec2) + + return h +end + + +--- Returns the distance from sea level at this vector. +-- @param #VECTOR self +-- @return #number Heigh above sea leavel in meters. +-- @return #number Depth (positive) at this point in meters. +function VECTOR:GetSurfaceHeightAndDepth() + + local vec2=self:GetVec2() + + local h,d=land.getSurfaceHeightWithSeabed(vec2) + + return h,d +end + +--- Returns a velocity vector of the wind at this vector. Turbolences can be optionally be included. +-- @param #VECTOR self +-- @param #boolean WithTurbulence If `true`, return wind including turbulence. +-- @return #VECTOR Velocity 3D vector [m/s] the wind is blowing to. +function VECTOR:GetWindVector(WithTurbulence) + + local vec3=self:GetVec3() + + local wind=nil + if WithTurbulence then + wind=atmosphere.getWindWithTurbulence(vec3) + + else + wind=atmosphere.getWind(vec3) + end + + local vector=VECTOR:New(wind) + + return vector +end + +--- Returns a temperature and pressure at this vector. +-- @param #VECTOR self +-- @return #number Temperatur in Kelvin. +-- @return #number Pressure in Pascals. +function VECTOR:GetTemperaturAndPressure() + + local vec3=self:GetVec3() + + local t,p=atmosphere.getTemperatureAndPressure(vec3) + + return t,p +end + + +--- Creates a large smoke and fire effect of a specified type and density at this vector. +-- @param #VECTOR self +-- @param #number Preset Preset of smoke. Default `BIGSMOKEPRESET.LargeSmokeAndFire`. +-- @param #number Density Density between [0,1]. Default 0.5. +-- @return #string Name of the smoke. Can be used to stop it. +function VECTOR:SmokeAndFire(Preset, Density) + + Preset=Preset or BIGSMOKEPRESET.LargeSmokeAndFire + Density=Density or 0.5 + + local vec3=self:GetVec3() + + --TODO: Get a unique name or pass name as parameter? + + trigger.action.effectSmokeBig(vec3, Preset, Density, Name) + + return Name +end + +--- Creates an illumination bomb at the specified point. +-- @param #VECTOR self +-- @param #number Power The power in Candela (cd). Should be between 1 and 1000000. Default 1000 cd. +-- @return #VECTOR self +function VECTOR:IlluminationBomb(Power) + + local vec3=self:GetVec3() + + trigger.action.illuminationBomb(vec3, Power or 1000) + +end + +--- Creates an explosion at a given point at the specified power. +-- @param #VECTOR self +-- @param #number Power The power in kg TNT. Default 100 kg. +-- @return #VECTOR self +function VECTOR:Explosion(Power) + + local vec3=self:GetVec3() + + trigger.action.explosion(vec3, Power or 100) + + return self +end + +--- Creates a signal flare at the given point in the specified color. The flare will be launched in the direction of the azimuth angle. +-- @param #VECTOR self +-- @param #number Color Color of flare. Default Green. +-- @param #number Azimuth Azimuth angle in degrees. Default 0. +-- @return #VECTOR self +function VECTOR:Flare(Color, Azimuth) + + local vec3=self:GetVec3() + + trigger.action.signalFlare(vec3, Color or 0, math.rad(Azimuth or 0)) + + return self +end + + +--- Creates a arrow from this VECTOR to another vector on the F10 map. +-- @param #VECTOR self +-- @param #VECTOR Vector The vector defining the endpoint. +-- @return #VECTOR self +function VECTOR:ArrowToAll(Vector) + + local vec3Start=self:GetVec3() + + trigger.action.arrowToAll(coalition , id, vec3Start, vec3End, color, fillColor , lineType, readOnly, "") + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Check if passed object is a Vector. +-- @param #table t The object to be tested. +-- @return #boolean Returns `true` if `t` is a #VECTOR and `false` otherwise. +function VECTOR._IsVector(t) + return getmetatable(t) == VECTOR +end + +--- Meta function to add vectors together. +-- @param #VECTOR a Vector a. +-- @param #VECTOR b Vector b. +-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]+b[i] for i=x,y,z. +function VECTOR.__add(a,b) + + assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "ERROR in VECTOR.__add: wrong argument types! (expected and )") + + local c=VECTOR:New(a.x+b.x, a.y+b.y, a.z+b.z) + + return c +end + +--- Meta function to substract vectors. +-- @param #VECTOR a Vector a. +-- @param #VECTOR b Vector b. +-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]-b[i] for i=x,y,z. +function VECTOR.__sub(a,b) + + assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "ERROR in VECTOR.__sub: wrong argument types: (expected and )") + + local c=VECTOR:New(a.x-b.x, a.y-b.y, a.z-b.z) + + return c +end + + +--- Meta function to multiplicate vector by another vector or a scalar. +-- @param #VECTOR a Vector a. Can also be a #number. +-- @param #VECTOR b Vector b. Can also be a #number. +-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]*b[i] for i=x,y,z. +function VECTOR.__mul(a, b) + + local c=nil --#VECTOR + + if type(a)=='number' then + c=VECTOR:New(a*b.x, a*b.y, a*b.z) + elseif type(b)=='number' then + c=VECTOR:New(b*a.x, b*a.y, b*a.z) + else + c=VECTOR:New(a.x*b.x, a.y*b.y, a.z*b.z) + end + + return c +end + +--- Meta function for dividing vectors by scalars. +-- @param #VECTOR a Vector a. +-- @param #number b Number by which the components of the vector are divided. +-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]/b for i=x,y,z. +function VECTOR.__div(a, b) + + assert(VECTOR._IsVector(a) and type(b) == "number", "div: wrong argument types (expected and )") + + env.info("FF __div") + + local c=VECTOR:New(a.x/b, a.y/b, a.z/b) + + return c +end + +--- Meta function to make vectors negative. +-- @param #VECTOR v Vector v. +function VECTOR.__unm(v) + local c=VECTOR:New(-v.x, -v.y, -v.z) + return c +end + +--- Meta function to check if two vectors are equal. +-- @param #VECTOR a Vector a. +-- @param #VECTOR b Vector b. +-- @return #boolean If `true`, both vectors are equal +function VECTOR.__eq(a, b) + --assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "ERROR in VECTOR.__eq: wrong argument types: (expected and )") + env.info("FF __eq",showMessageBox) + BASE:I(a) + BASE:I(b) + return a.x==b.x and a.y==b.y and a.z==b.z +end + + +--- Meta function to change how vectors appear as string. +-- @param #VECTOR self +-- @return #string String representation of vector. +function VECTOR:__tostring() + local text=string.format("(x=%.1f, y=%.1f, z=%.1f) |v|=%.1f Phi=%4.1f°", self.x, self.y, self.z, self:GetLength(), self:GetHeading(false)) + return text +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 37c6ac45c..fecf7f271 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -32,6 +32,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/MarkerOps_Base.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Pathline.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/ClientMenu.lua') +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Vector.lua') __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Object.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.lua' ) @@ -186,4 +187,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Cargo_Dispatcher __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Zone.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatcher.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Point.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.lua' ) + __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Globals.lua' ) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 15c6bcba5..15e1b3b05 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -32,6 +32,7 @@ __Moose.Include( 'Core\\MarkerOps_Base.lua' ) __Moose.Include( 'Core\\TextAndSound.lua' ) __Moose.Include( 'Core\\Condition.lua' ) __Moose.Include( 'Core\\ClientMenu.lua' ) +__Moose.Include( 'Core\\Vector.lua' ) __Moose.Include( 'Wrapper\\Object.lua' ) __Moose.Include( 'Wrapper\\Identifiable.lua' ) @@ -178,4 +179,7 @@ __Moose.Include( 'Tasking\\Task_Cargo_Dispatcher.lua' ) __Moose.Include( 'Tasking\\Task_Capture_Zone.lua' ) __Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' ) +__Moose.Include( 'Navigation\\Point.lua' ) +__Moose.Include( 'Navigation\\Beacons.lua' ) + __Moose.Include( 'Globals.lua' ) diff --git a/Moose Development/Moose/Navigation/Beacons.lua b/Moose Development/Moose/Navigation/Beacons.lua new file mode 100644 index 000000000..ec7b4d060 --- /dev/null +++ b/Moose Development/Moose/Navigation/Beacons.lua @@ -0,0 +1,139 @@ +--- **NAVIGATION** - Beacons of the map/theatre. +-- +-- **Main Features:** +-- +-- * Beacons of the map +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Beacons). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Beacons +-- @image NAVIGATION_Beacons.png + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- BEACONS class. +-- @type BEACONS +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #table beacons Beacons. +-- +-- @extends Core.Base#BASE + +--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson +-- +-- === +-- +-- # The BEACONS Concept +-- +-- The NAVFIX class has a great concept! +-- +-- Bla, bla... +-- +-- # Basic Setup +-- +-- A new `BEACONS` object can be created with the @{#BEACONS.New}() function. +-- +-- local beacons=BEACONS:New("G:\Games\DCS World Testing\Mods\terrains\GermanyColdWar\beacons.lua") +-- +-- This is how it works. +-- +-- @field #BEACONS +BEACONS = { + ClassName = "BEACONS", + verbose = 0, + beacons = {}, +} + +--- BEACONS class version. +-- @field #string version +BEACONS.version="0.0.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new BECAONS class instance from a given file. +-- @param #BEACONS self +-- @param #string FileName Full path to the file containing the map beacons. +-- @return #BEACONS self +function BEACONS:NewFromFile(FileName) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #BEACONS + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Add marker all beacons on the F10 map. +-- @param #BEACONS self +-- @return #BEACONS self +function BEACONS:MarkerShow() + + return self +end + +--- Remove markers of all beacons from the F10 map. +-- @param #BEACONS self +-- @return #BEACONS self +function BEACONS:MarkerRemove() + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get text displayed in the F10 marker. +-- @param #BEACONS self +-- @return #string Marker text. +function BEACONS:_GetMarkerText(beacon) + + local altmin=self.altMin and tostring(self.altMin) or "" + local altmax=self.altMax and tostring(self.altMax) or "" + local speedmin=self.speedMin and tostring(self.speedMin) or "" + local speedmax=self.speedMax and tostring(self.speedMax) or "" + + + local text=string.format("NAVFIX %s", self.name) + if self.isIAF then + text=text..string.format(" (IAF)") + end + if self.isIF then + text=text..string.format(" (IF)") + end + text=text..string.format("\nAltitude [ft]: %s - %s", altmin, altmax) + text=text..string.format("\nSpeed [knots]: %s - %s", speedmin, speedmax) + text=text..string.format("\nCompulsory: %s", tostring(self.isCompulsory)) + text=text..string.format("\nFly Over: %s", tostring(self.isFlyover)) + + return text +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Navigation/Point.lua b/Moose Development/Moose/Navigation/Point.lua new file mode 100644 index 000000000..976cc44ee --- /dev/null +++ b/Moose Development/Moose/Navigation/Point.lua @@ -0,0 +1,587 @@ +--- **NAVIGATION** - Navigation Airspace Points, Fixes and Aids. +-- +-- **Main Features:** +-- +-- * Stuff +-- * More Stuff +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20NavFix). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Point +-- @image NAVIGATION_Point.png + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- NAVFIX class. +-- @type NAVFIX +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #string name Name of the point. +-- @field #string typePoint Type of the point, *e.g. "Intersection", "VOR", "Airport". +-- @field Core.Vector#VECTOR vector Position vector of the fix. +-- @field Wrapper.Marker#MARKER marker Marker on F10 map. +-- @field #number altMin Minimum altitude in meters. +-- @field #number altMax Maximum altitude in meters. +-- @field #number speedMin Minimum speed in knots. +-- @field #number speedMax Maximum speed in knots. +-- +-- @field #boolean isCompulsory Is this a compulsory fix. +-- @field #boolean isFlyover Is this a flyover fix (`true`) or turning point otherwise. +-- @field #boolean isFAF Is this a final approach fix. +-- @field #boolean isIAF Is this an initial approach fix. +-- @field #boolean isIF Is this an initial fix. +-- @field #boolean isMAF Is this an initial fix. +-- +-- @extends Core.Base#BASE + +--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson +-- +-- === +-- +-- # The NAVFIX Concept +-- +-- The NAVFIX class has a great concept! +-- +-- A NAVFIX describes a geo position and can, *e.g.*, be part of a FLIGHTPLAN. It has a unique name and is of a certain type, *e.g.* "Intersection", "VOR", "Airbase" etc. +-- It can also have further properties as min/max altitudes and speeds that aircraft need to obey when they pass the point. +-- +-- # Basic Setup +-- +-- A new `NAVFIX` object can be created with the @{#NAVFIX.New}() function. +-- +-- myNavPoint=NAVFIX:New() +-- myTemplate:SetXYZ(X, Y, Z) +-- +-- This is how it works. +-- +-- @field #NAVFIX +NAVFIX = { + ClassName = "NAVFIX", + verbose = 0, +} + +--- Type of point. +-- @type NAVFIX.Type +-- @field #string POINT Waypoint. +-- @field #string INTERSECTION Intersection of airway. +-- @field #string AIRPORT Airport. +-- @field #string VOR Very High Frequency Omnidirectional Range Station. +-- @field #string DME Distance Measuring Equipment. +-- @field #string NDB Non-Directional Beacon. +-- @field #string VORDME Combined VHF omnidirectional range (VOR) with a distance-measuring equipment (DME). +-- @field #string LOC Localizer. +-- @field #string ILS Instrument Landing System. +-- @field #string TACAN TACtical Air Navigation System (TACAN). +NAVFIX.Type={ + POINT="Point", + INTERSECTION="Intersection", + AIRPORT="Airport", + NDB="NDB", + VOR="VOR", + DME="DME", + VORDME="VOR/DME", + LOC="Localizer", + ILS="ILS", + TACAN="TACAN" +} + +--- NAVFIX class version. +-- @field #string version +NAVFIX.version="0.0.1" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new NAVFIX class instance from a given VECTOR. +-- @param #NAVFIX self +-- @param #string Name Name/ident of the point. Should be unique! +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param Core.Vector#VECTOR Vector Position vector of the navpoint. +-- @return #NAVFIX self +function NAVFIX:NewFromVector(Name, Type, Vector) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #NAVFIX + + -- Vector of point. + self.vector=Vector + + -- Name of point. + self.name=Name + + -- Type of the point. + self.typePoint=Type or NAVFIX.Type.POINT + + local coord=COORDINATE:NewFromVec3(self.vector) + + -- Marker on F10. + self.marker=MARKER:New(coord, self:_GetMarkerText()) + + -- Log ID string. + self.lid=string.format("NAVFIX %s [%s] | ", tostring(self.name), tostring(self.typePoint)) + + -- Debug info. + self:I(self.lid..string.format("Created NAVFIX")) + + return self +end + + +--- Create a new NAVFIX class instance from a given COORDINATE. +-- @param #NAVFIX self +-- @param #string Name Name of the fix. Should be unique! +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param Core.Point#COORDINATE Coordinate Coordinate of the point. +-- @return #NAVFIX self +function NAVFIX:NewFromCoordinate(Name, Type, Coordinate) + + -- Create a VECTOR from the coordinate. + local Vector=VECTOR:NewFromVec(Coordinate) + + -- Create NAVFIX. + self=NAVFIX:NewFromVector(Name, Type, Vector) + + return self +end + + +--- Create a new NAVFIX instance from given latitude and longitude in degrees, minutes and seconds (DMS). +-- @param #NAVFIX self +-- @param #string Name Name of the fix. Should be unique! +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param #string Latitude Latitude in DMS as string. +-- @param #string Longitude Longitude in DMS as string. +-- @return #NAVFIX self +function NAVFIX:NewFromLLDMS(Name, Type, Latitude, Longitude) + + -- Create a VECTOR from the coordinate. + local Vector=VECTOR:NewFromLLDMS(Latitude, Longitude) + + -- Create NAVFIX. + self=NAVFIX:NewFromVector(Name, Type, Vector) + + return self +end + +--- Create a new NAVFIX instance from given latitude and longitude in decimal degrees (DD). +-- @param #NAVFIX self +-- @param #string Name Name of the fix. Should be unique! +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param #number Latitude Latitude in DD. +-- @param #number Longitude Longitude in DD. +-- @return #NAVFIX self +function NAVFIX:NewFromLLDD(Name, Type, Latitude, Longitude) + + -- Create a VECTOR from the coordinate. + local Vector=VECTOR:NewFromLLDD(Latitude, Longitude) + + -- Create NAVFIX. + self=NAVFIX:NewFromVector(Name, Type, Vector) + + return self +end + + +--- Create a new NAVFIX class instance relative to a given other NAVFIX. +-- You have to specify the distance and bearing from the new point to the given point. *E.g.*, for a distance of 5 NM and a bearing of 090° (West), the +-- new nav point is created 5 NM East of the given nav point. The reason is that this corresponts to convention used in most maps. +-- You can, however, use the `Reciprocal` switch to create the new point in the direction you specify. +-- @param #NAVFIX self +-- @param #string Name Name of the fix. Should be unique! +-- @param #string Type Type of navfix. +-- @param #NAVFIX NavFix The given/existing navigation fix relative to which the new fix is created. +-- @param #number Distance Distance from the given to the new point in nautical miles. +-- @param #number Bearing Bearing [Deg] from the new point to the given one. +-- @param #boolean Reciprocal If `true` the reciprocal `Bearing` is taken so it specifies the direction from the given point to the new one. +-- @return #NAVFIX self +function NAVFIX:NewFromNavFix(Name, Type, NavFix, Distance, Bearing, Reciprocal) + + -- Convert magnetic to true bearing by adding magnetic declination, e.g. mag. bearing 10°M ==> true bearing 16°M (for 6° variation on Caucasus map) + Bearing=Bearing+UTILS.GetMagneticDeclination() + + if Reciprocal then + Bearing=Bearing-180 + end + + -- Translate. + local Vector=NavFix.vector:Translate(UTILS.NMToMeters(Distance), Bearing, true) + + self=NAVFIX:NewFromVector(Name, Type, Vector) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Set whether this is the intermediate fix (IF). +-- @param #NAVFIX self +-- @return #NAVFIX self +function NAVFIX:SetIntermediateFix(IntermediateFix) + self.isIF=IntermediateFix + return self +end + +--- Set whether this is an initial approach fix (IAF). +-- The IAF is the point where the initial approach segment of an instrument approach begins. +-- It is usually a designated intersection, VHF omidirectional range (VOR) non-directional beacon (NDB) +-- or distance measuring equipment (DME) fix. +-- The IAF may be collocated with the intermediate fix (IF) of the instrument apprach an in such case they designate the +-- beginning of the intermediate segment of the approach. When the IAF and the IF are combined, there is no inital approach segment. +-- @param #NAVFIX self +-- @param #boolean IntermediateFix If `true`, this is an intermediate fix. +-- @return #NAVFIX self +function NAVFIX:SetInitialApproachFix(IntermediateFix) + self.isIAF=IntermediateFix + return self +end + + +--- Set whether this is the final approach fix (FAF). +-- @param #NAVFIX self +-- @param #boolean FinalApproachFix If `true`, this is a final approach fix. +-- @return #NAVFIX self +function NAVFIX:SetFinalApproachFix(FinalApproachFix) + self.isFAF=FinalApproachFix + return self +end + +--- Set whether this is the final approach fix (FAF). +-- @param #NAVFIX self +-- @param #boolean FinalApproachFix If `true`, this is a final approach fix. +-- @return #NAVFIX self +function NAVFIX:SetMissedApproachFix(MissedApproachFix) + self.isMAF=MissedApproachFix + return self +end + + +--- Set minimum altitude. +-- @param #NAVFIX self +-- @param #number Altitude Min altitude in feet. +-- @return #NAVFIX self +function NAVFIX:SetAltMin(Altitude) + + self.altMin=Altitude + + return self +end + +--- Set maximum altitude. +-- @param #NAVFIX self +-- @param #number Altitude Max altitude in feet. +-- @return #NAVFIX self +function NAVFIX:SetAltMax(Altitude) + + self.altMax=Altitude + + return self +end + +--- Set mandatory altitude (min alt = max alt). +-- @param #NAVFIX self +-- @param #number Altitude Altitude in feet. +-- @return #NAVFIX self +function NAVFIX:SetAltMandatory(Altitude) + + self.altMin=Altitude + self.altMax=Altitude + + return self +end + +--- Set minimum allowed speed at this fix. +-- @param #NAVFIX self +-- @param #number Speed Min speed in knots. +-- @return #NAVFIX self +function NAVFIX:SetSpeedMin(Speed) + + self.speedMin=Speed + + return self +end + +--- Set maximum allowed speed at this fix. +-- @param #NAVFIX self +-- @param #number Speed Max speed in knots. +-- @return #NAVFIX self +function NAVFIX:SetSpeedMax(Speed) + + self.speedMax=Speed + + return self +end + +--- Set mandatory speed (min speed = max speed) at this fix. +-- @param #NAVFIX self +-- @param #number Speed Mandatory speed in knots. +-- @return #NAVFIX self +function NAVFIX:SetSpeedMandatory(Speed) + + self.speedMin=Speed + self.speedMax=Speed + + return self +end + + +--- Set whether this fix is compulsory. +-- @param #NAVFIX self +-- @param #boolean Compulsory If `true`, this is a compusory fix. If `false` or nil, it is non-compulsory. +-- @return #NAVFIX self +function NAVFIX:SetCompulsory(Compulsory) + self.isCompulsory=Compulsory + return self +end + +--- Set whether this is a fly-over fix fix. +-- @param #NAVFIX self +-- @param #boolean FlyOver If `true`, this is a fly over fix. If `false` or nil, it is not. +-- @return #NAVFIX self +function NAVFIX:SetFlyOver(FlyOver) + self.isFlyover=FlyOver + return self +end + + +--- Get the altitude in feet MSL. If min and max altitudes are set, it will return a random altitude between min and max. +-- @param #NAVFIX self +-- @return #number Altitude in feet MSL. Can be `nil`, if neither min nor max altitudes have beeen set. +function NAVFIX:GetAltitude() + + local alt=nil + if self.altMin and self.altMax and self.altMin~=self.altMax then + alt=math.random(self.altMin, self.altMax) + elseif self.altMin then + alt=self.altMin + elseif self.altMax then + alt=self.altMax + end + + return alt +end + + +--- Get the speed. If min and max speeds are set, it will return a random speed between min and max. +-- @param #NAVFIX self +-- @return #number Speed in knots. Can be `nil`, if neither min nor max speeds have beeen set. +function NAVFIX:GetSpeed() + + local speed=nil + if self.speedMin and self.speedMax and self.speedMin~=self.speedMax then + speed=math.random(self.speedMin, self.speedMax) + elseif self.speedMin then + speed=self.speedMin + elseif self.speedMax then + speed=self.speedMax + end + + return speed +end + + + +--- Add marker the NAVFIX on the F10 map. +-- @param #NAVFIX self +-- @return #NAVFIX self +function NAVFIX:MarkerShow() + + self.marker:ToAll() + + return self +end + +--- Remove marker of the NAVFIX from the F10 map. +-- @param #NAVFIX self +-- @return #NAVFIX self +function NAVFIX:MarkerRemove() + + self.marker:Remove() + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get text displayed in the F10 marker. +-- @param #NAVFIX self +-- @return #string Marker text. +function NAVFIX:_GetMarkerText() + + local altmin=self.altMin and tostring(self.altMin) or "" + local altmax=self.altMax and tostring(self.altMax) or "" + local speedmin=self.speedMin and tostring(self.speedMin) or "" + local speedmax=self.speedMax and tostring(self.speedMax) or "" + + + local text=string.format("NAVFIX %s", self.name) + if self.isIAF then + text=text..string.format(" (IAF)") + end + if self.isIF then + text=text..string.format(" (IF)") + end + text=text..string.format("\nAltitude [ft]: %s - %s", altmin, altmax) + text=text..string.format("\nSpeed [knots]: %s - %s", speedmin, speedmax) + text=text..string.format("\nCompulsory: %s", tostring(self.isCompulsory)) + text=text..string.format("\nFly Over: %s", tostring(self.isFlyover)) + + return text +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- NAVAID class. +-- @type NAVAID +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @extends Navigation.Point#NAVFIX + +--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson +-- +-- === +-- +-- # The NAVAID Concept +-- +-- A NAVAID consists of one or multiple FLOTILLAs. These flotillas "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed. +-- +-- # Basic Setup +-- +-- A new `NAVAID` object can be created with the @{#NAVAID.New}(`WarehouseName`, `FleetName`) function, where `WarehouseName` is the name of the static or unit object hosting the fleet +-- and `FleetName` is the name you want to give the fleet. This must be *unique*! +-- +-- myFleet=NAVAID:New("myWarehouseName", "1st Fleet") +-- myFleet:SetPortZone(ZonePort1stFleet) +-- myFleet:Start() +-- +-- A fleet needs a *port zone*, which is set via the @{#NAVAID.SetPortZone}(`PortZone`) function. This is the zone where the naval assets are spawned and return to. +-- +-- Finally, the fleet needs to be started using the @{#NAVAID.Start}() function. If the fleet is not started, it will not process any requests. +-- +-- @field #NAVAID +NAVAID = { + ClassName = "NAVAID", + verbose = 0, +} + +--- NAVAID class version. +-- @field #string version +NAVAID.version="0.0.1" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: Add frequencies. Which unit MHz, kHz, Hz? +-- TODO: Add radial function + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new NAVAID class instance. +-- @param #NAVAID self +-- @param #string Name Name/ident of this navaid. +-- @param #string Type Type of the point. Default `NAVFIX.Type.POINT`. +-- @param #string ZoneName Name of the zone to scan the scenery. +-- @param #string SceneryName Name of the scenery object. +-- @return #NAVAID self +function NAVAID:NewFromScenery(Name, Type, ZoneName, SceneryName) + + -- Get the zone. + local zone=ZONE:FindByName(ZoneName) + + -- Get coordinate. + local Coordinate=zone:GetCoordinate() + + -- Inherit everything from NAVFIX class. + self=BASE:Inherit(self, NAVFIX:NewFromCoordinate(Name, Type, Coordinate)) -- #NAVAID + + -- Set zone. + self.zone=ZONE:FindByName(ZoneName) + + -- Try to get the scenery object. Note not all can be found unfortunately. + if SceneryName then + self.scenery=SCENERY:FindByNameInZone(SceneryName, ZoneName) + if not self.scenery then + self:E(string.format("ERROR: Could not find scenery object %s in zone %s", SceneryName, ZoneName)) + end + end + + -- Alias. + self.alias=string.format("%s %s %s", tostring(ZoneName), tostring(SceneryName), tostring(Type)) + + -- Set some string id for output to DCS.log file. + self.lid=string.format("NAVAID %s | ", self.alias) + + -- Debug info. + self:I(self.lid..string.format("Created NAVAID!")) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Set frequency the beacon transmits on. +-- @param #NAVAID self +-- @param #number Frequency Frequency in Hz. +-- @return #NAVAID self +function NAVAID:SetFrequency(Frequency) + + self.frequency=Frequency + + return self +end + +--- Set channel of, *e.g.*, TACAN beacons. +-- @param #NAVAID self +-- @param #number Channel The channel. +-- @param #string Band The band either `"X"` (default) or `"Y"`. +-- @return #NAVAID self +function NAVAID:SetChannel(Channel, Band) + + self.channel=Channel + self.band=Band or "X" + + return self +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- Add private CLASS functions here. +-- No private NAVAID functions yet. + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 73525feb68b3d719ca38e3b5477a4b9ca71939c4 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 12 Apr 2025 11:14:01 +0200 Subject: [PATCH 03/31] Update Beacons.lua - Added user functions - Fixed bug with incorrect position --- .../Moose/Navigation/Beacons.lua | 218 +++++++++++++++--- 1 file changed, 190 insertions(+), 28 deletions(-) diff --git a/Moose Development/Moose/Navigation/Beacons.lua b/Moose Development/Moose/Navigation/Beacons.lua index ec7b4d060..e3b1f2472 100644 --- a/Moose Development/Moose/Navigation/Beacons.lua +++ b/Moose Development/Moose/Navigation/Beacons.lua @@ -18,7 +18,6 @@ -- @module Navigation.Beacons -- @image NAVIGATION_Beacons.png - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -32,23 +31,31 @@ -- -- @extends Core.Base#BASE ---- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson +--- *Hope is the beacon that guides lost ships back to the shore.* -- -- === -- -- # The BEACONS Concept -- --- The NAVFIX class has a great concept! +-- This class is desinged to make information about beacons of a map/theatre easier accessible. The information contains location, type and frequencies of all or specific beacons of the map. -- --- Bla, bla... +-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `beacons.lua` that can be +-- found in the installation directory of DCS for each map or a table that the user needs to provide. -- -- # Basic Setup -- --- A new `BEACONS` object can be created with the @{#BEACONS.New}() function. +-- A new `BEACONS` object can be created with the @{#BEACONS.NewFromFile}(*beacons_lua_file*) function. +-- +-- local beacons=BEACONS:NewFromFile("\Mods\terrains\\beacons.lua") +-- beacons:MarkerShow() +-- +-- This will load the beacons from the `` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file +-- you provided is correct and all relevant beacons are present. +-- +-- # User Functions +-- +-- ## Get Closest Beacon -- --- local beacons=BEACONS:New("G:\Games\DCS World Testing\Mods\terrains\GermanyColdWar\beacons.lua") --- --- This is how it works. -- -- @field #BEACONS BEACONS = { @@ -57,20 +64,61 @@ BEACONS = { beacons = {}, } +--- Mission capability. +-- @type BEACONS.Beacon +-- @field #function display_name Function that returns the localized name. +-- @field #number type Beacon type. +-- @field #string beaconId Beacon ID. +-- @field #string callsign Call sign. +-- @field #number frequency Frequency in Hz. +-- @field #table position Position table. +-- @field #number direction Direction in degrees. +-- @field #table positionGeo Table with latitude and longitude. +-- @field #table sceneObjects Table with scenery objects, e.g. `{t:393396742}`. +-- @field #number chartOffsetX No idea what this offset is?! +-- @field DCS#Vec3 vec3 Position vector 3D. +-- @field #number markerID ID for the F10 marker. + --- BEACONS class version. -- @field #string version -BEACONS.version="0.0.0" +BEACONS.version="0.0.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: A lot... +-- TODO: TACAN channel from frequency +-- TODO: Scenery object ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Create a new BECAONS class instance from a given table. +-- @param #BEACONS self +-- @param #table BeaconTable Table with beacon info. +-- @return #BEACONS self +function BEACONS:NewFromTable(BeaconTable) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #BEACONS + + for _,_beacon in pairs(BeaconTable) do + local beacon=_beacon --#BEACONS.Beacon + + beacon.vec3={x=beacon.position[1], y=beacon.position[2], z=beacon.position[3]} + + table.insert(self.beacons, beacon) + end + + self:I(string.format("Added %d beacons", #self.beacons)) + + + return self +end + + --- Create a new BECAONS class instance from a given file. -- @param #BEACONS self -- @param #string FileName Full path to the file containing the map beacons. @@ -79,7 +127,20 @@ function BEACONS:NewFromFile(FileName) -- Inherit everything from BASE class. self=BASE:Inherit(self, BASE:New()) -- #BEACONS + + local exists=UTILS.FileExists(FileName) + + if exists==false then + self:E(string.format("ERROR: file with beacon info does not exist!")) + return nil + end + -- This will create a global table `beacons` + dofile(FileName) + + -- Get beacons from table. + self=self:NewFromTable(beacons) + return self end @@ -87,18 +148,110 @@ end -- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Add marker all beacons on the F10 map. +--- Get 3D position vector of a specific beacon. -- @param #BEACONS self +-- @param #BEACONS.Beacon beacon The beacon data structure. +-- @return DCS#Vec3 Position vector. +function BEACONS:GetVec3(beacon) + return beacon.vec3 +end + +--- Get COORDINATE of a specific beacon. +-- @param #BEACONS self +-- @param #BEACONS.Beacon beacon The beacon data structure. +-- @return Core.Point#COORDINATE The coordinate. +function BEACONS:GetCoordinate(beacon) + local coordinate=COORDINATE:NewFromVec3(beacon.vec3) + return coordinate +end + +--- Find closest beacon to a given coordinate. +-- @param #BEACONS self +-- @param Core.Point#COORDINATE Coordinate The reference coordinate. +-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`. +-- @return #BEACONS.Beacon The closest beacon. +function BEACONS:GetClosestBeacon(Coordinate, TypeID) + + local beacon=nil --#BEACONS.Beacon + local distmin=math.huge + + for _,_beacon in pairs(self.beacons) do + local bc=_beacon --#BEACONS.Beacon + + if TypeID==nil or TypeID==bc.type then + + local dist=Coordinate:Get2DDistance(bc.vec3) + + if dist Date: Sat, 12 Apr 2025 21:18:19 +0200 Subject: [PATCH 04/31] Update Beacons.lua - Added channel - Added scenery --- .../Moose/Navigation/Beacons.lua | 95 +++++++++++++++++-- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Navigation/Beacons.lua b/Moose Development/Moose/Navigation/Beacons.lua index e3b1f2472..cce4ff9f8 100644 --- a/Moose Development/Moose/Navigation/Beacons.lua +++ b/Moose Development/Moose/Navigation/Beacons.lua @@ -2,7 +2,9 @@ -- -- **Main Features:** -- --- * Beacons of the map +-- * Access beacons of the map +-- * Find closest beacon +-- * Get frequencies and channels -- -- === -- @@ -54,6 +56,10 @@ -- -- # User Functions -- +-- ## F10 Map Markers +-- +-- ## Position +-- -- ## Get Closest Beacon -- -- @@ -71,6 +77,7 @@ BEACONS = { -- @field #string beaconId Beacon ID. -- @field #string callsign Call sign. -- @field #number frequency Frequency in Hz. +-- @field #number channel TACAN, RSBN or PRMG channel depending on type. -- @field #table position Position table. -- @field #number direction Direction in degrees. -- @field #table positionGeo Table with latitude and longitude. @@ -78,18 +85,20 @@ BEACONS = { -- @field #number chartOffsetX No idea what this offset is?! -- @field DCS#Vec3 vec3 Position vector 3D. -- @field #number markerID ID for the F10 marker. +-- @field #string typeName Name of becon type. +-- @field Wrapper.Scenery#SCENERY scenery The scenery object. --- BEACONS class version. -- @field #string version -BEACONS.version="0.0.1" +BEACONS.version="0.0.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: A lot... --- TODO: TACAN channel from frequency --- TODO: Scenery object +-- DONE: TACAN channel from frequency (was already in beacon.lua as channel) +-- DONE: Scenery object ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -107,14 +116,36 @@ function BEACONS:NewFromTable(BeaconTable) for _,_beacon in pairs(BeaconTable) do local beacon=_beacon --#BEACONS.Beacon + -- Get 3D vector beacon.vec3={x=beacon.position[1], y=beacon.position[2], z=beacon.position[3]} + -- Get coordinate + beacon.coordinate=COORDINATE:NewFromVec3(beacon.vec3) + + -- Get type name + beacon.typeName=self:_GetTypeName(beacon.type) + + -- Find closest scenery object from scan + beacon.scenery=beacon.coordinate:FindClosestScenery(20) + + -- Debug stuff for scenery object + if false then + if beacon.scenery then + env.info(string.format("FF Beacon %s %s %s got scenery object %s, %s", beacon.callsign, beacon.beaconId, beacon.typeName, beacon.scenery:GetName(), beacon.scenery:GetTypeName() )) + UTILS.PrintTableToLog(beacon.scenery.SceneryObject) + UTILS.PrintTableToLog(beacon.sceneObjects) + else + env.info(string.format("FF NO scenery object %s %s %s ", beacon.callsign, beacon.beaconId, beacon.typeName)) + end + end + + -- Add to table table.insert(self.beacons, beacon) end + -- Debug output self:I(string.format("Added %d beacons", #self.beacons)) - return self end @@ -214,7 +245,28 @@ function BEACONS:GetBeacons(TypeID) return beacons end +--- Count beacons, optionally of a given type. +-- @param #BEACONS self +-- @param #number TypeID (Optional) Only count specific beacon types, *e.g.* `BEACON.Type.TACAN`. +-- @return #number Number of beacons. +function BEACONS:CountBeacons(TypeID) + local n=#self.beacons + + if TypeID then + for _,_beacon in pairs(self.beacons) do + local bc=_beacon --#BEACONS.Beacon + + if TypeID==bc.type then + n=n+1 + end + end + else + n=#self.beacons + end + + return n +end --- Add markers for all beacons on the F10 map. -- @param #BEACONS self @@ -266,18 +318,44 @@ end -- @return #string Marker text. function BEACONS:_GetMarkerText(beacon) - local frequency=beacon.frequency~=nil and beacon.frequency/1000 or -1 + local frequency, funit=self:_GetFrequency(beacon.frequency) local direction=beacon.direction~=nil and beacon.direction or -1 local text=string.format("Beacon %s", tostring(beacon.beaconId)) text=text..string.format("\nCallsign: %s", tostring(beacon.callsign)) - text=text..string.format("\nType: %s", tostring(self:_GetTypeName(beacon.type))) - text=text..string.format("\nFrequency: %.3f kHz", frequency) + text=text..string.format("\nType: %s", tostring(beacon.typeName)) + --if beacon.type==BEACON.Type.TACAN or beacon.type==BEACON.Type.RSBN or beacon.type==BEACON.Type.PRMG_GLIDESLOPE or beacon.type==BEACON.Type.PRMG_LOCALIZER then + if UTILS.IsInTable({BEACON.Type.TACAN, BEACON.Type.RSBN, BEACON.Type.PRMG_GLIDESLOPE, BEACON.Type.PRMG_LOCALIZER}, beacon.type) then + text=text..string.format("\nChannel: %s", tostring(beacon.channel)) + end + text=text..string.format("\nFrequency: %.3f %s", frequency, funit) text=text..string.format("\nDirection: %.1f°", direction) return text end + +--- Get converted frequency. +-- @param #BEACONS self +-- @param #number freq Frequency in Hz. +-- @return #number Frequency in better unit. +-- @return #string Unit ("Hz", "kHz", "MHz"). +function BEACONS:_GetFrequency(freq) + + freq=freq or 0 + local unit="Hz" + + if freq>=1e6 then + freq=freq/1e6 + unit="MHz" + elseif freq>=1e3 then + freq=freq/1e3 + unit="kHz" + end + + return freq, unit +end + --- Get name of beacon type. -- @param #BEACONS self -- @param #number typeID Beacon type number. @@ -295,7 +373,6 @@ function BEACONS:_GetTypeName(typeID) return "Unknown" end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 0a9717a8c2a6d884a6c9730e5d5083310e6b3184 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 13 Apr 2025 23:14:19 +0200 Subject: [PATCH 05/31] RADIOS - Added new class RADIOS --- Moose Development/Moose/Modules.lua | 1 + Moose Development/Moose/Modules_local.lua | 1 + Moose Development/Moose/Navigation/Radios.lua | 324 ++++++++++++++++++ Moose Development/Moose/Utilities/Enums.lua | 25 ++ 4 files changed, 351 insertions(+) create mode 100644 Moose Development/Moose/Navigation/Radios.lua diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index fecf7f271..24c23898a 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -189,5 +189,6 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatch __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Point.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Radios.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Globals.lua' ) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 15e1b3b05..0fea70e2e 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -181,5 +181,6 @@ __Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' ) __Moose.Include( 'Navigation\\Point.lua' ) __Moose.Include( 'Navigation\\Beacons.lua' ) +__Moose.Include( 'Navigation\\Radios.lua' ) __Moose.Include( 'Globals.lua' ) diff --git a/Moose Development/Moose/Navigation/Radios.lua b/Moose Development/Moose/Navigation/Radios.lua new file mode 100644 index 000000000..5b1d5e013 --- /dev/null +++ b/Moose Development/Moose/Navigation/Radios.lua @@ -0,0 +1,324 @@ +--- **NAVIGATION** - Airbase radios. +-- +-- **Main Features:** +-- +-- * Get radio frequencies of airbases +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Radios). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Radios +-- @image NAVIGATION_Radios.png + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- RADIOS class. +-- @type RADIOS +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #table radios Radios. +-- +-- @extends Core.Base#BASE + +--- *It's not true I had nothing on, I had the radio on.* -- *Marilyn Monroe* +-- +-- === +-- +-- # The RADIOS Concept +-- +-- This class is desinged to make information about radios of a map/theatre easier accessible. The information contains mostly the frequencies of airbases of the map. +-- +-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `radio.lua` that can be +-- found in the installation directory of DCS for each map or a table that the user needs to provide. +-- +-- # Basic Setup +-- +-- A new `RADIOS` object can be created with the @{#RADIOS.NewFromFile}(*radio_lua_file*) function. +-- +-- local radios=RADIOS:NewFromFile("\Mods\terrains\\radios.lua") +-- radios:MarkerShow() +-- +-- This will load the radios from the `` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file +-- you provided is correct and all relevant radios are present. +-- +-- # User Functions +-- +-- ## F10 Map Markers +-- +-- ## Position +-- +-- ## Closest Radio +-- +-- +-- @field #RADIOS +RADIOS = { + ClassName = "RADIOS", + verbose = 0, + radios = {}, +} + +--- Radio item data structure. +-- @type RADIOS.Radio +-- @field #string radioId Radio ID. +-- @field #table role Roles of the radio (usually {"ground", "tower", "approach"}). +-- @field #table callsign Callsigns of the radio (usually the airbase name). +-- @field #table frequency Frequencies of the radios. +-- @field #table position Position table. +-- @field #table sceneObjects Scenery objects. +-- @field #string name Name of the airbase. +-- @field Wrapper.Airbase#AIRBASE airbase Airbase. + +--- Radio item data structure. +-- @type RADIOS.Frequency +-- @field #number modu Modulation type. +-- @field #number freq Frequency in Hz. + + +--- RADIOS class version. +-- @field #string version +RADIOS.version="0.0.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new RADIOS class instance from a given table. +-- @param #RADIOS self +-- @param #table RadioTable Table with radios info. +-- @return #RADIOS self +function RADIOS:NewFromTable(RadioTable) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #RADIOS + + local airbasenames=AIRBASE.GetAllAirbaseNames() + + for _,_radio in pairs(RadioTable) do + local radio=_radio --#RADIOS.Radio + + --UTILS.PrintTableToLog(radio) + --UTILS.PrintTableToLog(radio.callsign) + + -- The table structure of callsign is a bit awkward. We need to get the airbase name. + local cs=radio.callsign[1] + if cs and cs.common then + radio.name=cs.common[1] + elseif cs and cs.nato then + radio.name=cs.nato[1] + else + radio.name="Unknown" + end + + --UTILS.PrintTableToLog(radio.callsign) + + radio.name=self:_GetAirbaseName(airbasenames, radio.name) + + radio.airbase=AIRBASE:FindByName(radio.name) + + if radio.airbase then + radio.coordinate=radio.airbase:GetCoordinate() + end + + -- Add to table + table.insert(self.radios, radio) + end + + -- Debug output + self:I(string.format("Added %d radios", #self.radios)) + + return self +end + + +--- Create a new RADIOS class instance from a given file. +-- @param #RADIOS self +-- @param #string FileName Full path to the file containing the map radios. +-- @return #RADIOS self +function RADIOS:NewFromFile(FileName) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #RADIOS + + local exists=UTILS.FileExists(FileName) + + if exists==false then + self:E(string.format("ERROR: file with radios info does not exist! File=%s", tostring(FileName))) + return nil + end + + -- This will create a global table `radio` + dofile(FileName) + + -- Get radios from table. + self=self:NewFromTable(radio) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get 3D position vector of a specific radio. +-- @param #RADIOS self +-- @param #RADIOS.Radio radio The radio data structure. +-- @return DCS#Vec3 Position vector. +function RADIOS:GetVec3(radio) + return radio.vec3 +end + +--- Get COORDINATE of a specific radio. +-- @param #RADIOS self +-- @param #RADIOS.Radio radio The radio data structure. +-- @return Core.Point#COORDINATE The coordinate. +function RADIOS:GetCoordinate(radio) + return radio.coordinate +end + +--- Add markers for all radios on the F10 map. +-- @param #RADIOS self +-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio. +-- @return #RADIOS self +function RADIOS:MarkerShow(Radio) + + for _,_radio in pairs(self.radios) do + local radio=_radio --#RADIOS.Radio + if Radio==nil or Radio.radioId==radio.radioId then + local coord=self:GetCoordinate(radio) + if coord then + local text=self:_GetMarkerText(radio) + if radio.markerID then + UTILS.RemoveMark(radio.markerID) + end + radio.markerID=coord:MarkToAll(text) + end + end + end + + return self +end + +--- Remove markers of all radios from the F10 map. +-- @param #RADIOS self +-- @param #RADIOS.Radio Radio (Optional) Only this specifc radio. +-- @return #RADIOS self +function RADIOS:MarkerRemove(Radio) + + for _,_radio in pairs(self.radios) do + local radio=_radio --#RADIOS.Radio + if Radio==nil or Radio.radioId==radio.radioId then + if radio.markerID then + UTILS.RemoveMark(radio.markerID) + radio.markerID=nil + end + end + end + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get text displayed in the F10 marker. +-- @param #RADIOS self +-- @param #RADIOS.Radio radio The radio data structure. +-- @return #string Marker text. +function RADIOS:_GetMarkerText(radio) + + local text=string.format("Radio %s", tostring(radio.name)) + for b,f in pairs(radio.frequency) do + local frequency=f --#RADIOS.Frequency + local mod=frequency[1] + local fre=frequency[2] + local freq, funit=self:_GetFrequency(fre) + --UTILS.PrintTableToLog(frequency) + local band=self:_GetBandName(b) + text=text..string.format("\n%s: %.3f %s", band, freq, funit) + end + + return text +end + + +--- Get converted frequency. +-- @param #RADIOS self +-- @param #number freq Frequency in Hz. +-- @return #number Frequency in better unit. +-- @return #string Unit ("Hz", "kHz", "MHz"). +function RADIOS:_GetFrequency(freq) + + freq=freq or 0 + local unit="Hz" + + if freq>=1e6 then + freq=freq/1e6 + unit="MHz" + elseif freq>=1e3 then + freq=freq/1e3 + unit="kHz" + end + + return freq, unit +end + +--- Get name of frequency band. +-- @param #RADIOS self +-- @param #number BandNumber Band as number. +-- @return #string Band name. +function RADIOS:_GetBandName(BandNumber) + + if BandNumber~=nil then + for bandName,bandNumber in pairs(ENUMS.FrequencyBand) do + if bandNumber==BandNumber then + return bandName + end + end + end + + return "Unknown" +end + +--- Get name of frequency band. +-- @param #RADIOS self +-- @param #table airbasenames Names of all airbases. +-- @param #string name Name of airbase. +-- @return #string Name of airbase +function RADIOS:_GetAirbaseName(airbasenames, name) + + local airbase=AIRBASE:FindByName(name) + + if airbase then + return name + else + for _,airbasename in pairs(airbasenames) do + if string.find(airbasename, name) then + return airbasename + end + end + end + + return "Unknown" +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Utilities/Enums.lua b/Moose Development/Moose/Utilities/Enums.lua index a269fb972..4e6ef999f 100644 --- a/Moose Development/Moose/Utilities/Enums.lua +++ b/Moose Development/Moose/Utilities/Enums.lua @@ -1359,3 +1359,28 @@ ENUMS.FARPObjectTypeNamesAndShape ={ [ENUMS.FARPType.PADSINGLE] = { TypeName="FARP_SINGLE_01", ShapeName="FARP_SINGLE_01"}, } +--- Radio frequency bands (HF, VHF, UHF) +-- @type ENUMS.FrequencyBand +-- @field #number HF High frequency +-- @field #number VHF_LOW Very high frequency +-- @field #number VHF_HI Very high frequency +-- @field #number UHF Ultra high frequency +ENUMS.FrequencyBand = { + HF = 0, + VHF_LOW = 1, + VHF_HI = 2, + UHF = 3, +} + +--- Radio modulation types (AM, FM) +-- @type ENUMS.ModulationType +-- @field #number AM Amplitude modulation +-- @field #number FM Frequency modulation +-- @field #number AMFM Amplitude and frequency modulation +-- @field #number DISCARD Discard modulation +ENUMS.ModulationType = { + AM = 0, + FM = 1, + AMFM = 2, + DISCARD = -1, +} From 17f672dad43f5d06175bd3a1e559fe4f4112fdb8 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 14 Apr 2025 15:49:39 +0200 Subject: [PATCH 06/31] Towns - Added new Class for towns --- Moose Development/Moose/Modules.lua | 1 + Moose Development/Moose/Modules_local.lua | 1 + Moose Development/Moose/Navigation/Towns.lua | 261 +++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 Moose Development/Moose/Navigation/Towns.lua diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 24c23898a..13f30e38f 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -190,5 +190,6 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Tasking/Task_Capture_Dispatch __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Point.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Beacons.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Radios.lua' ) +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Navigation/Towns.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Globals.lua' ) diff --git a/Moose Development/Moose/Modules_local.lua b/Moose Development/Moose/Modules_local.lua index 0fea70e2e..f43c2a378 100644 --- a/Moose Development/Moose/Modules_local.lua +++ b/Moose Development/Moose/Modules_local.lua @@ -182,5 +182,6 @@ __Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' ) __Moose.Include( 'Navigation\\Point.lua' ) __Moose.Include( 'Navigation\\Beacons.lua' ) __Moose.Include( 'Navigation\\Radios.lua' ) +__Moose.Include( 'Navigation\\Towns.lua' ) __Moose.Include( 'Globals.lua' ) diff --git a/Moose Development/Moose/Navigation/Towns.lua b/Moose Development/Moose/Navigation/Towns.lua new file mode 100644 index 000000000..fc31e65d5 --- /dev/null +++ b/Moose Development/Moose/Navigation/Towns.lua @@ -0,0 +1,261 @@ +--- **NAVIGATION** - Beacons of the map/theatre. +-- +-- **Main Features:** +-- +-- * Find towns of map +-- * Road and rail connections +-- +-- === +-- +-- ## Example Missions: +-- +-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Navigation%20-%20Towns). +-- +-- === +-- +-- ### Author: **funkyfranky** +-- +-- === +-- @module Navigation.Towns +-- @image NAVIGATION_Towns.png + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- TOWNS class. +-- @type TOWNS +-- +-- @field #string ClassName Name of the class. +-- @field #number verbose Verbosity of output. +-- @field #table towns Towns. +-- +-- @extends Core.Base#BASE + +--- *Hope is the beacon that guides lost ships back to the shore.* +-- +-- === +-- +-- # The TOWNS Concept +-- +-- This class is desinged to make information about towns of a map/theatre easier accessible. The information contains location and road/rail connections of the towns. +-- +-- **Note** that try to avoid hard coding stuff in Moose since DCS is updated frequently and things change. Therefore, the main source of information is either a file `towns.lua` that can be +-- found in the installation directory of DCS for each map or a table that the user needs to provide. +-- +-- # Basic Setup +-- +-- A new `TOWNS` object can be created with the @{#TOWNS.NewFromFile}(*towns_lua_file*) function. +-- +-- local towns=TOWNS:NewFromFile("\Mods\terrains\\towns.lua") +-- towns:MarkerShow() +-- +-- This will load the towns from the `` for the specific map and place markers on the F10 map. This is the first step you should do to ensure that the file +-- you provided is correct and all relevant towns are present. +-- +-- # User Functions +-- +-- ## F10 Map Markers +-- +-- ## Position +-- +-- ## Get Closest Town +-- +-- +-- @field #TOWNS +TOWNS = { + ClassName = "TOWNS", + verbose = 0, + towns = {}, +} + +--- Town data. +-- @type TOWNS.Town +-- @field #string display_name Displayed name. +-- @field #string name Name of the town. +-- @field #number latitude Latitude. +-- @field #number longitude Longitude +-- @field DCS#Vec3 vec3 Position vector 3D. +-- @field Core.Point#COORDINATE coordinate The coordinate. +-- @field Core.Point#COORDINATE coordRoad The coordinate of the closest road. +-- @field Core.Point#COORDINATE coordRail The coordinate of the closest railway. +-- @field #number markerID ID for the F10 marker. + +--- TOWNS class version. +-- @field #string version +TOWNS.version="0.0.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ToDo list +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot... +-- TODO: Road connection +-- TODO: Rail connection + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new TOWNS class instance from a given table. +-- @param #TOWNS self +-- @param #table TownTable Table with all towns data. +-- @return #TOWNS self +function TOWNS:NewFromTable(TownTable) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #TOWNS + + for TownName,_town in pairs(TownTable) do + local town=_town --#TOWNS.Town + + town.name=TownName + + -- Get coordinate + town.coordinate=COORDINATE:NewFromLLDD(town.latitude, town.longitude) + + -- Add to table + table.insert(self.towns, town) + end + + -- Debug output + self:I(string.format("Added %d towns", #self.towns)) + + return self +end + + +--- Create a new TOWNS class instance from a given file. +-- @param #TOWNS self +-- @param #string FileName Full path to the file containing the towns data. +-- @return #TOWNS self +function TOWNS:NewFromFile(FileName) + + -- Inherit everything from BASE class. + self=BASE:Inherit(self, BASE:New()) -- #TOWNS + + local exists=UTILS.FileExists(FileName) + + if exists==false then + self:E(string.format("ERROR: file with towns info does not exist!")) + return nil + end + + -- This will create a global table `towns` + dofile(FileName) + + -- Get towns from table. + self=self:NewFromTable(towns) + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get 3D position vector of a specific town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return DCS#Vec3 Position vector. +function TOWNS:GetVec3(town) + return town.vec3 +end + +--- Get COORDINATE of a specific town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return Core.Point#COORDINATE The coordinate. +function TOWNS:GetCoordinate(town) + return town.coordinate +end + +--- Find closest town to a given coordinate. +-- @param #TOWNS self +-- @param Core.Point#COORDINATE Coordinate The reference coordinate. +-- @return #TOWNS.Town The closest town. +function TOWNS:GetClosestTown(Coordinate) + + local Town=nil --#TOWNS.Town + local distmin=math.huge + + for _,_town in pairs(self.towns) do + local town=_town --#TOWNS.Town + + local dist=Coordinate:Get2DDistance(bc.coordinate) + + if dist Date: Mon, 14 Apr 2025 22:42:29 +0200 Subject: [PATCH 07/31] Update Towns.lua --- Moose Development/Moose/Navigation/Towns.lua | 43 ++++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Navigation/Towns.lua b/Moose Development/Moose/Navigation/Towns.lua index fc31e65d5..dda193874 100644 --- a/Moose Development/Moose/Navigation/Towns.lua +++ b/Moose Development/Moose/Navigation/Towns.lua @@ -83,7 +83,7 @@ TOWNS = { --- TOWNS class version. -- @field #string version -TOWNS.version="0.0.0" +TOWNS.version="0.0.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -92,6 +92,7 @@ TOWNS.version="0.0.0" -- TODO: A lot... -- TODO: Road connection -- TODO: Rail connection +-- TODO: Connection between towns ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -114,6 +115,12 @@ function TOWNS:NewFromTable(TownTable) -- Get coordinate town.coordinate=COORDINATE:NewFromLLDD(town.latitude, town.longitude) + -- Get coordinate of closest road + town.coordRoad=town.coordinate:GetClosestPointToRoad() + + -- Get coordinate of closest rail + town.coordRail=town.coordinate:GetClosestPointToRoad(true) + -- Add to table table.insert(self.towns, town) end @@ -170,6 +177,34 @@ function TOWNS:GetCoordinate(town) return town.coordinate end +--- Get closest road coordinate of a town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return Core.Point#COORDINATE The closest road coordinate. +function TOWNS:GetCoordRoad(town) + return town.coordRoad +end + +--- Get closest rail coordinate of a town. +-- @param #TOWNS self +-- @param #TOWNS.Town town The town data structure. +-- @return Core.Point#COORDINATE The closest rail coordinate. +function TOWNS:GetCoordRail(town) + return town.coordRail +end + +--- Get road connection between two towns. +-- @param #TOWNS self +-- @param #TOWNS.Town townA The town data structure. +-- @param #TOWNS.Town townB The town data structure. +-- @return #table Table containing path coordinates. +function TOWNS:GetConnectionRoad(townA, townB) + + local path=townA.coordRoad:GetPathOnRoad(townB.coordRoad) + + return path +end + --- Find closest town to a given coordinate. -- @param #TOWNS self -- @param Core.Point#COORDINATE Coordinate The reference coordinate. @@ -182,7 +217,7 @@ function TOWNS:GetClosestTown(Coordinate) for _,_town in pairs(self.towns) do local town=_town --#TOWNS.Town - local dist=Coordinate:Get2DDistance(bc.coordinate) + local dist=Coordinate:Get2DDistance(town.coordinate) if dist Date: Tue, 15 Apr 2025 22:06:53 +0200 Subject: [PATCH 08/31] Update Beacons.lua - Fixed bug in counting beacons - Added option to mark certain beacon types - Improved maker text --- .../Moose/Navigation/Beacons.lua | 49 ++++++++++++------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Navigation/Beacons.lua b/Moose Development/Moose/Navigation/Beacons.lua index cce4ff9f8..03ed7dc7c 100644 --- a/Moose Development/Moose/Navigation/Beacons.lua +++ b/Moose Development/Moose/Navigation/Beacons.lua @@ -66,7 +66,7 @@ -- @field #BEACONS BEACONS = { ClassName = "BEACONS", - verbose = 0, + verbose = 1, beacons = {}, } @@ -90,7 +90,7 @@ BEACONS = { --- BEACONS class version. -- @field #string version -BEACONS.version="0.0.2" +BEACONS.version="0.0.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -146,6 +146,15 @@ function BEACONS:NewFromTable(BeaconTable) -- Debug output self:I(string.format("Added %d beacons", #self.beacons)) + if self.verbose > 0 then + local text="Beacon types:" + for typeName,typeID in pairs(BEACON.Type) do + local n=self:CountBeacons(typeID) + text=text..string.format("\n%s = %d", typeName, n) + end + self:I(text) + end + return self end @@ -251,7 +260,7 @@ end -- @return #number Number of beacons. function BEACONS:CountBeacons(TypeID) - local n=#self.beacons + local n=0 if TypeID then for _,_beacon in pairs(self.beacons) do @@ -268,39 +277,45 @@ function BEACONS:CountBeacons(TypeID) return n end ---- Add markers for all beacons on the F10 map. +--- Add markers for all beacons on the F10 map. Optionally, only a specific beacon or a certain beacon type can be marked. -- @param #BEACONS self -- @param #BEACONS.Beacon Beacon (Optional) Only this specifc beacon. +-- @param #number TypeID (Optional) Only show specific beacon types, *e.g.* `BEACON.Type.TACAN`. -- @return #BEACONS self -function BEACONS:MarkerShow(Beacon) +function BEACONS:MarkerShow(Beacon, TypeID) for _,_beacon in pairs(self.beacons) do local beacon=_beacon --#BEACONS.Beacon if Beacon==nil or Beacon.beaconId==beacon.beaconId then - local text=self:_GetMarkerText(beacon) - local coord=COORDINATE:NewFromVec3(beacon.vec3) - if beacon.markerID then - UTILS.RemoveMark(beacon.markerID) + if TypeID==nil or beacon.type==TypeID then + local text=self:_GetMarkerText(beacon) + local coord=COORDINATE:NewFromVec3(beacon.vec3) + if beacon.markerID then + UTILS.RemoveMark(beacon.markerID) + end + beacon.markerID=coord:MarkToAll(text) end - beacon.markerID=coord:MarkToAll(text) end end return self end ---- Remove markers of all beacons from the F10 map. +--- Remove markers of all beacons from the F10 map. Optionally, remove only marker of a specific beacon or a certain beacon type. -- @param #BEACONS self -- @param #BEACONS.Beacon Beacon (Optional) Only this specifc beacon. +-- @param #number TypeID (Optional) Only show specific beacon types, *e.g.* `BEACON.Type.TACAN`. -- @return #BEACONS self -function BEACONS:MarkerRemove(Beacon) +function BEACONS:MarkerRemove(Beacon, TypeID) for _,_beacon in pairs(self.beacons) do local beacon=_beacon --#BEACONS.Beacon if Beacon==nil or Beacon.beaconId==beacon.beaconId then - if beacon.markerID then - UTILS.RemoveMark(beacon.markerID) - beacon.markerID=nil + if TypeID==nil or beacon.type==TypeID then + if beacon.markerID then + UTILS.RemoveMark(beacon.markerID) + beacon.markerID=nil + end end end end @@ -321,10 +336,8 @@ function BEACONS:_GetMarkerText(beacon) local frequency, funit=self:_GetFrequency(beacon.frequency) local direction=beacon.direction~=nil and beacon.direction or -1 - local text=string.format("Beacon %s", tostring(beacon.beaconId)) + local text=string.format("Beacon %s [ID=%s]", tostring(beacon.typeName), tostring(beacon.beaconId)) text=text..string.format("\nCallsign: %s", tostring(beacon.callsign)) - text=text..string.format("\nType: %s", tostring(beacon.typeName)) - --if beacon.type==BEACON.Type.TACAN or beacon.type==BEACON.Type.RSBN or beacon.type==BEACON.Type.PRMG_GLIDESLOPE or beacon.type==BEACON.Type.PRMG_LOCALIZER then if UTILS.IsInTable({BEACON.Type.TACAN, BEACON.Type.RSBN, BEACON.Type.PRMG_GLIDESLOPE, BEACON.Type.PRMG_LOCALIZER}, beacon.type) then text=text..string.format("\nChannel: %s", tostring(beacon.channel)) end From ce61f454bf73975e7d1ac927d79e4832831739bc Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 17 Apr 2025 20:34:46 +0200 Subject: [PATCH 09/31] Update Beacons.lua - Added excludelist to GetClosestBeacon function --- Moose Development/Moose/Navigation/Beacons.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Navigation/Beacons.lua b/Moose Development/Moose/Navigation/Beacons.lua index 03ed7dc7c..f22fecaf5 100644 --- a/Moose Development/Moose/Navigation/Beacons.lua +++ b/Moose Development/Moose/Navigation/Beacons.lua @@ -90,7 +90,7 @@ BEACONS = { --- BEACONS class version. -- @field #string version -BEACONS.version="0.0.3" +BEACONS.version="0.0.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -209,20 +209,24 @@ end -- @param #BEACONS self -- @param Core.Point#COORDINATE Coordinate The reference coordinate. -- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`. +-- @param #number DistMax (Optional) Max search distance in meters. +-- @param #table ExcludeList (Optional) List of beacons to exclude. -- @return #BEACONS.Beacon The closest beacon. -function BEACONS:GetClosestBeacon(Coordinate, TypeID) +function BEACONS:GetClosestBeacon(Coordinate, TypeID, DistMax, ExcludeList) local beacon=nil --#BEACONS.Beacon local distmin=math.huge + ExcludeList=ExcludeList or {} + for _,_beacon in pairs(self.beacons) do local bc=_beacon --#BEACONS.Beacon - if TypeID==nil or TypeID==bc.type then + if (TypeID==nil or TypeID==bc.type) and (not UTILS.IsInTable(ExcludeList, bc, "beaconId")) then local dist=Coordinate:Get2DDistance(bc.vec3) - if dist Date: Sun, 26 Oct 2025 15:19:34 +0100 Subject: [PATCH 10/31] Update Radios.lua --- Moose Development/Moose/Navigation/Radios.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Moose Development/Moose/Navigation/Radios.lua b/Moose Development/Moose/Navigation/Radios.lua index 5b1d5e013..7e13e57e3 100644 --- a/Moose Development/Moose/Navigation/Radios.lua +++ b/Moose Development/Moose/Navigation/Radios.lua @@ -162,6 +162,9 @@ function RADIOS:NewFromFile(FileName) self:E(string.format("ERROR: file with radios info does not exist! File=%s", tostring(FileName))) return nil end + + -- Backup DCS radio table + local radiobak=UTILS.DeepCopy(radio) -- This will create a global table `radio` dofile(FileName) @@ -169,6 +172,9 @@ function RADIOS:NewFromFile(FileName) -- Get radios from table. self=self:NewFromTable(radio) + -- Restore DCS radio table + radio=UTILS.DeepCopy(radiobak) + return self end From 2931b32ce69263406070bc836dc468f82fb57b90 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 26 Oct 2025 20:54:52 +0100 Subject: [PATCH 11/31] Vector 0.0.3 --- Moose Development/Moose/Core/Vector.lua | 70 ++++++++++++++++--- Moose Development/Moose/Globals.lua | 7 ++ Moose Development/Moose/Wrapper/Group.lua | 17 +++++ .../Moose/Wrapper/Positionable.lua | 21 ++++++ 4 files changed, 105 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Core/Vector.lua b/Moose Development/Moose/Core/Vector.lua index 3ee2cc1e7..086cfd6c6 100644 --- a/Moose Development/Moose/Core/Vector.lua +++ b/Moose Development/Moose/Core/Vector.lua @@ -133,7 +133,7 @@ VECTOR = { --- VECTOR class version. -- @field #string version -VECTOR.version="0.0.2" +VECTOR.version="0.0.3" --- VECTOR private index. -- @field #VECTOR __index @@ -528,16 +528,25 @@ end --- Get MGRS coordinates of this vector. -- @param #VECTOR self --- @param #VECTOR Vector Vector from which the heading is requested. --- @return #number Latitude --- @return #number Longitude +-- @return #number Easting +-- @return #number Northing function VECTOR:GetMGRS() local lat, long=self:GetLatitudeLongitude() local mrgs=coord.LLtoMGRS(lat, long) + + -- Example table returned by coord.LLtoMGRS + --[[ + MGRS = { + UTMZone = string, + MGRSDigraph = string, + Easting = number, + Northing = number + } + ]] - return mrgs.Easing, mrgs.Northing + return mrgs.Easting, mrgs.Northing end --- Get the difference of the heading of this vector w. @@ -857,7 +866,7 @@ end -- * RUNWAY = 5 -- -- @param #VECTOR self --- @return #number Surface Type +-- @return #number Surface type function VECTOR:GetSurfaceType() local vec2=self:GetVec2() @@ -867,6 +876,32 @@ function VECTOR:GetSurfaceType() return s end + +--- Get the name of surface type at the vector. +-- +-- * LAND = 1 +-- * SHALLOW_WATER = 2 +-- * WATER = 3 +-- * ROAD = 4 +-- * RUNWAY = 5 +-- +-- @param #VECTOR self +-- @return #string Surface type name +function VECTOR:GetSurfaceTypeName() + + local vec2=self:GetVec2() + + local s=land.getSurfaceType(vec2) + + for name,id in land.SurfaceType() do + if id==s then + return name + end + end + + return "unknown" +end + --- Check if a given vector has line of sight with this vector. -- @param #VECTOR self -- @param #VECTOR Vec The other vector. @@ -1052,6 +1087,7 @@ function VECTOR:IlluminationBomb(Power) trigger.action.illuminationBomb(vec3, Power or 1000) + return self end --- Creates an explosion at a given point at the specified power. @@ -1085,14 +1121,28 @@ end --- Creates a arrow from this VECTOR to another vector on the F10 map. -- @param #VECTOR self -- @param #VECTOR Vector The vector defining the endpoint. --- @return #VECTOR self -function VECTOR:ArrowToAll(Vector) +-- @param #number Coalition Coalition Id: -1=All, 0=Neutral, 1=Red, 2=Blue. Default -1. +-- @param #table Color RGB color with alpha {r, g, b, alpha}. Default {1, 0, 0, 0.7}. +-- @param #table FillColor RGB color with alpha {r, g, b, alpha}. Default {1, 0, 0, 0.5}. +-- @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. +-- @return #number Marker ID. Can be used to remove the drawing. +function VECTOR:ArrowToAll(Vector, Coalition, Color, FillColor, LineType) local vec3Start=self:GetVec3() + local vec3End=Vector:GetVec3() - trigger.action.arrowToAll(coalition , id, vec3Start, vec3End, color, fillColor , lineType, readOnly, "") + local id=UTILS.GetMarkID() + + Coalition=Coalition or -1 + Color=Color or {1,0,0,0.7} + FillColor=FillColor or {1,0,0,0.5} + LineType=LineType or 1 + + local readOnly=false + + trigger.action.arrowToAll(Coalition , id, vec3Start, vec3End, Color, FillColor , LineType, readOnly, "") - return self + return id end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Globals.lua b/Moose Development/Moose/Globals.lua index 771547060..5108254d1 100644 --- a/Moose Development/Moose/Globals.lua +++ b/Moose Development/Moose/Globals.lua @@ -20,6 +20,13 @@ _DATABASE:_RegisterCargos() _DATABASE:_RegisterZones() _DATABASE:_RegisterAirbases() +--- Function that writes to DCS log file +-- @param #string text Formatted text. +-- @param ... Format passed to string.format(). +function printf(text, ...) + env.info(string.format(text, ...)) +end + --- Check if os etc is available. BASE:I("Checking de-sanitization of os, io and lfs:") local __na = false diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 1c38db672..37b120863 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1152,6 +1152,23 @@ function GROUP:GetVec3() return nil end +--- Returns the current {@Core.Vector#VECTOR} of the first Unit in the GROUP. +-- @param #GROUP self +-- @return Core.Vector#VECTOR Vector of the first Unit of the GROUP or nil if cannot be found. +function GROUP:GetVector() + + -- Get first unit. + local unit=self:GetUnit(1) + + if unit then + local vector=unit:GetVector() + return vector + end + + self:E("ERROR: Cannot get Vector of group "..tostring(self.GroupName)) + return nil +end + --- Returns the average Vec3 vector of the Units in the GROUP. -- @param #GROUP self -- @return DCS#Vec3 Current Vec3 of the GROUP or nil if cannot be found. diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 038eefe56..13008b613 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -286,6 +286,27 @@ function POSITIONABLE:GetVec2() return nil end +--- Returns the @{Core.Vector#VECTOR} indicating the 3D position of the object on the map. +-- @param #POSITIONABLE self +-- @return Core.Vector#VECTOR The 3D vector. +function POSITIONABLE:GetVector() + + local DCSPositionable = self:GetDCSObject() + + if DCSPositionable then + + local Vec3 = DCSPositionable:getPoint() -- DCS#Vec3 + + local vector=VECTOR:NewFromVec(Vec3) + + return vector + end + + self:E( { "Cannot GetVec2", Positionable = self, Alive = self:IsAlive() } ) + + return nil +end + --- Returns a COORDINATE object indicating the point in 2D of the POSITIONABLE within the mission. -- @param #POSITIONABLE self -- @return Core.Point#COORDINATE The 3D point vector of the POSITIONABLE. From 3686e0de639fe1b8c2fa14966d5bf0e57aa68e50 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 26 Oct 2025 23:00:32 +0100 Subject: [PATCH 12/31] Vector --- Moose Development/Moose/Core/Vector.lua | 80 +++++++++++++++++---- Moose Development/Moose/Utilities/Utils.lua | 16 +++++ 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Core/Vector.lua b/Moose Development/Moose/Core/Vector.lua index 086cfd6c6..68ead6645 100644 --- a/Moose Development/Moose/Core/Vector.lua +++ b/Moose Development/Moose/Core/Vector.lua @@ -135,6 +135,9 @@ VECTOR = { -- @field #string version VECTOR.version="0.0.3" +--- VECTOR unique ID +_VECTORID=0 + --- VECTOR private index. -- @field #VECTOR __index VECTOR.__index = VECTOR @@ -168,6 +171,10 @@ function VECTOR:New(x, y, z) else self=setmetatable({x=x or 0, y=y or 0, z=z or 0}, VECTOR) end + + _VECTORID=_VECTORID+1 + + self.uid=_VECTORID return self end @@ -578,7 +585,7 @@ end -- @param #VECTOR Vector The destination vector. -- @param #number Fraction The fraction (0,1) where the new vector is created. Default 0.5, *i.e.* in the middle. -- @return #VECTOR Vector between this and the other vector. -function VECTOR:GetIntermediateCoordinate(Vector, Fraction) +function VECTOR:GetIntermediateVector(Vector, Fraction) local f=Fraction or 0.5 @@ -590,13 +597,7 @@ function VECTOR:GetIntermediateCoordinate(Vector, Fraction) -- Set/scale the length. vec:SetLength(f*length) - - --TODO: Not sure what this was supposed to do?! --- if f>1 then --- local norm=UTILS.VecNorm(vec) --- f=Fraction/norm --- end - + -- Get to the desired position. vec=self+vec @@ -675,7 +676,7 @@ function VECTOR:AddVec(Vec) return self end ---- Substract a vector from this one. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. +--- Subtract a vector from this one. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. -- Note that if you want to add a VECTOR, you can also simply use the `-` operator. -- @param #VECTOR self -- @param DCS#Vec3 Vec Vector to substract. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. @@ -1057,8 +1058,61 @@ function VECTOR:GetTemperaturAndPressure() return t,p end +--- Creates a smoke at this vector. +-- @param #VECTOR self +-- @param #number Color Color of the smoke: 0=Green, 1=Red, 2=White, 3=Orange, 4=Blue. Default 0. +-- @param #number Duration (Optional) Duration of the smoke in seconds. Default nil. +function VECTOR:Smoke(Color, Duration) + + local vec3=self:GetVec3() + + Color=Color or 0 + + self.nameSmoke=string.format("Vector-Smoke-%d", self.uid) + + trigger.action.smoke(vec3, Color, self.nameSmoke) + + + if Duration and Duration>0 then + self:StopSmoke(Duration) + end + + return self +end + + +--- Stops smoke. +-- @param #VECTOR self +-- @return #VECTOR self +function VECTOR:StopSmoke(Delay) + + printf("stop smoke") + + if Delay and Delay>0 then + timer.scheduleFunction(VECTOR.StopSmoke, self, timer.getTime()+Delay) + printf("stop smoke scheduled") + else + if self.nameSmoke then + trigger.action.effectSmokeStop(self.nameSmoke) + self.nameSmoke=nil + printf("stop smoke NOW") + end + end + + return self +end --- Creates a large smoke and fire effect of a specified type and density at this vector. +-- +-- * 1 = small smoke and fire +-- * 2 = medium smoke and fire +-- * 3 = large smoke and fire +-- * 4 = huge smoke and fire +-- * 5 = small smoke +-- * 6 = medium smoke +-- * 7 = large smoke +-- * 8 = huge smoke +-- -- @param #VECTOR self -- @param #number Preset Preset of smoke. Default `BIGSMOKEPRESET.LargeSmokeAndFire`. -- @param #number Density Density between [0,1]. Default 0.5. @@ -1126,10 +1180,10 @@ end -- @param #table FillColor RGB color with alpha {r, g, b, alpha}. Default {1, 0, 0, 0.5}. -- @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. -- @return #number Marker ID. Can be used to remove the drawing. -function VECTOR:ArrowToAll(Vector, Coalition, Color, FillColor, LineType) +function VECTOR:ArrowTo(Vector, Coalition, Color, FillColor, LineType) - local vec3Start=self:GetVec3() - local vec3End=Vector:GetVec3() + local vec3End=self:GetVec3() + local vec3Start=Vector:GetVec3() local id=UTILS.GetMarkID() @@ -1169,7 +1223,7 @@ function VECTOR.__add(a,b) return c end ---- Meta function to substract vectors. +--- Meta function to subtract vectors. -- @param #VECTOR a Vector a. -- @param #VECTOR b Vector b. -- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]-b[i] for i=x,y,z. diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 9d52ee972..a6c393484 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -3227,6 +3227,22 @@ function UTILS.BearingToCardinal(Heading) end end +--- Adjust given heading so that is is in [0, 360). +-- @param #number Heading The heading in degrees. +-- @return #number Adjust heading in [0,360). +function UTILS.AdjustHeading360(Heading) + + while Heading>=360 or Heading<0 do + if Heading>=360 then + Heading=Heading-360 + elseif Heading<0 then + Heading=Heading+360 + end + end + + return Heading +end + --- Create a BRAA NATO call string BRAA between two GROUP objects -- @param Wrapper.Group#GROUP FromGrp GROUP object -- @param Wrapper.Group#GROUP ToGrp GROUP object From e3cee8dafef0973d0975b99085879e44fed3ae84 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 28 Oct 2025 23:07:59 +0100 Subject: [PATCH 13/31] VECTOR --- Moose Development/Moose/Core/Vector.lua | 187 ++++++++++++------ .../Moose/Wrapper/Positionable.lua | 37 ++++ 2 files changed, 163 insertions(+), 61 deletions(-) diff --git a/Moose Development/Moose/Core/Vector.lua b/Moose Development/Moose/Core/Vector.lua index 68ead6645..47fdafac3 100644 --- a/Moose Development/Moose/Core/Vector.lua +++ b/Moose Development/Moose/Core/Vector.lua @@ -282,7 +282,7 @@ end -- @param #VECTOR self -- @param #number Latitude Latitude in decimal degrees. -- @param #number Longitude Longitude in decimal degrees. --- @param #number altitude (Optional) Altitude in meters. Default is the land height at the 2D position. +-- @param #number Altitude (Optional) Altitude in meters. Default is the land height at the 2D position. -- @return #VECTOR self function VECTOR:NewFromLLDD(Latitude, Longitude, Altitude) @@ -291,13 +291,11 @@ function VECTOR:NewFromLLDD(Latitude, Longitude, Altitude) -- Convert vec3 to coordinate object. self=VECTOR:NewFromVec(vec3) - --- -- Adjust height --- if Altitude==nil then --- self.y=self:GetSurfaceHeight() --- else --- self.y=Altitude --- end + + -- Adjust height + if Altitude then + self.y=Altitude + end return self end @@ -533,15 +531,17 @@ function VECTOR:GetLatitudeLongitude() end ---- Get MGRS coordinates of this vector. +--- Get MGRS information of this vector. +-- +-- `MGRS = {UTMZone = string, MGRSDigraph = string, Easting = number, Northing = number}` +-- -- @param #VECTOR self --- @return #number Easting --- @return #number Northing +-- @return #table MGRS table with `UTMZone`, `MGRSDiGraph`, `Easting` and `Northing` keys. function VECTOR:GetMGRS() local lat, long=self:GetLatitudeLongitude() - local mrgs=coord.LLtoMGRS(lat, long) + local mgrs=coord.LLtoMGRS(lat, long) -- Example table returned by coord.LLtoMGRS --[[ @@ -553,7 +553,7 @@ function VECTOR:GetMGRS() } ]] - return mrgs.Easting, mrgs.Northing + return mgrs end --- Get the difference of the heading of this vector w. @@ -648,14 +648,13 @@ end --- Set z-component of vector. The z-axis points to the East. -- @param #VECTOR self -- @param #number z Value of z. Default 0. --- @param #VECTOR self +-- @return #VECTOR self function VECTOR:SetZ(z) self.z=z or 0 return self end - --- Add a vector to this. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. -- Note that if you want to add a VECTOR, you can also simply use the `+` operator. -- @param #VECTOR self @@ -677,7 +676,8 @@ function VECTOR:AddVec(Vec) end --- Subtract a vector from this one. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects. --- Note that if you want to add a VECTOR, you can also simply use the `-` operator. +-- +-- **Note** that if you want to add a VECTOR, you can also simply use the `-` operator. -- @param #VECTOR self -- @param DCS#Vec3 Vec Vector to substract. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object. -- @return #VECTOR self @@ -828,10 +828,14 @@ function VECTOR:Rotate2D(Angle, Copy) local x = X*sinPhi + Y*cosPhi -- Create new vector. - -- TODO: Copy argument - local vector=VECTOR:New(x, self.y, z) - - return vector + if Copy then + local vector=VECTOR:New(x, self.y, z) + return vector + else + self:SetX(x) + self:SetZ(z) + return self + end end @@ -1061,47 +1065,28 @@ end --- Creates a smoke at this vector. -- @param #VECTOR self -- @param #number Color Color of the smoke: 0=Green, 1=Red, 2=White, 3=Orange, 4=Blue. Default 0. --- @param #number Duration (Optional) Duration of the smoke in seconds. Default nil. +-- @param #number Duration (Optional) Duration of the smoke in seconds. Default nil. +-- @return #string Name of the smoke object. Can be used to stop it. function VECTOR:Smoke(Color, Duration) local vec3=self:GetVec3() Color=Color or 0 - self.nameSmoke=string.format("Vector-Smoke-%d", self.uid) - - trigger.action.smoke(vec3, Color, self.nameSmoke) + -- Create a name for the smoke object + local name=string.format("Vector-Smoke-%d", self.uid) + -- Create smoke at this position + trigger.action.smoke(vec3, Color, name) if Duration and Duration>0 then - self:StopSmoke(Duration) + self:StopSmoke(name, Duration) end - return self + return name end ---- Stops smoke. --- @param #VECTOR self --- @return #VECTOR self -function VECTOR:StopSmoke(Delay) - - printf("stop smoke") - - if Delay and Delay>0 then - timer.scheduleFunction(VECTOR.StopSmoke, self, timer.getTime()+Delay) - printf("stop smoke scheduled") - else - if self.nameSmoke then - trigger.action.effectSmokeStop(self.nameSmoke) - self.nameSmoke=nil - printf("stop smoke NOW") - end - end - - return self -end - --- Creates a large smoke and fire effect of a specified type and density at this vector. -- -- * 1 = small smoke and fire @@ -1116,29 +1101,62 @@ end -- @param #VECTOR self -- @param #number Preset Preset of smoke. Default `BIGSMOKEPRESET.LargeSmokeAndFire`. -- @param #number Density Density between [0,1]. Default 0.5. --- @return #string Name of the smoke. Can be used to stop it. -function VECTOR:SmokeAndFire(Preset, Density) +-- @param #number Duration (Optional) Duration of the smoke and fire in seconds. +-- @return #string Name of the smoke. Can be used to stop it. +function VECTOR:SmokeAndFire(Preset, Density, Duration) Preset=Preset or BIGSMOKEPRESET.LargeSmokeAndFire Density=Density or 0.5 local vec3=self:GetVec3() - --TODO: Get a unique name or pass name as parameter? + -- Get a name of this smoke & fire object + local name=string.format("Vector-Fire-%d", self.uid) - trigger.action.effectSmokeBig(vec3, Preset, Density, Name) + trigger.action.effectSmokeBig(vec3, Preset, Density, name) + + if Duration and Duration>0 then + self:StopSmoke(name, Duration) + end - return Name + return name end +--- Stop smoke or fire effect. +-- @param #VECTOR self +-- @param #string Name Name of the smoke object. +-- @param #number Delay Delay in seconds before the smoke is stopped. +-- @return #VECTOR self +function VECTOR:StopSmoke(Name, Delay) + + if Delay and Delay>0 then + TIMER:New(VECTOR.StopSmoke, self, Name):Start(Delay) + else + if Name then + trigger.action.effectSmokeStop(Name) + else + env.error(string.format("No name provided in VECTOR.StopSmoke function!")) + end + end + + return self +end + + --- Creates an illumination bomb at the specified point. -- @param #VECTOR self -- @param #number Power The power in Candela (cd). Should be between 1 and 1000000. Default 1000 cd. +-- @param #number Altitude (Optional) Altitude [m] at which the illumination bomb is created. -- @return #VECTOR self -function VECTOR:IlluminationBomb(Power) +function VECTOR:IlluminationBomb(Power, Altitude) local vec3=self:GetVec3() + + if Altitude then + vec3.y=Altitude + end + -- Create an illumination bomb. trigger.action.illuminationBomb(vec3, Power or 1000) return self @@ -1199,6 +1217,49 @@ function VECTOR:ArrowTo(Vector, Coalition, Color, FillColor, LineType) return id end + +--- Create mark on F10 map. +-- @param #VECTOR self +-- @param #string MarkText Free format text that shows the marking clarification. +-- @param #number Recipient Recipient of the mark: -1=All (default), 0=Neutral, 1=Red, 2=Blue. Can also be a `GROUP` object. +-- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @return #number Mark ID. +function VECTOR:Mark(MarkText, Recipient, ReadOnly) + + Recipient=Recipient or -1 + + if type(Recipient)=="number" then + + local MarkID = UTILS.GetMarkID() + + if Recipient==-1 then + trigger.action.markToAll(MarkID, MarkText, self:GetVec3(), ReadOnly, "") + elseif Recipient==0 then + trigger.action.markToCoalition(MarkID, MarkText, self:GetVec3(), coalition.side.NEUTRAL, ReadOnly, "") + elseif Recipient==1 then + trigger.action.markToCoalition(MarkID, MarkText, self:GetVec3(), coalition.side.RED, ReadOnly, "") + elseif Recipient==2 then + trigger.action.markToCoalition(MarkID, MarkText, self:GetVec3(), coalition.side.BLUE, ReadOnly, "") + end + + return MarkID + + elseif type(Recipient)=="table" then + + local MarkID = UTILS.GetMarkID() + + local group=Recipient --Wrapper.Group#GROUP + + trigger.action.markToGroup( MarkID, MarkText, self:GetVec3(), group:GetID(), ReadOnly, "") + + return MarkID + + end + + return nil +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Private Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1256,17 +1317,21 @@ function VECTOR.__mul(a, b) return c end ---- Meta function for dividing vectors by scalars. +--- Meta function for dividing a vector by a scalar or by another vector. -- @param #VECTOR a Vector a. --- @param #number b Number by which the components of the vector are divided. --- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]/b for i=x,y,z. +-- @param #VECTOR b Vector b. Can also be a #number. +-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]/b or c[i]=a[i]/b[i] for i=x,y,z. function VECTOR.__div(a, b) - assert(VECTOR._IsVector(a) and type(b) == "number", "div: wrong argument types (expected and )") - - env.info("FF __div") + assert(VECTOR._IsVector(a) and (type(b) == "number" or VECTOR._IsVector(b)), "div: wrong argument types (expected and ( or ))") - local c=VECTOR:New(a.x/b, a.y/b, a.z/b) + local c=nil --#VECTOR + + if type(b) == "number" then + c=VECTOR:New(a.x/b, a.y/b, a.z/b) + else + c=VECTOR:New(a.x/b.x, a.y/b.y, a.z/b.z) + end return c end @@ -1295,7 +1360,7 @@ end -- @param #VECTOR self -- @return #string String representation of vector. function VECTOR:__tostring() - local text=string.format("(x=%.1f, y=%.1f, z=%.1f) |v|=%.1f Phi=%4.1f°", self.x, self.y, self.z, self:GetLength(), self:GetHeading(false)) + local text=string.format("VECTOR: x=%.1f, y=%.1f, z=%.1f |v|=%.1f Phi=%4.1f°", self.x, self.y, self.z, self:GetLength(), self:GetHeading(false)) return text end diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 13008b613..3e980c0ab 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -219,6 +219,25 @@ function POSITIONABLE:GetOrientationZ() end end +--- Returns the vectors of the orientation of the object: +-- X is the orientation parallel to the movement of the object, Z perpendicular and Y vertical orientation. +-- @param #POSITIONABLE self +-- @return Core.Vector#VECTOR X orientation, i.e. parallel to the direction of movement. +-- @return Core.Vector#VECTOR Y orientation, i.e. vertical. +-- @return Core.Vector#VECTOR Z orientation, i.e. perpendicular to the direction of movement. +function POSITIONABLE:GetOrientationVectors() + local position = self:GetPosition() + if position then + local vecx=VECTOR:NewFromVec(position.x) + local vecy=VECTOR:NewFromVec(position.y) + local vecz=VECTOR:NewFromVec(position.z) + return vecx, vecy, vecz + else + BASE:E( { "Cannot GetOrientation", Positionable = self, Alive = self:IsAlive() } ) + return nil, nil, nil + end +end + --- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. -- @param #POSITIONABLE self -- @return DCS#Position The 3D position vectors of the POSITIONABLE. @@ -935,6 +954,24 @@ function POSITIONABLE:GetVelocityVec3() return nil end +--- Returns the velocity vector. +-- @param #POSITIONABLE self +-- @return Core.Vector#VECTOR The velocity vector or `nil` if the object does not exist. +function POSITIONABLE:GetVelocityVector() + + local DCSPositionable = self:GetDCSObject() + + if DCSPositionable and DCSPositionable:isExist() then + local vec3 = DCSPositionable:getVelocity() + local vector=VECTOR:NewFromVec(vec3) + return vector + end + + BASE:E( { "Cannot GetVelocityVector", Positionable = self, Alive = self:IsAlive() } ) + + return nil +end + --- Get relative velocity with respect to another POSITIONABLE. -- @param #POSITIONABLE self -- @param #POSITIONABLE Positionable Other POSITIONABLE. From 3479d0819341a9f42c4d110deeb8e67c547634d4 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 31 Oct 2025 14:16:20 +0100 Subject: [PATCH 14/31] VECTOR + PATHLINE --- Moose Development/Moose/Core/Pathline.lua | 81 +++++++++++++++++++- Moose Development/Moose/Core/Vector.lua | 29 +++++-- Moose Development/Moose/Navigation/Point.lua | 4 +- 3 files changed, 103 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Core/Pathline.lua b/Moose Development/Moose/Core/Pathline.lua index 8fdda2bc6..977cc5cbb 100644 --- a/Moose Development/Moose/Core/Pathline.lua +++ b/Moose Development/Moose/Core/Pathline.lua @@ -49,7 +49,7 @@ -- -- # Mark on F10 map -- --- The ponints of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and +-- The points of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and -- water depth. -- -- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`). @@ -69,11 +69,12 @@ PATHLINE = { -- @field #number landHeight Land height in meters. -- @field #number depth Water depth in meters. -- @field #number markerID Marker ID. +-- @field #number lineID Line marker ID. --- PATHLINE class version. -- @field #string version -PATHLINE.version="0.1.1" +PATHLINE.version="0.2.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -301,11 +302,35 @@ function PATHLINE:GetPoint2DFromIndex(n) return nil end +--- Calculate the length of the line. +-- @param #PATHLINE self +-- @param #boolean Project2D Calculate 2D distance between points. +-- @return #number Length in meters. +function PATHLINE:GetLength(Project2D) + local l=0 + + local np=#self.points + + for i=1,np-1 do + local p1=self.points[i] --#PATHLINE.Point + local p2=self.points[i+1] --#PATHLINE.Point + + if Project2D then + l=l+UTILS.VecDist2D(p1.vec2, p2.vec2) + else + l=l+UTILS.VecDist3D(p1.vec3, p2.vec3) + end + + end + + return l +end + --- Mark points on F10 map. -- @param #PATHLINE self -- @param #boolean Switch If `true` or nil, set marks. If `false`, remove marks. --- @return List of DCS#Vec3 points. +-- @return #PATHLINE self function PATHLINE:MarkPoints(Switch) for i,_point in pairs(self.points) do local point=_point --#PATHLINE.Point @@ -329,6 +354,56 @@ function PATHLINE:MarkPoints(Switch) end end + return self +end + +--- Draw line on F10 map. +-- @param #PATHLINE self +-- @param #number Recipient Recipent of the line: -1=All. +-- @param #table Color Color as RGB table plus alpha value. Default {1, 0, 0, 1.0}. +-- @param #number LineType Line type: 1=Solid (default). +-- @return #PATHLINE self +function PATHLINE:DrawLine(Recipient, Color, LineType) + + -- Input + Recipient= Recipient or -1 + Color= Color or {1,0,0, 1.0} + LineType=LineType or 1 + local ReadOnly=false + + + local np=#self.points + + for i=1,np-1 do + local p1=self.points[i] --#PATHLINE.Point + local p2=self.points[i+1] --#PATHLINE.Point + + p1.lineID = UTILS.GetMarkID() + + trigger.action.lineToAll(Recipient, p1.lineID, p1.vec3, p2.vec3, Color, LineType, ReadOnly, "") + + end + + return self +end + +--- Remove line on F10 map. +-- @param #PATHLINE self +-- @param #number Delay Delay in seconds before line is removed. +-- @return #PATHLINE self +function PATHLINE:UnDrawLine(Delay) + + + local np=#self.points + + for _,_point in pairs(self.points) do + local p=_point --#PATHLINE.Point + if p.lineID then + UTILS.RemoveMark(p.lineID, Delay) + end + end + + return self end diff --git a/Moose Development/Moose/Core/Vector.lua b/Moose Development/Moose/Core/Vector.lua index 47fdafac3..4cd588061 100644 --- a/Moose Development/Moose/Core/Vector.lua +++ b/Moose Development/Moose/Core/Vector.lua @@ -924,8 +924,7 @@ end --- Get a vector on the closest road. -- @param #VECTOR self --- @param #VECTOR Vec The other vector. --- @return #VECTOR Closest vector on a road. +-- @return #VECTOR Closest vector to a road. function VECTOR:GetClosestRoad() local vec2=self:GetVec2() @@ -940,18 +939,36 @@ function VECTOR:GetClosestRoad() return road end +--- Get a vector on the closest railroad. +-- @param #VECTOR self +-- @return #VECTOR Closest vector to a railroad. +function VECTOR:GetClosestRailroad() + + local vec2=self:GetVec2() + + local x,y=land.getClosestPointOnRoads('railroads', vec2.x, vec2.y) + + local road=nil + if x and y then + road=VECTOR:New(x, y) + end + + return road +end + --- Get the path on road from this vector to a given other vector. -- @param #VECTOR self -- @param #VECTOR Vec The destination vector. --- @return Core.Path#PATHLINE Pathline with points on road. +-- @return Core.Pathline#PATHLINE Pathline with points on road. function VECTOR:GetPathOnRoad(Vec) - local vec2=self:GetVec2() + local vec1=self:GetVec2() + local vec2=Vec:GetVec2() local path=nil - local vec2points=land.findPathOnRoads("roads", vec2.x , vec2.y, vec2.x, vec2.y) + local vec2points=land.findPathOnRoads("roads", vec1.x , vec1.y, vec2.x, vec2.y) if vec2points then path=PATHLINE:NewFromVec2Array("Road", vec2points) @@ -964,7 +981,7 @@ end --- Get profile of the land between the two passed points. -- @param #VECTOR self -- @param #VECTOR Vec3 The 3D destination vector. If a 2D vector is passed, `y` is set to the land height. --- @return Core.Path#PATHLINE Pathline with points of the profile. +-- @return Core.Pathline#PATHLINE Pathline with points of the profile. function VECTOR:GetProfile(Vec3) local vec3=self:GetVec3() diff --git a/Moose Development/Moose/Navigation/Point.lua b/Moose Development/Moose/Navigation/Point.lua index 976cc44ee..5a63a8487 100644 --- a/Moose Development/Moose/Navigation/Point.lua +++ b/Moose Development/Moose/Navigation/Point.lua @@ -2,8 +2,8 @@ -- -- **Main Features:** -- --- * Stuff --- * More Stuff +-- * Navigation Fixes +-- * Navigation Aids -- -- === -- From bef805d694609196377412f72c65edb5cc48dccf Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 31 Oct 2025 14:18:38 +0100 Subject: [PATCH 15/31] Update Pathline.lua --- Moose Development/Moose/Core/Pathline.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Pathline.lua b/Moose Development/Moose/Core/Pathline.lua index 977cc5cbb..bc6e9305e 100644 --- a/Moose Development/Moose/Core/Pathline.lua +++ b/Moose Development/Moose/Core/Pathline.lua @@ -337,7 +337,7 @@ function PATHLINE:MarkPoints(Switch) if Switch==false then if point.markerID then - UTILS.RemoveMark(point.markerID, Delay) + UTILS.RemoveMark(point.markerID) end else From 7cc18b80827c6277b6f4b229179f9b406abdb621 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 31 Oct 2025 19:13:36 +0100 Subject: [PATCH 16/31] Nav updates --- Moose Development/Moose/Core/Point.lua | 34 +++++++++ Moose Development/Moose/Core/Vector.lua | 11 +-- .../Moose/Navigation/Beacons.lua | 29 ++++++- Moose Development/Moose/Navigation/Point.lua | 4 +- Moose Development/Moose/Navigation/Radios.lua | 75 +++++++++++++++++-- Moose Development/Moose/Navigation/Towns.lua | 63 ++++++++++++---- 6 files changed, 185 insertions(+), 31 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b17e40f27..68eaae970 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2059,6 +2059,40 @@ do -- COORDINATE return Path, Way, GotPath end + + --- Returns a table of coordinates to a destination using only roads or railroads. + -- The first point is the closest point on road of the given coordinate. + -- By default, the last point is the closest point on road of the ToCoord. Hence, the coordinate itself and the final ToCoord are not necessarily included in the path. + -- @param #COORDINATE self + -- @param #COORDINATE ToCoord Coordinate of destination. + -- @param #boolean IncludeEndpoints (Optional) Include the coordinate itself and the ToCoordinate in the path. + -- @param #boolean Railroad (Optional) If true, path on railroad is returned. Default false. + -- @return Core.Pathline#PATHLINE Pathline containing the points on road. If no path on road can be found, nil is returned or just the endpoints. + function COORDINATE:GetPathlineOnRoad(ToCoord, IncludeEndpoints, Railroad) + + -- Set road type. + local RoadType="roads" + if Railroad==true then + RoadType="railroads" + end + + -- DCS API function returning a table of vec2. + local path = land.findPathOnRoads(RoadType, self.x, self.z, ToCoord.x, ToCoord.z) + + if IncludeEndpoints then + path=path or {} + table.insert(path, 1, self:GetVec2()) + table.insert(path, ToCoord:GetVec2()) + end + + local pathline=nil + if path then + pathline=PATHLINE:NewFromVec2Array(RoadType, path) + end + + return pathline + end + --- Gets the surface type at the coordinate. -- @param #COORDINATE self diff --git a/Moose Development/Moose/Core/Vector.lua b/Moose Development/Moose/Core/Vector.lua index 4cd588061..ce8ab3631 100644 --- a/Moose Development/Moose/Core/Vector.lua +++ b/Moose Development/Moose/Core/Vector.lua @@ -133,7 +133,7 @@ VECTOR = { --- VECTOR class version. -- @field #string version -VECTOR.version="0.0.3" +VECTOR.version="0.1.0" --- VECTOR unique ID _VECTORID=0 @@ -147,7 +147,7 @@ VECTOR.__index = VECTOR ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: 3D rotation --- TODO: Markers +-- DONE: Markers -- TODO: Documentation ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -966,10 +966,9 @@ function VECTOR:GetPathOnRoad(Vec) local vec1=self:GetVec2() local vec2=Vec:GetVec2() - local path=nil - local vec2points=land.findPathOnRoads("roads", vec1.x , vec1.y, vec2.x, vec2.y) + local path=nil if vec2points then path=PATHLINE:NewFromVec2Array("Road", vec2points) end @@ -1365,10 +1364,6 @@ end -- @param #VECTOR b Vector b. -- @return #boolean If `true`, both vectors are equal function VECTOR.__eq(a, b) - --assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "ERROR in VECTOR.__eq: wrong argument types: (expected and )") - env.info("FF __eq",showMessageBox) - BASE:I(a) - BASE:I(b) return a.x==b.x and a.y==b.y and a.z==b.z end diff --git a/Moose Development/Moose/Navigation/Beacons.lua b/Moose Development/Moose/Navigation/Beacons.lua index f22fecaf5..9c7acba8b 100644 --- a/Moose Development/Moose/Navigation/Beacons.lua +++ b/Moose Development/Moose/Navigation/Beacons.lua @@ -90,7 +90,7 @@ BEACONS = { --- BEACONS class version. -- @field #string version -BEACONS.version="0.0.4" +BEACONS.version="0.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -238,6 +238,33 @@ function BEACONS:GetClosestBeacon(Coordinate, TypeID, DistMax, ExcludeList) return beacon end +--- Find closest beacons to a given coordinate. +-- @param #BEACONS self +-- @param Core.Point#COORDINATE Coordinate The reference coordinate. +-- @param #number Nmax Max number of beacons. Default 5. +-- @param #number TypeID (Optional) Only search for specific beacon types, *e.g.* `BEACON.Type.TACAN`. +-- @param #number DistMax (Optional) Max search distance in meters. +-- @return #table Table of #BEACONS.Beacon closest beacons. +function BEACONS:GetClosestBeacons(Coordinate, Nmax, TypeID, DistMax) + + Nmax=Nmax or 5 + + local closest={} + for i=1,Nmax do + + local beacon=self:GetClosestBeacon(Coordinate, TypeID, DistMax, closest) + + if beacon then + table.insert(closest, beacon) + else + break + end + + end + + return closest +end + --- Get table of all beacons, optionally of a given type. -- @param #BEACONS self -- @param #number TypeID (Optional) Only return specific beacon types, *e.g.* `BEACON.Type.TACAN`. diff --git a/Moose Development/Moose/Navigation/Point.lua b/Moose Development/Moose/Navigation/Point.lua index 5a63a8487..2c8d1e378 100644 --- a/Moose Development/Moose/Navigation/Point.lua +++ b/Moose Development/Moose/Navigation/Point.lua @@ -100,7 +100,7 @@ NAVFIX.Type={ --- NAVFIX class version. -- @field #string version -NAVFIX.version="0.0.1" +NAVFIX.version="0.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -492,7 +492,7 @@ NAVAID = { --- NAVAID class version. -- @field #string version -NAVAID.version="0.0.1" +NAVAID.version="0.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list diff --git a/Moose Development/Moose/Navigation/Radios.lua b/Moose Development/Moose/Navigation/Radios.lua index 7e13e57e3..f5c3e0faa 100644 --- a/Moose Development/Moose/Navigation/Radios.lua +++ b/Moose Development/Moose/Navigation/Radios.lua @@ -3,6 +3,8 @@ -- **Main Features:** -- -- * Get radio frequencies of airbases +-- * Find closest airbase radios +-- * Mark radio frequencies on F10 map -- -- === -- @@ -64,7 +66,7 @@ -- @field #RADIOS RADIOS = { ClassName = "RADIOS", - verbose = 0, + verbose = 0, radios = {}, } @@ -78,6 +80,9 @@ RADIOS = { -- @field #table sceneObjects Scenery objects. -- @field #string name Name of the airbase. -- @field Wrapper.Airbase#AIRBASE airbase Airbase. +-- @field Core.Point#COORDINATE coordinate The COORDINATE of the radio. +-- @field DCS#Vec3 vec3 3D vector. +-- @field #number markerID Marker ID. --- Radio item data structure. -- @type RADIOS.Frequency @@ -87,7 +92,7 @@ RADIOS = { --- RADIOS class version. -- @field #string version -RADIOS.version="0.0.0" +RADIOS.version="0.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -112,9 +117,7 @@ function RADIOS:NewFromTable(RadioTable) for _,_radio in pairs(RadioTable) do local radio=_radio --#RADIOS.Radio - - --UTILS.PrintTableToLog(radio) - --UTILS.PrintTableToLog(radio.callsign) + -- The table structure of callsign is a bit awkward. We need to get the airbase name. local cs=radio.callsign[1] @@ -126,7 +129,6 @@ function RADIOS:NewFromTable(RadioTable) radio.name="Unknown" end - --UTILS.PrintTableToLog(radio.callsign) radio.name=self:_GetAirbaseName(airbasenames, radio.name) @@ -134,6 +136,7 @@ function RADIOS:NewFromTable(RadioTable) if radio.airbase then radio.coordinate=radio.airbase:GetCoordinate() + radio.vec3=radio.airbase:GetVec3() end -- Add to table @@ -198,6 +201,66 @@ function RADIOS:GetCoordinate(radio) return radio.coordinate end + +--- Find closest radio to a given coordinate. +-- @param #RADIOS self +-- @param Core.Point#COORDINATE Coordinate The reference coordinate. +-- @param #number DistMax (Optional) Max search distance in meters. +-- @param #table ExcludeList (Optional) List of radios to exclude. +-- @return #RADIOS.Radio The closest radio. +function RADIOS:GetClosestRadio(Coordinate, DistMax, ExcludeList) + + local radio=nil --#RADIOS.Radio + local distmin=math.huge + + ExcludeList=ExcludeList or {} + + for _,_radio in pairs(self.radios) do + local ra=_radio --#RADIOS.Radio + + if (not UTILS.IsInTable(ExcludeList, ra, "radioId")) then + + local dist=Coordinate:Get2DDistance(ra.coordinate) + + if dist Date: Fri, 31 Oct 2025 22:55:37 +0100 Subject: [PATCH 17/31] Update Radios.lua - Improved airbase identification by UID --- Moose Development/Moose/Navigation/Radios.lua | 53 ++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Navigation/Radios.lua b/Moose Development/Moose/Navigation/Radios.lua index f5c3e0faa..910235109 100644 --- a/Moose Development/Moose/Navigation/Radios.lua +++ b/Moose Development/Moose/Navigation/Radios.lua @@ -113,30 +113,41 @@ function RADIOS:NewFromTable(RadioTable) -- Inherit everything from BASE class. self=BASE:Inherit(self, BASE:New()) -- #RADIOS - local airbasenames=AIRBASE.GetAllAirbaseNames() + --local airbasenames=AIRBASE.GetAllAirbaseNames() + + -- Get all airdromes + local airdromes=AIRBASE.GetAllAirbases(nil, Airbase.Category.AIRDROME) for _,_radio in pairs(RadioTable) do local radio=_radio --#RADIOS.Radio - -- The table structure of callsign is a bit awkward. We need to get the airbase name. - local cs=radio.callsign[1] - if cs and cs.common then - radio.name=cs.common[1] - elseif cs and cs.nato then - radio.name=cs.nato[1] - else - radio.name="Unknown" + -- Note that unfortunately, the callsign does not always correspond to the airbase name. + if false then + local cs=radio.callsign[1] + if cs and cs.common then + radio.name=cs.common[1] + elseif cs and cs.nato then + radio.name=cs.nato[1] + else + radio.name="Unknown" + end + radio.name=self:_GetAirbaseName(airbasenames, radio.name) + radio.airbase=AIRBASE:FindByName(radio.name) end + -- Each radio item has a key radioId = 'airfield106_0', where 106 is the UID of the airbase. + -- So we can use that to get the airbase. + local aid = tonumber(string.match(radio.radioId, "airfield(%d+)_")) - radio.name=self:_GetAirbaseName(airbasenames, radio.name) - - radio.airbase=AIRBASE:FindByName(radio.name) + -- Get airbase + radio.airbase=self:_GetAirbaseByID(airdromes, aid) + -- Set other stuff if radio.airbase then radio.coordinate=radio.airbase:GetCoordinate() radio.vec3=radio.airbase:GetVec3() + radio.name=radio.airbase:GetName() end -- Add to table @@ -388,6 +399,24 @@ function RADIOS:_GetAirbaseName(airbasenames, name) return "Unknown" end +--- Get name of frequency band. +-- @param #RADIOS self +-- @param #table airbases Table of airbases. +-- @param #number aid Airbase ID. +-- @return Wrapper.Airbase#AIRBASE Airbase matching the ID or nil. +function RADIOS:_GetAirbaseByID(airbases, aid) + + for _,_airbase in pairs(airbases) do + local airbase=_airbase --Wrapper.Airbase#AIRBASE + local id=airbase:GetID(true) + if id==aid then + return airbase + end + end + + return nil +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From e194d6073f7cf8556774d06f291bf09a86191741 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 2 Nov 2025 22:45:52 +0100 Subject: [PATCH 18/31] OPS improvements - AUFTRAG success is checked based on really alive targets - AUFTRAG is done if all groups are done with the mission --- .../Moose/Functional/Warehouse.lua | 4 ++-- Moose Development/Moose/Ops/ArmyGroup.lua | 2 +- Moose Development/Moose/Ops/Auftrag.lua | 22 ++++++++++++++----- Moose Development/Moose/Ops/Legion.lua | 15 ++++++++----- Moose Development/Moose/Ops/OpsGroup.lua | 7 +++--- Moose Development/Moose/Ops/Target.lua | 18 ++++++++++----- 6 files changed, 45 insertions(+), 23 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 807f57f98..ea60abe46 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3918,7 +3918,7 @@ end -- @param #string assignment A free to choose string specifying an assignment for the asset. This can be used with the @{#WAREHOUSE.OnAfterNewAsset} function. -- @param #table other (Optional) Table of other useful data. Can be collected via WAREHOUSE.OnAfterNewAsset() function for example function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute, forcecargobay, forceweight, loadradius, skill, liveries, assignment, other) - self:T({group=group, ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) + --self:T({group=group:GetName(), ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) -- Set default. local n=ngroups or 1 @@ -6154,7 +6154,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol template.uncontrolled=uncontrolled -- Debug info. - self:T2({airtemplate=template}) + --self:T2({airtemplate=template}) -- Spawn group. local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index e5c822ed3..9e21b02af 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -1571,7 +1571,7 @@ end -- @param Core.Zone#ZONE Zone The zone to return to. -- @param #number Formation Formation of the group. function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation) - self:T2(self.lid.."onafterRTZ") + self:T(self.lid.."onafterRTZ") -- Zone. local zone=Zone or self.homezone diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index a425eacae..111543919 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -4440,7 +4440,7 @@ function AUFTRAG:Evaluate() local owndamage=self.Ncasualties/self.Nelements*100 -- Current number of mission targets. - local Ntargets=self:CountMissionTargets() + local Ntargets=self:CountMissionTargets(true) local Ntargets0=self:GetTargetInitialNumber() local Life=self:GetTargetLife() @@ -4891,16 +4891,25 @@ function AUFTRAG:CheckGroupsDone() self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] and reinfoce=%d. Mission NOT DONE!", self.status, self:GetState(), self.reinforce)) return false end + + local NopsgroupsAlive=self:CountOpsGroups() + local NopsgroupsDone=self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.DONE)+self:CountOpsGroupsInStatus(AUFTRAG.GroupStatus.CANCELLED) -- It could be that all groups were destroyed on the way to the mission execution waypoint. -- TODO: would be better to check if everybody is dead by now. - if self:IsStarted() and self:CountOpsGroups()==0 then + if self:IsStarted() and NopsgroupsAlive==0 then self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] but count of alive OPSGROUP is zero. Mission DONE!", self.status, self:GetState())) return true end - if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and self:CountOpsGroups()>0 then - self:T(self.lid..string.format("CheckGroupsDone: Mission is STARTED state %s [FSM=%s] and count of alive OPSGROUP > zero. Mission NOT DONE!", self.status, self:GetState())) + -- Every group alive is done or cancelled + if NopsgroupsAlive==NopsgroupsDone then + self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] but all groups [=%d] are done or cancelled. Mission DONE!", self.status, self:GetState(), NopsgroupsAlive)) + return true + end + + if (self:IsStarted() or self:IsExecuting()) and (fsmState == AUFTRAG.Status.STARTED or fsmState == AUFTRAG.Status.EXECUTING) and NopsgroupsAlive>0 then + self:T(self.lid..string.format("CheckGroupsDone: Mission is in state %s [FSM=%s] and count of alive OPSGROUP > zero. Mission NOT DONE!", self.status, self:GetState())) return false end @@ -5494,8 +5503,9 @@ end --- Count alive mission targets. -- @param #AUFTRAG self +-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones. -- @return #number Number of alive target units. -function AUFTRAG:CountMissionTargets() +function AUFTRAG:CountMissionTargets(OnlyReallyAlive) local N=0 @@ -5503,7 +5513,7 @@ function AUFTRAG:CountMissionTargets() local Coalitions=self.coalition and UTILS.GetCoalitionEnemy(self.coalition, true) or nil if self.engageTarget then - N=self.engageTarget:CountTargets(Coalitions) + N=self.engageTarget:CountTargets(Coalitions, OnlyReallyAlive) end return N diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 8e3a3f10c..513ed07f0 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -1901,7 +1901,7 @@ function LEGION:_TacticalOverview() for _,mtype in pairs(AUFTRAG.Type) do local n=self:CountMissionsInQueue(mtype) if n>0 then - local N=self:CountMissionsInQueue(mtype) + local N=self:CountMissionsInQueue(mtype, true) text=text..string.format(" - %s: %d [Running=%d]\n", mtype, n, N) end end @@ -2057,18 +2057,23 @@ end --- Count missions in mission queue. -- @param #LEGION self -- @param #table MissionTypes Types on mission to be checked. Default *all* possible types `AUFTRAG.Type`. +-- @param #boolean OnlyRunning If `true`, only count running missions. -- @return #number Number of missions that are not over yet. -function LEGION:CountMissionsInQueue(MissionTypes) +function LEGION:CountMissionsInQueue(MissionTypes, OnlyRunning) MissionTypes=MissionTypes or AUFTRAG.Type local N=0 for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG + + if (not OnlyRunning) or (mission.statusLegion~=AUFTRAG.Status.PLANNED) then - -- Check if this mission type is requested. - if mission:IsNotOver() and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then - N=N+1 + -- Check if this mission type is requested. + if mission:IsNotOver() and AUFTRAG.CheckMissionType(mission.type, MissionTypes) then + N=N+1 + end + end end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 24fc98f2d..ab20523d1 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -5718,7 +5718,7 @@ end function OPSGROUP:onafterMissionDone(From, Event, To, Mission) -- Debug info. - local text=string.format("Mission %s DONE!", Mission.name) + local text=string.format("Mission DONE %s!", Mission.name) self:T(self.lid..text) -- Set group status. @@ -6231,11 +6231,12 @@ function OPSGROUP:RouteToMission(mission, delay) self:T(self.lid.."Already in mission zone ==> TaskExecute()") self:TaskExecute(waypointtask) -- TODO: Calling PassingWaypoint here is probably better as it marks the mission waypoint as passed! - --self:PassingWaypoint(waypoint) + self:PassingWaypoint(waypoint) return elseif d<25 then self:T(self.lid.."Already within 25 meters of mission waypoint ==> TaskExecute()") self:TaskExecute(waypointtask) + self:PassingWaypoint(waypoint) return end @@ -7850,7 +7851,7 @@ function OPSGROUP:_Spawn(Delay, Template) self:ScheduleOnce(Delay, OPSGROUP._Spawn, self, 0, Template) else -- Debug output. - self:T2({Template=Template}) + --self:T2({Template=Template}) if self:IsArmygroup() and self.ValidateAndRepositionGroundUnits then UTILS.ValidateAndRepositionGroundUnits(Template.units) diff --git a/Moose Development/Moose/Ops/Target.lua b/Moose Development/Moose/Ops/Target.lua index 4863f5202..5a3b75464 100644 --- a/Moose Development/Moose/Ops/Target.lua +++ b/Moose Development/Moose/Ops/Target.lua @@ -2006,9 +2006,10 @@ end --- Count alive objects. -- @param #TARGET self -- @param #TARGET.Object Target Target objective. --- @param #table Coalitions (Optional) Only count targets of the given coalition(s). +-- @param #table Coalitions (Optional) Only count targets of the given coalition(s). +-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones. -- @return #number Number of alive target objects. -function TARGET:CountObjectives(Target, Coalitions) +function TARGET:CountObjectives(Target, Coalitions, OnlyReallyAlive) local N=0 @@ -2067,13 +2068,17 @@ function TARGET:CountObjectives(Target, Coalitions) -- No target, where we can check the alive status, so we assume it is alive. Changed this because otherwise target count is 0 if we pass a coordinate. -- This is also more consitent with the life and is alive status. - N=N+1 + if not OnlyReallyAlive then + N=N+1 + end elseif Target.Type==TARGET.ObjectType.ZONE then -- No target, where we can check the alive status, so we assume it is alive. Changed this because otherwise target count is 0 if we pass a coordinate. -- This is also more consitent with the life and is alive status. - N=N+1 + if not OnlyReallyAlive then + N=N+1 + end elseif Target.Type==TARGET.ObjectType.OPSZONE then @@ -2093,15 +2098,16 @@ end --- Count alive targets. -- @param #TARGET self -- @param #table Coalitions (Optional) Only count targets of the given coalition(s). +-- @param #boolean OnlyReallyAlive (Optional) If `true`, count only really alive targets (units, groups) but not coordinates or zones. -- @return #number Number of alive target objects. -function TARGET:CountTargets(Coalitions) +function TARGET:CountTargets(Coalitions, OnlyReallyAlive) local N=0 for _,_target in pairs(self.targets) do local Target=_target --#TARGET.Object - N=N+self:CountObjectives(Target, Coalitions) + N=N+self:CountObjectives(Target, Coalitions, OnlyReallyAlive) end From 832941494bd2e4cd2f51e3db46b18e967e23003a Mon Sep 17 00:00:00 2001 From: Shafik Date: Wed, 5 Nov 2025 12:35:55 +0200 Subject: [PATCH 19/31] [FIXED] `dcsgroup:getUnit(1)` nil pointer --- Moose Development/Moose/Wrapper/Group.lua | 36 +++++++++++++++-------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 1c38db672..0109a9719 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1058,9 +1058,12 @@ function GROUP:GetTypeName() local DCSGroup = self:GetDCSObject() if DCSGroup then - local GroupTypeName = DCSGroup:getUnit(1):getTypeName() - --self:T3( GroupTypeName ) - return( GroupTypeName ) + local unit = DCSGroup:getUnit(1) + if unit then + local GroupTypeName = unit:getTypeName() + --self:T3( GroupTypeName ) + return( GroupTypeName ) + end end return nil @@ -1075,9 +1078,12 @@ function GROUP:GetNatoReportingName() local DCSGroup = self:GetDCSObject() if DCSGroup then - local GroupTypeName = DCSGroup:getUnit(1):getTypeName() - --self:T3( GroupTypeName ) - return UTILS.GetReportingName(GroupTypeName) + local unit = DCSGroup:getUnit(1) + if unit then + local GroupTypeName = unit:getTypeName() + --self:T3( GroupTypeName ) + return UTILS.GetReportingName(GroupTypeName) + end end return "Bogey" @@ -1093,9 +1099,12 @@ function GROUP:GetPlayerName() local DCSGroup = self:GetDCSObject() if DCSGroup then - local PlayerName = DCSGroup:getUnit(1):getPlayerName() - --self:T3( PlayerName ) - return( PlayerName ) + local unit = DCSGroup:getUnit(1) + if unit then + local PlayerName = unit:getPlayerName() + --self:T3( PlayerName ) + return( PlayerName ) + end end return nil @@ -1111,9 +1120,12 @@ function GROUP:GetCallsign() local DCSGroup = self:GetDCSObject() if DCSGroup then - local GroupCallSign = DCSGroup:getUnit(1):getCallsign() - --self:T3( GroupCallSign ) - return GroupCallSign + local unit = DCSGroup:getUnit(1) + if unit then + local GroupCallSign = unit:getCallsign() + --self:T3( GroupCallSign ) + return GroupCallSign + end end BASE:E( { "Cannot GetCallsign", Positionable = self, Alive = self:IsAlive() } ) From 74c3b9fbcbdd608818bb8b9c3cc2b5ecb3d0e9e6 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 5 Nov 2025 22:05:02 +0100 Subject: [PATCH 20/31] Update Auftrag.lua --- Moose Development/Moose/Ops/Auftrag.lua | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 111543919..689783f60 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -2210,7 +2210,7 @@ end -- **Note** that it is recommended to set the weapon range via the `OPSGROUP:AddWeaponRange()` function as this cannot be retrieved from the DCS API. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE Target Center of the firing solution. --- @param #number Nshots Number of shots to be fired. Default `#nil`. +-- @param #number Nshots Number of shots to be fired. Default `#nil`. If value is in (0,1), it is interpreted as per cent of available ammo. -- @param #number Radius Radius of the shells in meters. Default 100 meters. -- @param #number Altitude Altitude in meters. Can be used to setup a Barrage. Default `#nil`. -- @return #AUFTRAG self @@ -2705,33 +2705,33 @@ function AUFTRAG:NewFromTarget(Target, MissionType) local mission=nil --#AUFTRAG if MissionType==AUFTRAG.Type.ANTISHIP then - mission=self:NewANTISHIP(Target, Altitude) + mission=self:NewANTISHIP(Target) elseif MissionType==AUFTRAG.Type.ARTY then - mission=self:NewARTY(Target, Nshots, Radius) + mission=self:NewARTY(Target, 0.3) -- use 30% of the available ammo elseif MissionType==AUFTRAG.Type.BAI then - mission=self:NewBAI(Target, Altitude) + mission=self:NewBAI(Target) elseif MissionType==AUFTRAG.Type.BOMBCARPET then - mission=self:NewBOMBCARPET(Target, Altitude, CarpetLength) + mission=self:NewBOMBCARPET(Target) elseif MissionType==AUFTRAG.Type.BOMBING then - mission=self:NewBOMBING(Target, Altitude) + mission=self:NewBOMBING(Target) elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then - mission=self:NewBOMBRUNWAY(Target, Altitude) + mission=self:NewBOMBRUNWAY(Target) elseif MissionType==AUFTRAG.Type.STRAFING then - mission=self:NewSTRAFING(Target, Altitude) + mission=self:NewSTRAFING(Target) elseif MissionType==AUFTRAG.Type.CAS then - mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, Target:GetAverageCoordinate(), Heading, Leg, TargetTypes) + mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), nil, nil, Target:GetAverageCoordinate()) elseif MissionType==AUFTRAG.Type.CASENHANCED then - mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes) + mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000)) elseif MissionType==AUFTRAG.Type.INTERCEPT then mission=self:NewINTERCEPT(Target) elseif MissionType==AUFTRAG.Type.SEAD then - mission=self:NewSEAD(Target, Altitude) + mission=self:NewSEAD(Target) elseif MissionType==AUFTRAG.Type.STRIKE then - mission=self:NewSTRIKE(Target, Altitude) + mission=self:NewSTRIKE(Target) elseif MissionType==AUFTRAG.Type.ARMORATTACK then - mission=self:NewARMORATTACK(Target, Speed) + mission=self:NewARMORATTACK(Target) elseif MissionType==AUFTRAG.Type.GROUNDATTACK then - mission=self:NewGROUNDATTACK(Target, Speed, Formation) + mission=self:NewGROUNDATTACK(Target) else return nil end @@ -2848,7 +2848,7 @@ function AUFTRAG:NewAUTO(EngageGroup) if auftrag==AUFTRAG.Type.ANTISHIP then mission=AUFTRAG:NewANTISHIP(Target) elseif auftrag==AUFTRAG.Type.ARTY then - mission=AUFTRAG:NewARTY(Target) + mission=AUFTRAG:NewARTY(Target, 0.2) elseif auftrag==AUFTRAG.Type.AWACS then mission=AUFTRAG:NewAWACS(Coordinate, Altitude,Speed,Heading,Leg) elseif auftrag==AUFTRAG.Type.BAI then @@ -4399,7 +4399,7 @@ function AUFTRAG:onafterStatus(From, Event, To) -- Group info. if self.verbose>=3 then -- Data on assigned groups. - local text=string.format("Assets [N=%d,Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0) + local text=string.format("Assets [N=%d, Nassigned=%s, Ndead=%s]:", self.Nassets or 0, self.Nassigned or 0, self.Ndead or 0) for i,_asset in pairs(self.assets or {}) do local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem text=text..string.format("\n[%d] %s: spawned=%s, requested=%s, reserved=%s", i, asset.spawngroupname, tostring(asset.spawned), tostring(asset.requested), tostring(asset.reserved)) From 17d1cf673d62482f6992679615b2180adb19ee07 Mon Sep 17 00:00:00 2001 From: Shafik Date: Thu, 6 Nov 2025 12:23:13 +0200 Subject: [PATCH 21/31] [ADDED] IsRed, IsBlue and IsNeutral to IDENTIFIABLE --- .../Moose/Wrapper/Identifiable.lua | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 8561d5b2b..4f2928cb5 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -170,6 +170,27 @@ function IDENTIFIABLE:GetCoalition() return nil end +--- Returns true if identifiable is of RED coalition. +-- @param #IDENTIFIABLE self +-- @return #boolean If the identifiable is red. +function IDENTIFIABLE:IsRed() + return self:GetCoalition() == coalition.side.RED +end + +--- Returns true if identifiable is of BLUE coalition. +-- @param #IDENTIFIABLE self +-- @return #boolean If the identifiable is blue. +function IDENTIFIABLE:IsBlue() + return self:GetCoalition() == coalition.side.BLUE +end + +--- Returns true if identifiable is of NEUTRAL coalition. +-- @param #IDENTIFIABLE self +-- @return #boolean If the identifiable is neutral. +function IDENTIFIABLE:IsNeutral() + return self:GetCoalition() == coalition.side.NEUTRAL +end + --- Returns the name of the coalition of the Identifiable. -- @param #IDENTIFIABLE self -- @return #string The name of the coalition. From 1267a64fcb63c9df2dadd78cfebbd4f7553669c9 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 6 Nov 2025 23:26:30 +0100 Subject: [PATCH 22/31] Ops Navy Update --- .../Moose/Functional/Warehouse.lua | 5 -- Moose Development/Moose/Ops/ArmyGroup.lua | 7 +- Moose Development/Moose/Ops/Auftrag.lua | 68 ++++++++++++++++++- Moose Development/Moose/Ops/Chief.lua | 10 ++- Moose Development/Moose/Ops/NavyGroup.lua | 42 +++++++----- Moose Development/Moose/Ops/OpsGroup.lua | 25 ++++++- 6 files changed, 121 insertions(+), 36 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index ea60abe46..d0f262f88 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3340,7 +3340,6 @@ function WAREHOUSE:FindAssetInDB(group) if aid~=nil then local asset=_WAREHOUSEDB.Assets[aid] - self:T2({asset=asset}) if asset==nil then self:_ErrorMessage(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName()), 0) end @@ -4446,7 +4445,6 @@ end -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return #boolean If true, request is granted. function WAREHOUSE:onbeforeRequest(From, Event, To, Request) - self:T3({warehouse=self.alias, request=Request}) -- Distance from warehouse to requesting warehouse. local distance=self:GetCoordinate():Get2DDistance(Request.warehouse:GetCoordinate()) @@ -6153,9 +6151,6 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol -- Uncontrolled spawning. template.uncontrolled=uncontrolled - -- Debug info. - --self:T2({airtemplate=template}) - -- Spawn group. local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 9e21b02af..608a1c004 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -1841,8 +1841,6 @@ function ARMYGROUP:_UpdateEngageTarget() -- Check if target moved more than 100 meters or we do not have line of sight. if dist>100 or los==false then - --env.info("FF Update Engage Target Moved "..self.engage.Target:GetName()) - -- Update new position. self.engage.Coordinate:UpdateFromVec3(vec3) @@ -1852,13 +1850,14 @@ function ARMYGROUP:_UpdateEngageTarget() -- Remove current waypoint self:RemoveWaypointByID(self.engage.Waypoint.uid) - local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9) + -- Get new coordinate where to go. + local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.95) -- Add waypoint after current. self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Formation, true) -- Set if we want to resume route after reaching the detour waypoint. - self.engage.Waypoint.detour=0 + self.engage.Waypoint.detour=1 end diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 689783f60..6e58b56d3 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -324,6 +324,10 @@ -- -- A ground attack mission can be created with the @{#AUFTRAG.NewGROUNDATTACK}() function. -- +-- ## NAVALENGAGEMENT +-- +-- A naval engagement mission can be created with the @{#AUFTRAG.NewNAVALENGAGEMENT}() function. +-- -- # Assigning Missions -- -- An AUFTRAG can be assigned to groups (FLIGHTGROUP, ARMYGROUP, NAVYGROUP), legions (AIRWING, BRIGADE, FLEET) or to a COMMANDER. @@ -443,6 +447,7 @@ _AUFTRAGSNR=0 -- @field #string HOVER Hover. -- @field #string LANDATCOORDINATE Land at coordinate. -- @field #string GROUNDATTACK Ground attack. +-- @field #string NAVALENGAGEMENT Naval engagement (similar to GROUNDATTACK). -- @field #string CARGOTRANSPORT Cargo transport. -- @field #string RELOCATECOHORT Relocate a cohort from one legion to another. -- @field #string AIRDEFENSE Air defense. @@ -491,6 +496,7 @@ AUFTRAG.Type={ HOVER="Hover", LANDATCOORDINATE="Land at Coordinate", GROUNDATTACK="Ground Attack", + NAVALENGAGEMENT="Naval Engagement", CARGOTRANSPORT="Cargo Transport", RELOCATECOHORT="Relocate Cohort", AIRDEFENSE="Air Defence", @@ -515,6 +521,7 @@ AUFTRAG.Type={ -- @field #string BARRAGE Barrage. -- @field #string HOVER Hover. -- @field #string GROUNDATTACK Ground attack. +-- @field #string NAVALENGAGEMENT Naval engagement. -- @field #string FERRY Ferry mission. -- @field #string RELOCATECOHORT Relocate cohort. -- @field #string AIRDEFENSE Air defense. @@ -537,6 +544,7 @@ AUFTRAG.SpecialTask={ ARMORATTACK="AmorAttack", HOVER="Hover", GROUNDATTACK="Ground Attack", + NAVALENGAGEMENT="Naval Engagement", FERRY="Ferry", RELOCATECOHORT="Relocate Cohort", AIRDEFENSE="Air Defense", @@ -666,7 +674,7 @@ AUFTRAG.Category={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="1.2.1" +AUFTRAG.version="1.3.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -2382,11 +2390,13 @@ function AUFTRAG:NewARMORATTACK(Target, Speed, Formation) return mission end ---- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack. +--- **[GROUND]** Create a GROUNDATTACK mission. Ground group(s) will go to a target object and attack at their own discretion. +-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft). +-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target. -- @param #AUFTRAG self -- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. -- @param #number Speed Speed in knots. Default max. --- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`. +-- @param #string Formation The attack formation, e.g. "Wedge", "Vee" etc. Default `ENUMS.Formation.Vehicle.Vee`. Only working for ground, not naval! -- @return #AUFTRAG self function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation) @@ -2413,6 +2423,38 @@ function AUFTRAG:NewGROUNDATTACK(Target, Speed, Formation) return mission end +--- **[NAVAL]** Create a NAVALENGAGEMENT mission. Naval group(s) will go to a target object and attack at their own discretion. +-- Unfortunately, the "Attack Group" and "Attack Unit" tasks do not work for ground and naval groups (only for aircraft). +-- Therefore, we resort to this workaround, which guides the attacking group to the vicinity of the target. Then they start shooting on their own, once they detect the target. +-- @param #AUFTRAG self +-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. +-- @param #number Speed Speed in knots. Default max. +-- @param #number Depth The attack depth in meters. Only for submarines! +-- @return #AUFTRAG self +function AUFTRAG:NewNAVALENGAGEMENT(Target, Speed, Depth) + + local mission=AUFTRAG:New(AUFTRAG.Type.NAVALENGAGEMENT) + + mission:_TargetFromObject(Target) + + mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.NAVALENGAGEMENT) + + -- Defaults. + mission.optionROE=ENUMS.ROE.OpenFire + mission.optionAlarm=ENUMS.AlarmState.Auto + mission.missionFraction=0.70 + mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil + mission.missionAltitude=Depth or 0 + + mission.categories={AUFTRAG.Category.NAVAL} + + mission.DCStask=mission:GetDCSMissionTask() + + mission.DCStask.params.speed=mission.missionSpeed and UTILS.KmphToMps(mission.missionSpeed) or nil + + return mission +end + --- **[AIR, GROUND, NAVAL]** Create a RECON mission. -- @param #AUFTRAG self -- @param Core.Set#SET_ZONE ZoneSet The recon zones. @@ -6652,6 +6694,26 @@ function AUFTRAG:GetDCSMissionTask() table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.NAVALENGAGEMENT then + + --------------------------- + -- NAVAL ENGAGEMENT Mission -- + --------------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.NAVALENGAGEMENT + + -- We create a "fake" DCS task and pass the parameters to the NAVYGROUP. + local param={} + param.target=self:GetTargetData() + param.speed=self.missionSpeed and UTILS.KmphToMps(self.missionSpeed) or nil + param.altitude=self.missionAltitude or 0 + + DCStask.params=param + + table.insert(DCStasks, DCStask) + elseif self.type==AUFTRAG.Type.AMMOSUPPLY then ------------------------- diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 3e9f46561..9500ae313 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -35,7 +35,7 @@ -- @field #number Nsuccess Number of successful missions. -- @field #number Nfailure Number of failed mission. -- @field #table assetNumbers Asset numbers. Each entry is a table of data type `#CHIEF.AssetNumber`. --- @extends Ops.Intel#INTEL +-- @extends Ops.Intelligence#INTEL --- *In preparing for battle I have always found that plans are useless, but planning is indispensable* -- Dwight D Eisenhower -- @@ -48,7 +48,7 @@ -- -- # Territory -- --- The chief class allows you to define boarder zones, conflict zones and attack zones. +-- The chief class allows you to define border zones, conflict zones and attack zones. -- -- ## Border Zones -- @@ -332,7 +332,7 @@ CHIEF.Strategy = { --- CHIEF class version. -- @field #string version -CHIEF.version="0.6.1" +CHIEF.version="0.7.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -2897,6 +2897,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) else + + -- Everything else table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.BAI, 100)) table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.GROUNDATTACK, 50)) @@ -2912,6 +2914,8 @@ function CHIEF:_GetMissionPerformanceFromTarget(Target) --- table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ANTISHIP, 100)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.NAVALENGAGEMENT, 50)) + table.insert(missionperf, self:_CreateMissionPerformance(AUFTRAG.Type.ARTY, 30)) else self:E(self.lid.."ERROR: Unknown Group category!") diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 85f03853d..24860af03 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -86,12 +86,14 @@ NAVYGROUP = { -- @field Ops.Target#TARGET Target The target. -- @field Core.Point#COORDINATE Coordinate Last known coordinate of the target. -- @field Ops.OpsGroup#OPSGROUP.Waypoint Waypoint the waypoint created to go to the target. +-- @field #number Speed Speed in knots. +-- @field #number Depth Depth of the engagement (submarines). -- @field #number roe ROE backup. -- @field #number alarmstate Alarm state backup. --- NavyGroup version. -- @field #string version -NAVYGROUP.version="1.0.3" +NAVYGROUP.version="1.0.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -1602,8 +1604,10 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Group#GROUP Group the group to be engaged. -function NAVYGROUP:onafterEngageTarget(From, Event, To, Target) +-- @param Ops.Target#TARGET Target The target to be engaged. Can also be a GROUP or UNIT object. +-- @param #number Speed Attack speed in knots. +-- @param #number Depth The depth in meters. Only for submarins. +function NAVYGROUP:onafterEngageTarget(From, Event, To, Target, Speed, Depth) self:T(self.lid.."Engaging Target") if Target:IsInstanceOf("TARGET") then @@ -1615,11 +1619,9 @@ function NAVYGROUP:onafterEngageTarget(From, Event, To, Target) -- Target coordinate. self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate()) - - local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9) + -- Get a coordinate close to the target. + local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.8) - - -- Backup ROE and alarm state. self.engage.roe=self:GetROE() self.engage.alarmstate=self:GetAlarmstate() @@ -1629,11 +1631,17 @@ function NAVYGROUP:onafterEngageTarget(From, Event, To, Target) self:SwitchROE(ENUMS.ROE.OpenFire) -- ID of current waypoint. - local uid=self:GetWaypointCurrent().uid + local uid=self:GetWaypointCurrentUID() + + -- Set formation. + self.engage.Depth=Depth or 0 + + -- Set speed. + self.engage.Speed=Speed -- Add waypoint after current. - self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true) - + self.engage.Waypoint=self:AddWaypoint(intercoord, Speed, uid, Depth, true) + -- Set if we want to resume route after reaching the detour waypoint. self.engage.Waypoint.detour=1 @@ -1656,24 +1664,22 @@ function NAVYGROUP:_UpdateEngageTarget() -- Check if target moved more than 100 meters. if dist>100 then - --env.info("FF Update Engage Target Moved "..self.engage.Target:GetName()) - -- Update new position. self.engage.Coordinate:UpdateFromVec3(vec3) -- ID of current waypoint. - local uid=self:GetWaypointCurrent().uid + local uid=self:GetWaypointCurrentUID() -- Remove current waypoint self:RemoveWaypointByID(self.engage.Waypoint.uid) - local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9) + local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.8) -- Add waypoint after current. - self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true) + self.engage.Waypoint=self:AddWaypoint(intercoord, self.engage.Speed, uid, self.engage.Depth, true) -- Set if we want to resume route after reaching the detour waypoint. - self.engage.Waypoint.detour=0 + self.engage.Waypoint.detour=1 end @@ -1709,8 +1715,8 @@ function NAVYGROUP:onafterDisengage(From, Event, To) local task=self:GetTaskCurrent() -- Get if current task is ground attack. - if task and task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK then - self:T(self.lid.."Disengage with current task GROUNDATTACK ==> Task Done!") + if task and (task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT) then + self:T(self.lid.."Disengage with current task GROUNDATTACK/NAVALENGAGEMENT ==> Task Done!") self:TaskDone(task) end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index ab20523d1..a1ea2fb68 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -1189,8 +1189,8 @@ function OPSGROUP:GetDCSObject() return self.dcsgroup end ---- Set detection on or off. --- If detection is on, detected targets of the group will be evaluated and FSM events triggered. +--- Make a target (unit, group, opsgroup) known to this group. +-- This is useing the DCS function `controller.knowTarget`. -- @param #OPSGROUP self -- @param Wrapper.Positionable#POSITIONABLE TargetObject The target object. -- @param #boolean KnowType Make type known. @@ -4513,6 +4513,25 @@ function OPSGROUP:_UpdateTask(Task, Mission) if target then self:EngageTarget(target, speed, Task.dcstask.params.formation) end + + elseif Task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT then + + --- + -- Task "Naval Engagement" Mission. + --- + + -- Engage target. + local target=Task.dcstask.params.target --Ops.Target#TARGET + + -- Set speed. Default max. + local speed=self.speedMax and UTILS.KmphToKnots(self.speedMax) or nil + if Task.dcstask.params.speed then + speed=UTILS.MpsToKnots(Task.dcstask.params.speed) + end + + if target then + self:EngageTarget(target, speed, Task.dcstask.params.altitude) + end elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then @@ -4886,7 +4905,7 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task) done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD or Task.dcstask.id==AUFTRAG.SpecialTask.ARMOREDGUARD then done=true - elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK then + elseif Task.dcstask.id==AUFTRAG.SpecialTask.GROUNDATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.ARMORATTACK or Task.dcstask.id==AUFTRAG.SpecialTask.NAVALENGAGEMENT then done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.NOTHING then done=true From caf2b4e73645fa605ddff7087d558870588e4723 Mon Sep 17 00:00:00 2001 From: leka1986 <83298840+leka1986@users.noreply.github.com> Date: Sat, 8 Nov 2025 02:47:56 +0100 Subject: [PATCH 23/31] Update CTLD.lua Added ability to drop "Sets" from 2 and up will be sets, else it will be like before. Changed _PackCratesNearby to handle all cargo within the range instead of the nearest. --- Moose Development/Moose/Ops/CTLD.lua | 210 +++++++++++++++++++-------- 1 file changed, 152 insertions(+), 58 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 372d32afc..062804662 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -1561,9 +1561,8 @@ function CTLD:New(Coalition, Prefixes, Alias) self.movetroopsdistance = 5000 self.returntroopstobase = true -- if set to false, troops would stay after deployment inside a load zone. self.troopdropzoneradius = 100 - + self.buildPairSeparation = 25 self.loadSavedCrates = true - self.VehicleMoveFormation = AI.Task.VehicleFormation.VEE -- added support Hercules Mod @@ -4235,23 +4234,62 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop) end -- let\'s get going if canbuild then + local notified=false -- loop again for _,_build in pairs(buildables) do local build = _build -- #CTLD.Buildable if build.CanBuild then - self:_CleanUpCrates(crates,build,number) - self:_RefreshLoadCratesMenu(Group,Unit) - if self.buildtime and self.buildtime > 0 then + local required = build.Required or 1 + if required < 1 then required = 1 end + local full = math.floor((build.Found or 0)/required) + if full < 1 then full = 1 end + + local sep = self.buildPairSeparation or 25 + local hdg = (Unit:GetHeading()+180)%360 + local lat = (hdg+90)%360 + local base = Unit:GetCoordinate():Translate(20,hdg) + + if full == 1 then + local cratesNow, numberNow = self:_FindCratesNearby(Group,Unit, finddist,true,true) + self:_CleanUpCrates(cratesNow,build,numberNow) + self:_RefreshLoadCratesMenu(Group,Unit) + if self.buildtime and self.buildtime > 0 then local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate(),MultiDrop) buildtimer:Start(self.buildtime) - self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) + if not notified then + self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) + notified=true + end self:__CratesBuildStarted(1,Group,Unit,build.Name) + else + self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop) + end else - self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop) + local start = -((full-1)*sep)/2 + for n=1,full do + local cratesNow, numberNow = self:_FindCratesNearby(Group,Unit, finddist,true,true) + self:_CleanUpCrates(cratesNow,build,numberNow) + self:_RefreshLoadCratesMenu(Group,Unit) + local off = start + (n-1)*sep + local coord = base:Translate(off,lat):GetVec2() + local b = { Name=build.Name, Required=build.Required, Template=build.Template, CanBuild=true, Type=build.Type, Coord=coord } + if self.buildtime and self.buildtime > 0 then + local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,b,false,Group:GetCoordinate(),MultiDrop) + buildtimer:Start(self.buildtime) + if not notified then + self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) + notified=true + end + self:__CratesBuildStarted(1,Group,Unit,build.Name) + else + self:_BuildObjectFromCrates(Group,Unit,b,false,nil,MultiDrop) + end + end end end end end + else if not Engineering then self:_SendMessage(string.format("No crates within %d meters!",finddist), 10, false, Group) end end -- number > 0 @@ -4273,28 +4311,37 @@ function CTLD:_PackCratesNearby(Group, Unit) -- get nearby vehicles local location = Group:GetCoordinate() -- get coordinate of group using function - local nearestGroups = SET_GROUP:New():FilterCoalitions("blue"):FilterZones({ZONE_RADIUS:New("TempZone", location:GetVec2(), self.PackDistance, false)}):FilterOnce() -- get all groups withing PackDistance from group using function - -- get template name of all vehicles in zone + local nearestGroups = SET_GROUP:New():FilterCoalitions("blue"):FilterZones({ZONE_RADIUS:New("TempZone", location:GetVec2(), self.PackDistance, false)}):FilterOnce() + + local packedAny = false -- determine if group is packable for _, _Group in pairs(nearestGroups.Set) do -- convert #SET_GROUP to a list of Wrapper.Group#GROUP + local didPackThisGroup = false for _, _Template in pairs(_DATABASE.Templates.Groups) do -- iterate through the database of templates - if (string.match(_Group:GetName(), _Template.GroupName)) then -- check if the Wrapper.Group#GROUP near the player is in the list of templates by name - -- generate crates and destroy group + if string.match(_Group:GetName(), _Template.GroupName) then -- check if the Wrapper.Group#GROUP near the player is in the list of templates by name for _, _entry in pairs(self.Cargo_Crates) do -- iterate through #CTLD_CARGO - if (_entry.Templates[1] == _Template.GroupName) then -- check if the #CTLD_CARGO matches the template name - _Group:Destroy() -- if a match is found destroy the Wrapper.Group#GROUP near the player + if _entry.Templates[1] == _Template.GroupName then -- check if the #CTLD_CARGO matches the template name + _Group:Destroy() self:_GetCrates(Group, Unit, _entry, nil, false, true) -- spawn the appropriate crates near the player self:_RefreshLoadCratesMenu(Group,Unit) -- call the refresher to show the crates in the menu self:__CratesPacked(1,Group,Unit,_entry) - return true + packedAny = true + didPackThisGroup = true + break end end end + if didPackThisGroup then break end end end + + if not packedAny then self:_SendMessage("Nothing to pack at this distance pilot!",10,false,Group) return false + end + + return true end --- (Internal) Function to repair nearby vehicles / FOBs @@ -4513,7 +4560,8 @@ function CTLD:_CleanUpCrates(Crates,Build,Number) if name == nametype then -- matching crate type table.insert(destIDs,thisID) found = found + 1 - nowcrate:GetPositionable():Destroy(false) + local pos = nowcrate:GetPositionable() + if pos then pos:Destroy(false) end nowcrate.Positionable = nil nowcrate.HasBeenDropped = false end @@ -5597,34 +5645,50 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) local needed=list[1]:GetCratesNeeded() or 1 table.sort(list,function(a,b)return a:GetID()=needed then + local sets=math.floor(#list/(needed>0 and needed or 1)) + if sets>0 then + local parentLabel=string.format("%d. %s (%d SET)",lineIndex,cName,sets) + local parentMenu=MENU_GROUP:New(Group,parentLabel,dropCratesMenu) + for s=1,sets do local chunk={} - for n=i,i+needed-1 do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s",lineIndex,cName) + for n=i,i+needed-1 do table.insert(chunk,list[n]) end table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) i=i+needed - else - local chunk={} - for n=i,#list do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) - table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - i=#list+1 end + for q=1,sets do + local qm=MENU_GROUP:New(Group,string.format("Drop %d Set%s",q,q>1 and "s" or ""),parentMenu) + MENU_GROUP_COMMAND:New(Group,"Drop",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + end,self,Group,Unit,cName,needed,q) + end + lineIndex=lineIndex+1 + end + if i<=#list then + local left=#list-i+1 + local chunk={} + for n=i,#list do table.insert(chunk,list[n]) end + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) lineIndex=lineIndex+1 end end else + -------------------------------------------------------------------- -------------------------------------------------------------------- -- one-step (enhanced) menu -------------------------------------------------------------------- @@ -5642,39 +5706,69 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) local needed=list[1]:GetCratesNeeded() or 1 table.sort(list,function(a,b)return a:GetID()=needed then + local sets=math.floor(#list/(needed>0 and needed or 1)) + if sets>0 then + local parentLabel=string.format("%d. %s (%d SET)",lineIndex,cName,sets) + local parentMenu=MENU_GROUP:New(Group,parentLabel,dropCratesMenu) + for s=1,sets do local chunk={} - for n=i,i+needed-1 do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s",lineIndex,cName) + for n=i,i+needed-1 do table.insert(chunk,list[n]) end table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - local mSet=MENU_GROUP:New(Group,label,dropCratesMenu) - MENU_GROUP_COMMAND:New(Group,"Drop",mSet,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then - MENU_GROUP_COMMAND:New(Group,"Drop and build",mSet,self._DropSingleAndBuild,self,Group,Unit,setIndex) - end i=i+needed - else - local chunk={} - for n=i,#list do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) - table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - i=#list+1 end + for q=1,sets do + local qm=MENU_GROUP:New(Group,string.format("Drop %d Set%s",q,q>1 and "s" or ""),parentMenu) + MENU_GROUP_COMMAND:New(Group,"Drop",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + end,self,Group,Unit,cName,needed,q) + if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then + MENU_GROUP_COMMAND:New(Group,"Drop and build",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + selfArg:_BuildCrates(GroupArg,UnitArg) + end,self,Group,Unit,cName,needed,q) + end + end + lineIndex=lineIndex+1 + end + if i<=#list then + local left=#list-i+1 + local chunk={} + for n=i,#list do table.insert(chunk,list[n]) end + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) lineIndex=lineIndex+1 end end end end + --- (Internal) Function to unload a single Troop group by ID. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group The calling group. @@ -8472,7 +8566,7 @@ end local injecttroops = CTLD_CARGO:New(nil,cargoname,cargotemplates,cargotype,true,true,size,nil,true,mass) self:InjectTroops(dropzone,injecttroops,self.surfacetypes,self.useprecisecoordloads,structure,timestamp) end - elseif self.loadSavedCrates and ((type(groupname) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR) then + elseif self.loadSavedCrates and (type(groupname) == "string" and groupname == "STATIC") or cargotype == CTLD_CARGO.Enum.REPAIR then local dropzone = ZONE_RADIUS:New("DropZone",vec2,20) local injectstatic = nil if cargotype == CTLD_CARGO.Enum.VEHICLE or cargotype == CTLD_CARGO.Enum.FOB then From aa4cf37aed31e29e6d1d186cc8d2df6f84dc046a Mon Sep 17 00:00:00 2001 From: leka1986 <83298840+leka1986@users.noreply.github.com> Date: Sat, 8 Nov 2025 02:52:23 +0100 Subject: [PATCH 24/31] Clean up comments in CTLD.lua Removed unnecessary comment lines in CTLD.lua. --- Moose Development/Moose/Ops/CTLD.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 062804662..481ae557f 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -5688,7 +5688,6 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) end else - -------------------------------------------------------------------- -------------------------------------------------------------------- -- one-step (enhanced) menu -------------------------------------------------------------------- From 7b44e7a3f645a969a7e4a3316b558b91861cdaf8 Mon Sep 17 00:00:00 2001 From: leka1986 <83298840+leka1986@users.noreply.github.com> Date: Sun, 9 Nov 2025 02:16:02 +0100 Subject: [PATCH 25/31] Update CTLD.lua Removed quantity menu if there is only one set. --- Moose Development/Moose/Ops/CTLD.lua | 72 ++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index 481ae557f..b29ac8866 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -5655,9 +5655,8 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) table.insert(self.CrateGroupList[Unit:GetName()],chunk) i=i+needed end - for q=1,sets do - local qm=MENU_GROUP:New(Group,string.format("Drop %d Set%s",q,q>1 and "s" or ""),parentMenu) - MENU_GROUP_COMMAND:New(Group,"Drop",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + if sets==1 then + MENU_GROUP_COMMAND:New(Group,"Drop",parentMenu,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) local uName=UnitArg:GetName() for k=1,qty do local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] @@ -5671,7 +5670,26 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) if not idx then break end selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) end - end,self,Group,Unit,cName,needed,q) + end,self,Group,Unit,cName,needed,1) + else + for q=1,sets do + local qm=MENU_GROUP:New(Group,string.format("Drop %d Set%s",q,q>1 and "s" or ""),parentMenu) + MENU_GROUP_COMMAND:New(Group,"Drop",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + end,self,Group,Unit,cName,needed,q) + end end lineIndex=lineIndex+1 end @@ -5715,9 +5733,8 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) table.insert(self.CrateGroupList[Unit:GetName()],chunk) i=i+needed end - for q=1,sets do - local qm=MENU_GROUP:New(Group,string.format("Drop %d Set%s",q,q>1 and "s" or ""),parentMenu) - MENU_GROUP_COMMAND:New(Group,"Drop",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + if sets==1 then + MENU_GROUP_COMMAND:New(Group,"Drop",parentMenu,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) local uName=UnitArg:GetName() for k=1,qty do local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] @@ -5731,9 +5748,9 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) if not idx then break end selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) end - end,self,Group,Unit,cName,needed,q) + end,self,Group,Unit,cName,needed,1) if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then - MENU_GROUP_COMMAND:New(Group,"Drop and build",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + MENU_GROUP_COMMAND:New(Group,"Drop and build",parentMenu,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) local uName=UnitArg:GetName() for k=1,qty do local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] @@ -5748,7 +5765,44 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit) selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) end selfArg:_BuildCrates(GroupArg,UnitArg) + end,self,Group,Unit,cName,needed,1) + end + else + for q=1,sets do + local qm=MENU_GROUP:New(Group,string.format("Drop %d Set%s",q,q>1 and "s" or ""),parentMenu) + MENU_GROUP_COMMAND:New(Group,"Drop",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end end,self,Group,Unit,cName,needed,q) + if not ( self:IsUnitInAir(Unit) and self:IsFixedWing(Unit) ) then + MENU_GROUP_COMMAND:New(Group,"Drop and build",qm,function(selfArg,GroupArg,UnitArg,cNameArg,neededArg,qty) + local uName=UnitArg:GetName() + for k=1,qty do + local lst=selfArg.CrateGroupList and selfArg.CrateGroupList[uName] + if not lst then break end + local idx=nil + for j=1,#lst do + local ch=lst[j] + local first=ch and ch[1] + if first and (not first:WasDropped()) and first:GetName()==cNameArg and #ch>=neededArg then idx=j break end + end + if not idx then break end + selfArg:_UnloadSingleCrateSet(GroupArg,UnitArg,idx) + end + selfArg:_BuildCrates(GroupArg,UnitArg) + end,self,Group,Unit,cName,needed,q) + end end end lineIndex=lineIndex+1 From 22097987dc2e5c3df22583534935d31863d67cbd Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 9 Nov 2025 14:43:11 +0100 Subject: [PATCH 26/31] #CTLD, #DYNAMICCARGO - C-130j-30 additions --- Moose Development/Moose/Ops/CTLD.lua | 8 +++++--- .../Moose/Wrapper/DynamicCargo.lua | 17 ++++++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index b29ac8866..42e8d4e03 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -913,7 +913,7 @@ do -- -- Make a Gazelle into a heavy truck, this type can load both crates and troops and eight of each type, up to 4000 kgs: -- my_ctld:SetUnitCapabilities("SA342L", true, true, 8, 8, 12, 4000) -- --- -- Default unit type capabilities are: +-- -- Default unit type capabilities are e.g. (list might be incomplete) -- ["SA342Mistral"] = {type="SA342Mistral", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12, cargoweightlimit = 400}, -- ["SA342L"] = {type="SA342L", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 12, cargoweightlimit = 400}, -- ["SA342M"] = {type="SA342M", crates=false, troops=true, cratelimit = 0, trooplimit = 4, length = 12, cargoweightlimit = 400}, @@ -1396,7 +1396,8 @@ CTLD.UnitTypeCapabilities = { ["Ka-50_3"] = {type="Ka-50_3", crates=false, troops=false, cratelimit = 0, trooplimit = 0, length = 15, cargoweightlimit = 0}, ["Mi-24P"] = {type="Mi-24P", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700}, ["Mi-24V"] = {type="Mi-24V", crates=true, troops=true, cratelimit = 2, trooplimit = 8, length = 18, cargoweightlimit = 700}, - ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers. + ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers. + ["C-130J-30"] = {type="C-130J-30", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 35, cargoweightlimit = 21500}, -- 19t cargo, 64 paratroopers. --Actually it's longer, but the center coord is off-center of the model. ["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats ["UH-60L_DAP"] = {type="UH-60L_DAP", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 16, cargoweightlimit = 500}, -- UH-60L DAP is an attack helo but can do limited CSAR and CTLD @@ -1417,11 +1418,12 @@ CTLD.FixedWingTypes = { ["Hercules"] = "Hercules", ["Bronco"] = "Bronco", ["Mosquito"] = "Mosquito", + ["C-130J-30"] = "C-130J-30", } --- CTLD class version. -- @field #string version -CTLD.version="1.3.38" +CTLD.version="1.3.39" --- Instantiate a new CTLD. -- @param #CTLD self diff --git a/Moose Development/Moose/Wrapper/DynamicCargo.lua b/Moose Development/Moose/Wrapper/DynamicCargo.lua index eb033972e..8c4987f72 100644 --- a/Moose Development/Moose/Wrapper/DynamicCargo.lua +++ b/Moose Development/Moose/Wrapper/DynamicCargo.lua @@ -108,8 +108,9 @@ DYNAMICCARGO.State = { -- @type DYNAMICCARGO.AircraftTypes DYNAMICCARGO.AircraftTypes = { ["CH-47Fbl1"] = "CH-47Fbl1", - ["Mi-8MTV2"] = "CH-47Fbl1", - ["Mi-8MT"] = "CH-47Fbl1", + ["Mi-8MTV2"] = "Mi-8MTV2", + ["Mi-8MT"] = "Mi-8MT", + ["C-130J-30"] = "C-130J-30", } --- Helo types possible. @@ -122,23 +123,29 @@ DYNAMICCARGO.AircraftDimensions = { ["length"] = 11, ["ropelength"] = 30, }, - ["Mi-8MTV2"] = { + ["Mi-8MTV2"] = { ["width"] = 6, ["height"] = 6, ["length"] = 15, ["ropelength"] = 30, }, - ["Mi-8MT"] = { + ["Mi-8MT"] = { ["width"] = 6, ["height"] = 6, ["length"] = 15, ["ropelength"] = 30, }, + ["C-130J-30"] = { + ["width"] = 4, + ["height"] = 12, + ["length"] = 35, + ["ropelength"] = 0, + }, } --- DYNAMICCARGO class version. -- @field #string version -DYNAMICCARGO.version="0.0.9" +DYNAMICCARGO.version="0.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list From ea60795e848683307b3fae227d429b1c7f87fc54 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 9 Nov 2025 16:21:38 +0100 Subject: [PATCH 27/31] Assets - remove assets from warehouse if they are removed from cohort --- Moose Development/Moose/Core/Fsm.lua | 1 + .../Moose/Functional/Warehouse.lua | 2 + Moose Development/Moose/Ops/Cohort.lua | 46 +++++++++++-------- Moose Development/Moose/Ops/OpsGroup.lua | 10 ++-- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index af2e971fe..e86b88267 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -79,6 +79,7 @@ do -- FSM + --- FSM class -- @type FSM -- @field #string ClassName Name of the class. -- @field Core.Scheduler#SCHEDULER CallScheduler Call scheduler. diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d0f262f88..d5dbaec92 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -8596,6 +8596,8 @@ function WAREHOUSE:_DeleteStockItem(stockitem) local item=self.stock[i] --#WAREHOUSE.Assetitem if item.uid==stockitem.uid then table.remove(self.stock,i) + -- remove also from warehouse DB + _WAREHOUSEDB.Assets[stockitem.uid]=nil break end end diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index c18d838d6..48f63112c 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -629,32 +629,42 @@ end --- Remove assets from pool. Not that assets must not be spawned or already reserved or requested. -- @param #COHORT self -- @param #number N Number of assets to be removed. Default 1. +-- @param #number Delay Delay in seconds before assets are removed. -- @return #COHORT self -function COHORT:RemoveAssets(N) +function COHORT:RemoveAssets(N, Delay) self:T2(self.lid..string.format("Remove %d assets of Cohort", N)) - - N=N or 1 - local n=0 - for i=#self.assets,1,-1 do - local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem + if Delay and Delay>0 then + -- Delayed call + self:ScheduleOnce(Delay, COHORT.RemoveAssets, self, N, 0) + else - self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname)) - if not (asset.requested or asset.spawned or asset.isReserved) then - self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname)) - table.remove(self.assets, i) - n=n+1 - else - self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname)) + N=N or 1 + + local n=0 + for i=#self.assets,1,-1 do + local asset=self.assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem + + self:T2(self.lid..string.format("Checking removing asset %s", asset.spawngroupname)) + if not (asset.requested or asset.spawned or asset.isReserved) then + self:T2(self.lid..string.format("Removing asset %s", asset.spawngroupname)) + -- Remove from warehouse and warehouse DB + asset.legion:_DeleteStockItem(asset) + table.remove(self.assets, i) + n=n+1 + else + self:T2(self.lid..string.format("Could NOT Remove asset %s", asset.spawngroupname)) + end + + if n>=N then + break + end + end - if n>=N then - break - end + self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets)) end - - self:T(self.lid..string.format("Removed %d/%d assets. New asset count=%d", n, N, #self.assets)) return self end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index a1ea2fb68..168359dd2 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -13466,10 +13466,10 @@ function OPSGROUP:GetAmmoUnit(unit, display) local ammotable=unit:GetAmmo() if ammotable then - local weapons=#ammotable - --self:I(ammotable) + --self:I(ammotable) + --UTILS.PrintTableToLog(ammotable) -- Loop over all weapons. for w=1,weapons do @@ -13477,9 +13477,9 @@ function OPSGROUP:GetAmmoUnit(unit, display) -- Number of current weapon. local Nammo=ammotable[w]["count"] - -- Range in meters. Seems only to exist for missiles (not shells). - local rmin=ammotable[w]["desc"]["rangeMin"] or 0 - local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0 + -- Range in meters. Seems only to exist for missiles (not shells). + local rmin=ammotable[w]["desc"]["rangeMin"] or 0 + local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0 -- Type name of current weapon. local Tammo=ammotable[w]["desc"]["typeName"] From fbf83b3aed5c07384568a11278de743669f07545 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 9 Nov 2025 16:53:10 +0100 Subject: [PATCH 28/31] #SET - Rationalize `FilterCoalitions()` and allow values like `coalition.side.BLUE` --- Moose Development/Moose/Core/Set.lua | 230 ++++++++------------------- 1 file changed, 66 insertions(+), 164 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 2935391dc..c67de6ad4 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -89,11 +89,23 @@ do -- SET_BASE Index = {}, Database = nil, CallScheduler = nil, - + Filter = {}, + FilterCoalitionNumbers = { + [coalition.side.RED+1] = "red", + [coalition.side.BLUE+1] = "blue", + [coalition.side.NEUTRAL+1] = "neutral", + }, + FilterMeta = { + Coalitions = { + ["red"] = coalition.side.RED, + ["blue"] = coalition.side.BLUE, + ["neutral"] = coalition.side.NEUTRAL, + }, + }, } --- Filters - -- @type SET_BASE.Filters + -- @type SET_BASE.Filter -- @field #table Coalition Coalitions -- @field #table Prefix Prefixes. @@ -197,6 +209,32 @@ do -- SET_BASE return self end + + --- Builds a set of objects of same coalitions. + -- Possible current coalitions are red, blue and neutral. + -- @param #SET_BASE self + -- @param #string Coalitions Can take the following values: "red", "blue", "neutral" and coalition.side.RED, coalition.side.BLUE,coalition.side.NEUTRAL + -- @param #boolean Clear If `true`, clear any previously defined filters. + -- @return #SET_BASE self + function SET_BASE:FilterCoalitions( Coalitions, Clear ) + + if Clear or (not self.Filter.Coalitions) then + self.Filter.Coalitions = {} + end + + -- Ensure table. + if type(Coalitions) ~= "table" then Coalitions = {Coalitions} end + for CoalitionID, Coalition in pairs( Coalitions ) do + local coalition = Coalition + if type(Coalition) == "number" then + coalition = self.FilterCoalitionNumbers[Coalition+1] or "unknown" + --self:I("Filter Coaltion for "..coalition) + end + self.Filter.Coalitions[coalition] = coalition + end + + return self + end --- Finds an @{Core.Base#BASE} object based on the object Name. -- @param #SET_BASE self @@ -1343,21 +1381,6 @@ do -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_GROUP self - function SET_GROUP:FilterCoalitions( Coalitions, Clear ) - - if Clear or (not self.Filter.Coalitions) then - self.Filter.Coalitions = {} - end - - -- Ensure table. - Coalitions = UTILS.EnsureTable(Coalitions, false) - - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - - return self - end --- Builds a set of groups out of categories. -- Possible current categories are plane, helicopter, ground, ship. @@ -2381,25 +2404,13 @@ do -- SET_UNIT local UnitFound = self.Set[UnitName] return UnitFound end - + --- Builds a set of units of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_UNIT self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_UNIT self - function SET_UNIT:FilterCoalitions( Coalitions ) - self.Filter.Coalitions = {} - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - - return self - end --- Builds a set of units out of categories. -- Possible current categories are plane, helicopter, ground, ship. @@ -3597,25 +3608,13 @@ do -- SET_STATIC local StaticFound = self.Set[StaticName] return StaticFound end - + --- Builds a set of units of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_STATIC self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_STATIC self - function SET_STATIC:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end - + --- Builds a set of statics in zones. -- @param #SET_STATIC self @@ -4390,25 +4389,13 @@ do -- SET_CLIENT end return self end - + --- Builds a set of clients of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_CLIENT self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_CLIENT self - function SET_CLIENT:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end - + --- Builds a set of clients out of categories. -- Possible current categories are plane, helicopter, ground, ship. -- @param #SET_CLIENT self @@ -5106,24 +5093,12 @@ do -- SET_PLAYER local ClientFound = self.Set[PlayerName] return ClientFound end - + --- Builds a set of clients of coalitions joined by specific players. -- Possible current coalitions are red, blue and neutral. -- @param #SET_PLAYER self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_PLAYER self - function SET_PLAYER:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end --- Builds a set of players in zones. -- @param #SET_PLAYER self @@ -5592,24 +5567,12 @@ do -- SET_AIRBASE return RandomAirbase end - + --- Builds a set of airbases of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_AIRBASE self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_AIRBASE self - function SET_AIRBASE:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end --- Builds a set of airbases out of categories. -- Possible current categories are plane, helicopter, ground, ship. @@ -5896,7 +5859,7 @@ do -- SET_CARGO return self end - --- (R2.1) Add CARGO to SET_CARGO. + --- Add CARGO to SET_CARGO. -- @param Core.Set#SET_CARGO self -- @param Cargo.Cargo#CARGO Cargo A single cargo. -- @return Core.Set#SET_CARGO self @@ -5907,7 +5870,7 @@ do -- SET_CARGO return self end - --- (R2.1) Add CARGOs to SET_CARGO. + --- Add CARGOs to SET_CARGO. -- @param Core.Set#SET_CARGO self -- @param #string AddCargoNames A single name or an array of CARGO names. -- @return Core.Set#SET_CARGO self @@ -5922,7 +5885,7 @@ do -- SET_CARGO return self end - --- (R2.1) Remove CARGOs from SET_CARGO. + --- Remove CARGOs from SET_CARGO. -- @param Core.Set#SET_CARGO self -- @param Cargo.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names. -- @return Core.Set#SET_CARGO self @@ -5937,7 +5900,7 @@ do -- SET_CARGO return self end - --- (R2.1) Finds a Cargo based on the Cargo Name. + --- Finds a Cargo based on the Cargo Name. -- @param #SET_CARGO self -- @param #string CargoName -- @return Cargo.Cargo#CARGO The found Cargo. @@ -5946,26 +5909,14 @@ do -- SET_CARGO local CargoFound = self.Set[CargoName] return CargoFound end - - --- (R2.1) Builds a set of cargos of coalitions. + + --- Builds a set of cargos of coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_CARGO self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_CARGO self - function SET_CARGO:FilterCoalitions( Coalitions ) -- R2.1 - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end - - --- (R2.1) Builds a set of cargos of defined cargo types. + + --- Builds a set of cargos of defined cargo types. -- Possible current types are those types known within DCS world. -- @param #SET_CARGO self -- @param #string Types Can take those type strings known within DCS world. @@ -5983,7 +5934,7 @@ do -- SET_CARGO return self end - --- (R2.1) Builds a set of cargos of defined countries. + --- Builds a set of cargos of defined countries. -- Possible current countries are those known within DCS world. -- @param #SET_CARGO self -- @param #string Countries Can take those country strings known within DCS world. @@ -6019,7 +5970,7 @@ do -- SET_CARGO return self end - --- (R2.1) Starts the filtering. + --- Starts the filtering. -- @param #SET_CARGO self -- @return #SET_CARGO self function SET_CARGO:FilterStart() -- R2.1 @@ -6044,7 +5995,7 @@ do -- SET_CARGO return self end - --- (R2.1) Handles the Database to check on an event (birth) that the Object was added in the Database. + --- Handles the Database to check on an event (birth) that the Object was added in the Database. -- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA Event @@ -6056,7 +6007,7 @@ do -- SET_CARGO return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- (R2.1) Handles the Database to check on any event that Object exists in the Database. + --- Handles the Database to check on any event that Object exists in the Database. -- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA Event @@ -6068,7 +6019,7 @@ do -- SET_CARGO return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - --- (R2.1) Iterate the SET_CARGO and call an iterator function for each CARGO, providing the CARGO and optional parameters. + --- Iterate the SET_CARGO and call an iterator function for each CARGO, providing the CARGO and optional parameters. -- @param #SET_CARGO self -- @param #function IteratorFunction The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter. -- @return #SET_CARGO self @@ -6080,7 +6031,7 @@ do -- SET_CARGO return self end - --- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#COORDINATE}. + --- Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#COORDINATE}. -- @param #SET_CARGO self -- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest @{Cargo.Cargo#CARGO}. -- @return Cargo.Cargo#CARGO The closest @{Cargo.Cargo#CARGO}. @@ -6155,7 +6106,7 @@ do -- SET_CARGO return FirstCargo end - --- (R2.1) + --- -- @param #SET_CARGO self -- @param AI.AI_Cargo#AI_CARGO MCargo -- @return #SET_CARGO self @@ -6214,7 +6165,7 @@ do -- SET_CARGO return MCargoInclude end - --- (R2.1) Handles the OnEventNewCargo event for the Set. + --- Handles the OnEventNewCargo event for the Set. -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA EventData function SET_CARGO:OnEventNewCargo( EventData ) -- R2.1 @@ -6228,7 +6179,7 @@ do -- SET_CARGO end end - --- (R2.1) Handles the OnDead or OnCrash event for alive units set. + --- Handles the OnDead or OnCrash event for alive units set. -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA EventData function SET_CARGO:OnEventDeleteCargo( EventData ) -- R2.1 @@ -7315,28 +7266,11 @@ do -- SET_OPSZONE return self end - + --- Builds a set of groups of coalitions. Possible current coalitions are red, blue and neutral. -- @param #SET_OPSZONE self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral" or combinations as a table, for example `{"red", "neutral"}`. -- @return #SET_OPSZONE self - function SET_OPSZONE:FilterCoalitions(Coalitions) - - -- Create an empty set. - if not self.Filter.Coalitions then - self.Filter.Coalitions={} - end - - -- Ensure we got a table. - Coalitions=UTILS.EnsureTable(Coalitions, false) - - -- Set filter. - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - - return self - end --- Filters for the defined collection. -- @param #SET_OPSZONE self @@ -7930,26 +7864,6 @@ do -- SET_OPSGROUP -- @param #string Coalitions Can take the following values: "red", "blue", "neutral" or combinations as a table, for example `{"red", "neutral"}`. -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_OPSGROUP self - function SET_OPSGROUP:FilterCoalitions(Coalitions, Clear) - - -- Create an empty set. - if Clear or not self.Filter.Coalitions then - self.Filter.Coalitions={} - end - - -- Ensure we got a table. - if type(Coalitions)~="table" then - Coalitions = {Coalitions} - end - - -- Set filter. - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - - return self - end - --- Builds a set of groups out of categories. -- @@ -8921,24 +8835,12 @@ do -- SET_DYNAMICCARGO --self:T2( DCargoInclude ) return DCargoInclude end - + --- Builds a set of dynamic cargo of defined coalitions. -- Possible current coalitions are red, blue and neutral. -- @param #SET_DYNAMICCARGO self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then - self.Filter.Coalitions = {} - end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end - for CoalitionID, Coalition in pairs( Coalitions ) do - self.Filter.Coalitions[Coalition] = Coalition - end - return self - end --- Builds a set of dynamic cargo of defined dynamic cargo type names. -- @param #SET_DYNAMICCARGO self From 046bd37fd52641b3afe9cd00df7b711c29619c2e Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Sun, 9 Nov 2025 17:19:29 +0100 Subject: [PATCH 29/31] xx --- Moose Development/Moose/Core/Set.lua | 2364 +++++++++++++------------- 1 file changed, 1182 insertions(+), 1182 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index c67de6ad4..f9134d6a5 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -115,14 +115,14 @@ do -- SET_BASE -- @usage -- -- Define a new SET_BASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE. -- DBObject = SET_BASE:New() - function SET_BASE:New( Database ) + function SET_BASE:New(Database) -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New() ) -- Core.Set#SET_BASE + local self = BASE:Inherit(self, FSM:New()) -- Core.Set#SET_BASE self.Database = Database - self:SetStartState( "Started" ) + self:SetStartState("Started") --- Added Handler OnAfter for SET_BASE -- @function [parent=#SET_BASE] OnAfterAdded @@ -133,7 +133,7 @@ do -- SET_BASE -- @param #string ObjectName The name of the object. -- @param Object The object. - self:AddTransition( "*", "Added", "*" ) + self:AddTransition("*", "Added", "*") --- Removed Handler OnAfter for SET_BASE -- @function [parent=#SET_BASE] OnAfterRemoved @@ -144,7 +144,7 @@ do -- SET_BASE -- @param #string ObjectName The name of the object. -- @param Object The object. - self:AddTransition( "*", "Removed", "*" ) + self:AddTransition("*", "Removed", "*") self.YieldInterval = 10 self.TimeInterval = 0.001 @@ -152,9 +152,9 @@ do -- SET_BASE self.Set = {} self.Index = {} - self.CallScheduler = SCHEDULER:New( self ) + self.CallScheduler = SCHEDULER:New(self) - self:SetEventPriority( 2 ) + self:SetEventPriority(2) return self end @@ -203,8 +203,8 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:Clear(TriggerEvent) - for Name, Object in pairs( self.Set ) do - self:Remove( Name, not TriggerEvent ) + for Name, Object in pairs(self.Set) do + self:Remove(Name, not TriggerEvent) end return self @@ -216,7 +216,7 @@ do -- SET_BASE -- @param #string Coalitions Can take the following values: "red", "blue", "neutral" and coalition.side.RED, coalition.side.BLUE,coalition.side.NEUTRAL -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_BASE self - function SET_BASE:FilterCoalitions( Coalitions, Clear ) + function SET_BASE:FilterCoalitions(Coalitions, Clear) if Clear or (not self.Filter.Coalitions) then self.Filter.Coalitions = {} @@ -224,7 +224,7 @@ do -- SET_BASE -- Ensure table. if type(Coalitions) ~= "table" then Coalitions = {Coalitions} end - for CoalitionID, Coalition in pairs( Coalitions ) do + for CoalitionID, Coalition in pairs(Coalitions) do local coalition = Coalition if type(Coalition) == "number" then coalition = self.FilterCoalitionNumbers[Coalition+1] or "unknown" @@ -240,7 +240,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #string ObjectName -- @return Core.Base#BASE The Object found. - function SET_BASE:_Find( ObjectName ) + function SET_BASE:_Find(ObjectName) local ObjectFound = self.Set[ObjectName] return ObjectFound @@ -263,8 +263,8 @@ do -- SET_BASE local Names = {} - for Name, Object in pairs( self.Set ) do - table.insert( Names, Name ) + for Name, Object in pairs(self.Set) do + table.insert(Names, Name) end return Names @@ -278,8 +278,8 @@ do -- SET_BASE local Objects = {} - for Name, Object in pairs( self.Set ) do - table.insert( Objects, Object ) + for Name, Object in pairs(self.Set) do + table.insert(Objects, Object) end return Objects @@ -289,8 +289,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #string ObjectName -- @param #boolean NoTriggerEvent (Optional) When `true`, the :Remove() method will not trigger a **Removed** event. - function SET_BASE:Remove( ObjectName, NoTriggerEvent ) - --self:F2( { ObjectName = ObjectName } ) + function SET_BASE:Remove(ObjectName, NoTriggerEvent) + --self:F2({ ObjectName = ObjectName }) local TriggerEvent = true if NoTriggerEvent then @@ -303,9 +303,9 @@ do -- SET_BASE if Object then - for Index, Key in ipairs( self.Index ) do + for Index, Key in ipairs(self.Index) do if Key == ObjectName then - table.remove( self.Index, Index ) + table.remove(self.Index, Index) self.Set[ObjectName] = nil break end @@ -313,7 +313,7 @@ do -- SET_BASE -- When NoTriggerEvent is true, then no Removed event will be triggered. if TriggerEvent then - self:Removed( ObjectName, Object ) + self:Removed(ObjectName, Object) end end end @@ -323,10 +323,10 @@ do -- SET_BASE -- @param #string ObjectName The name of the object. -- @param Core.Base#BASE Object The object itself. -- @return Core.Base#BASE The added BASE Object. - function SET_BASE:Add( ObjectName, Object ) + function SET_BASE:Add(ObjectName, Object) -- Debug info. - --self:T2( { ObjectName = ObjectName, Object = Object } ) + --self:T2({ ObjectName = ObjectName, Object = Object }) -- Error ahndling if not ObjectName or ObjectName == "" then @@ -337,17 +337,17 @@ do -- SET_BASE -- Ensure that the existing element is removed from the Set before a new one is inserted to the Set if self.Set[ObjectName] then - self:Remove( ObjectName, true ) + self:Remove(ObjectName, true) end -- Add object to set. self.Set[ObjectName] = Object -- Add Object name to Index. - table.insert( self.Index, ObjectName ) + table.insert(self.Index, ObjectName) -- Trigger Added event. - self:Added( ObjectName, Object ) + self:Added(ObjectName, Object) return self end @@ -356,12 +356,12 @@ do -- SET_BASE -- @param #SET_BASE self -- @param Wrapper.Object#OBJECT Object -- @return Core.Base#BASE The added BASE Object. - function SET_BASE:AddObject( Object ) - --self:F2( Object.ObjectName ) + function SET_BASE:AddObject(Object) + --self:F2(Object.ObjectName) - --self:T( Object.UnitName ) - --self:T( Object.ObjectName ) - self:Add( Object.ObjectName, Object ) + --self:T(Object.UnitName) + --self:T(Object.ObjectName) + self:Add(Object.ObjectName, Object) end @@ -398,16 +398,16 @@ do -- SET_BASE -- @param #SET_BASE self -- @param Core.Set#SET_BASE SetB Set *B*. -- @return Core.Set#SET_BASE The union set, i.e. contains objects that are in set *A* **or** in set *B*. - function SET_BASE:GetSetUnion( SetB ) + function SET_BASE:GetSetUnion(SetB) local union = SET_BASE:New() - for _, ObjectA in pairs( self.Set ) do - union:AddObject( ObjectA ) + for _, ObjectA in pairs(self.Set) do + union:AddObject(ObjectA) end - for _, ObjectB in pairs( SetB.Set ) do - union:AddObject( ObjectB ) + for _, ObjectB in pairs(SetB.Set) do + union:AddObject(ObjectB) end return union @@ -436,7 +436,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @param Core.Set#SET_BASE SetB Set other set, called *B*. -- @return Core.Set#SET_BASE The set of objects that are in set *B* but **not** in this set *A*. - function SET_BASE:GetSetComplement( SetB ) + function SET_BASE:GetSetComplement(SetB) local complement = self:GetSetUnion(SetB) local intersection = self:GetSetIntersection(SetB) @@ -453,11 +453,11 @@ do -- SET_BASE -- @param Core.Set#SET_BASE SetA First set. -- @param Core.Set#SET_BASE SetB Set to be merged into first set. -- @return Core.Set#SET_BASE The set of objects that are included in SetA and SetB. - function SET_BASE:CompareSets( SetA, SetB ) + function SET_BASE:CompareSets(SetA, SetB) - for _, ObjectB in pairs( SetB.Set ) do - if SetA:IsIncludeObject( ObjectB ) then - SetA:Add( ObjectB ) + for _, ObjectB in pairs(SetB.Set) do + if SetA:IsIncludeObject(ObjectB) then + SetA:Add(ObjectB) end end @@ -468,12 +468,12 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #string ObjectName -- @return Core.Base#BASE - function SET_BASE:Get( ObjectName ) - --self:F( ObjectName ) + function SET_BASE:Get(ObjectName) + --self:F(ObjectName) local Object = self.Set[ObjectName] - --self:T3( { ObjectName, Object } ) + --self:T3({ ObjectName, Object }) return Object end @@ -483,7 +483,7 @@ do -- SET_BASE function SET_BASE:GetFirst() local ObjectName = self.Index[1] local FirstObject = self.Set[ObjectName] - --self:T3( { FirstObject } ) + --self:T3({ FirstObject }) return FirstObject end @@ -494,7 +494,7 @@ do -- SET_BASE local tablemax = table.maxn(self.Index) local ObjectName = self.Index[tablemax] local LastObject = self.Set[ObjectName] - --self:T3( { LastObject } ) + --self:T3({ LastObject }) return LastObject end @@ -508,7 +508,7 @@ do -- SET_BASE end --local tablemax = table.maxn(self.Index) local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] - --self:T3( { RandomItem } ) + --self:T3({ RandomItem }) return RandomItem end @@ -525,7 +525,7 @@ do -- SET_BASE --local tablemax = table.maxn(self.Index) --local RandomItem = self.Set[self.Index[math.random(1,tablemax)]] local RandomItem = sorted[math.random(1,tablemax)] - --self:T3( { RandomItem } ) + --self:T3({ RandomItem }) return RandomItem end @@ -540,10 +540,10 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #SET_BASE BaseSet -- @return #SET_BASE - function SET_BASE:SetDatabase( BaseSet ) + function SET_BASE:SetDatabase(BaseSet) -- Copy the filter criteria of the BaseSet - local OtherFilter = UTILS.DeepCopy( BaseSet.Filter ) + local OtherFilter = UTILS.DeepCopy(BaseSet.Filter) self.Filter = OtherFilter -- Now base the new Set on the BaseSet @@ -555,7 +555,7 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #number Limit Defines how many objects are evaluated of the set as part of the Some iterators. The default is 1. -- @return #SET_BASE self - function SET_BASE:SetSomeIteratorLimit( Limit ) + function SET_BASE:SetSomeIteratorLimit(Limit) self.SomeIteratorLimit = Limit or 1 @@ -592,10 +592,10 @@ do -- SET_BASE --self:Clear() - for ObjectName, Object in pairs( self.Database ) do + for ObjectName, Object in pairs(self.Database) do - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) else self:Remove(ObjectName, true) end @@ -621,16 +621,16 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:_FilterStart() - for ObjectName, Object in pairs( self.Database ) do + for ObjectName, Object in pairs(self.Database) do - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) end end -- Follow alive players and clients - -- self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) - -- self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) + -- self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit) + -- self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit) return self end @@ -640,7 +640,7 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:FilterDeads() -- R2.1 allow deads to be filtered to automatically handle deads in the collection. - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) return self end @@ -650,7 +650,7 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:FilterCrashes() -- R2.1 allow crashes to be filtered to automatically handle crashes in the collection. - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) return self end @@ -660,9 +660,9 @@ do -- SET_BASE -- @return #SET_BASE self function SET_BASE:FilterStop() - self:UnHandleEvent( EVENTS.Birth ) - self:UnHandleEvent( EVENTS.Dead ) - self:UnHandleEvent( EVENTS.Crash ) + self:UnHandleEvent(EVENTS.Birth) + self:UnHandleEvent(EVENTS.Dead) + self:UnHandleEvent(EVENTS.Crash) return self end @@ -672,19 +672,19 @@ do -- SET_BASE -- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object (but **not** a simple DCS#Vec2!) from where to evaluate the closest object in the set. -- @return Core.Base#BASE The closest object. -- @usage - -- myset:FindNearestObjectFromPointVec2( ZONE:New("Test Zone"):GetCoordinate() ) - function SET_BASE:FindNearestObjectFromPointVec2( Coordinate ) - --self:F2( Coordinate ) + -- myset:FindNearestObjectFromPointVec2(ZONE:New("Test Zone"):GetCoordinate()) + function SET_BASE:FindNearestObjectFromPointVec2(Coordinate) + --self:F2(Coordinate) local NearestObject = nil local ClosestDistance = nil - for ObjectID, ObjectData in pairs( self.Set ) do + for ObjectID, ObjectData in pairs(self.Set) do if NearestObject == nil then NearestObject = ObjectData - ClosestDistance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + ClosestDistance = Coordinate:DistanceFromPointVec2(ObjectData:GetCoordinate()) else - local Distance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + local Distance = Coordinate:DistanceFromPointVec2(ObjectData:GetCoordinate()) if Distance < ClosestDistance then NearestObject = ObjectData ClosestDistance = Distance @@ -700,15 +700,15 @@ do -- SET_BASE --- Handles the OnBirth event for the Set. -- @param #SET_BASE self -- @param Core.Event#EVENTDATA Event - function SET_BASE:_EventOnBirth( Event ) - --self:F3( { Event } ) + function SET_BASE:_EventOnBirth(Event) + --self:F3({ Event }) if Event.IniDCSUnit then - local ObjectName, Object = self:AddInDatabase( Event ) - --self:T3( ObjectName, Object ) - if Object and self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - -- self:_EventOnPlayerEnterUnit( Event ) + local ObjectName, Object = self:AddInDatabase(Event) + --self:T3(ObjectName, Object) + if Object and self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) + -- self:_EventOnPlayerEnterUnit(Event) end end end @@ -716,13 +716,13 @@ do -- SET_BASE --- Handles the OnDead or OnCrash event for alive units set. -- @param #SET_BASE self -- @param Core.Event#EVENTDATA Event - function SET_BASE:_EventOnDeadOrCrash( Event ) - --self:F( { Event } ) + function SET_BASE:_EventOnDeadOrCrash(Event) + --self:F({ Event }) if Event.IniDCSUnit then - local ObjectName, Object = self:FindInDatabase( Event ) + local ObjectName, Object = self:FindInDatabase(Event) if ObjectName then - self:Remove( ObjectName ) + self:Remove(ObjectName) end end end @@ -730,15 +730,15 @@ do -- SET_BASE --- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). -- @param #SET_BASE self -- @param Core.Event#EVENTDATA Event - -- function SET_BASE:_EventOnPlayerEnterUnit( Event ) - -- --self:F3( { Event } ) + -- function SET_BASE:_EventOnPlayerEnterUnit(Event) + -- --self:F3({ Event }) -- -- if Event.IniDCSUnit then - -- local ObjectName, Object = self:AddInDatabase( Event ) - -- self:T3( ObjectName, Object ) - -- if self:IsIncludeObject( Object ) then - -- self:Add( ObjectName, Object ) - -- --self:_EventOnPlayerEnterUnit( Event ) + -- local ObjectName, Object = self:AddInDatabase(Event) + -- self:T3(ObjectName, Object) + -- if self:IsIncludeObject(Object) then + -- self:Add(ObjectName, Object) + -- --self:_EventOnPlayerEnterUnit(Event) -- end -- end -- end @@ -746,15 +746,15 @@ do -- SET_BASE --- Handles the OnPlayerLeaveUnit event to clean the active players table. -- @param #SET_BASE self -- @param Core.Event#EVENTDATA Event - -- function SET_BASE:_EventOnPlayerLeaveUnit( Event ) - -- --self:F3( { Event } ) + -- function SET_BASE:_EventOnPlayerLeaveUnit(Event) + -- --self:F3({ Event }) -- -- local ObjectName = Event.IniDCSUnit -- if Event.IniDCSUnit then -- if Event.IniDCSGroup then -- local GroupUnits = Event.IniDCSGroup:getUnits() -- local PlayerCount = 0 - -- for _, DCSUnit in pairs( GroupUnits ) do + -- for _, DCSUnit in pairs(GroupUnits) do -- if DCSUnit ~= Event.IniDCSUnit then -- if DCSUnit:getPlayerName() ~= nil then -- PlayerCount = PlayerCount + 1 @@ -763,7 +763,7 @@ do -- SET_BASE -- end -- self:E(PlayerCount) -- if PlayerCount == 0 then - -- self:Remove( Event.IniDCSGroupName ) + -- self:Remove(Event.IniDCSGroupName) -- end -- end -- end @@ -779,43 +779,43 @@ do -- SET_BASE -- @param #function Function (Optional) A function returning a #boolean true/false. Only if true, the IteratorFunction is called. -- @param #table FunctionArguments (Optional) Function arguments. -- @return #SET_BASE self - function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArguments ) - --self:F3( arg ) + function SET_BASE:ForEach(IteratorFunction, arg, Set, Function, FunctionArguments) + --self:F3(arg) Set = Set or self:GetSet() arg = arg or {} local function CoRoutine() local Count = 0 - for ObjectID, ObjectData in pairs( Set ) do + for ObjectID, ObjectData in pairs(Set) do local Object = ObjectData - --self:T3( Object ) + --self:T3(Object) if Function then - if Function( unpack( FunctionArguments or {} ), Object ) == true then - IteratorFunction( Object, unpack( arg ) ) + if Function(unpack(FunctionArguments or {}), Object) == true then + IteratorFunction(Object, unpack(arg)) end else - IteratorFunction( Object, unpack( arg ) ) + IteratorFunction(Object, unpack(arg)) end Count = Count + 1 -- if Count % self.YieldInterval == 0 then - -- coroutine.yield( false ) + -- coroutine.yield(false) -- end end return true end - -- local co = coroutine.create( CoRoutine ) + -- local co = coroutine.create(CoRoutine) local co = CoRoutine local function Schedule() - -- local status, res = coroutine.resume( co ) + -- local status, res = coroutine.resume(co) local status, res = co() - --self:T3( { status, res } ) + --self:T3({ status, res }) if status == false then - error( res ) + error(res) end if res == false then return true -- resume next time the loop @@ -824,7 +824,7 @@ do -- SET_BASE return false end - -- self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + -- self.CallScheduler:Schedule(self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0) Schedule() return self @@ -834,8 +834,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #function IteratorFunction The function that will be called. -- @return #SET_BASE self - function SET_BASE:ForSome( IteratorFunction, arg, Set, Function, FunctionArguments ) - --self:F3( arg ) + function SET_BASE:ForSome(IteratorFunction, arg, Set, Function, FunctionArguments) + --self:F3(arg) Set = Set or self:GetSet() arg = arg or {} @@ -844,38 +844,38 @@ do -- SET_BASE local function CoRoutine() local Count = 0 - for ObjectID, ObjectData in pairs( Set ) do + for ObjectID, ObjectData in pairs(Set) do local Object = ObjectData - --self:T3( Object ) + --self:T3(Object) if Function then - if Function( unpack( FunctionArguments ), Object ) == true then - IteratorFunction( Object, unpack( arg ) ) + if Function(unpack(FunctionArguments), Object) == true then + IteratorFunction(Object, unpack(arg)) end else - IteratorFunction( Object, unpack( arg ) ) + IteratorFunction(Object, unpack(arg)) end Count = Count + 1 if Count >= Limit then break end -- if Count % self.YieldInterval == 0 then - -- coroutine.yield( false ) + -- coroutine.yield(false) -- end end return true end - -- local co = coroutine.create( CoRoutine ) + -- local co = coroutine.create(CoRoutine) local co = CoRoutine local function Schedule() - -- local status, res = coroutine.resume( co ) + -- local status, res = coroutine.resume(co) local status, res = co() - --self:T3( { status, res } ) + --self:T3({ status, res }) if status == false then - error( res ) + error(res) end if res == false then return true -- resume next time the loop @@ -884,7 +884,7 @@ do -- SET_BASE return false end - -- self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + -- self.CallScheduler:Schedule(self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0) Schedule() return self @@ -895,10 +895,10 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self - -- function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) - -- --self:F3( arg ) + -- function SET_BASE:ForEachDCSUnitAlive(IteratorFunction, ...) + -- --self:F3(arg) -- - -- self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive ) + -- self:ForEach(IteratorFunction, arg, self.DCSUnitsAlive) -- -- return self -- end @@ -907,10 +907,10 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self - -- function SET_BASE:ForEachPlayer( IteratorFunction, ... ) - -- --self:F3( arg ) + -- function SET_BASE:ForEachPlayer(IteratorFunction, ...) + -- --self:F3(arg) -- - -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) + -- self:ForEach(IteratorFunction, arg, self.PlayersAlive) -- -- return self -- end @@ -920,10 +920,10 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. ---- @return #SET_BASE self - -- function SET_BASE:ForEachClient( IteratorFunction, ... ) - -- --self:F3( arg ) + -- function SET_BASE:ForEachClient(IteratorFunction, ...) + -- --self:F3(arg) -- - -- self:ForEach( IteratorFunction, arg, self.Clients ) + -- self:ForEach(IteratorFunction, arg, self.Clients) -- -- return self -- end @@ -932,8 +932,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #table Object -- @return #SET_BASE self - function SET_BASE:IsIncludeObject( Object ) - --self:F3( Object ) + function SET_BASE:IsIncludeObject(Object) + --self:F3(Object) return true end @@ -942,8 +942,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #table Object -- @return #boolean `true` if object is in set and `false` otherwise. - function SET_BASE:IsInSet( Object ) - --self:F3( Object ) + function SET_BASE:IsInSet(Object) + --self:F3(Object) local outcome = false local name = Object:GetName() --self:I("SET_BASE: Objectname = "..name) @@ -954,7 +954,7 @@ do -- SET_BASE outcome = true end end - ) + ) return outcome end @@ -962,8 +962,8 @@ do -- SET_BASE -- @param #SET_BASE self -- @param #table Object -- @return #SET_BASE self - function SET_BASE:IsNotInSet( Object ) - --self:F3( Object ) + function SET_BASE:IsNotInSet(Object) + --self:F3(Object) return not self:IsInSet(Object) end @@ -974,7 +974,7 @@ do -- SET_BASE --self:F3() local ObjectNames = "" - for ObjectName, Object in pairs( self.Set ) do + for ObjectName, Object in pairs(self.Set) do ObjectNames = ObjectNames .. ObjectName .. ", " end @@ -985,14 +985,14 @@ do -- SET_BASE -- @param #SET_BASE self -- @param Core.Base#BASE MasterObject (Optional) The master object as a reference. -- @return #string A string with the names of the objects. - function SET_BASE:Flush( MasterObject ) + function SET_BASE:Flush(MasterObject) --self:F3() local ObjectNames = "" - for ObjectName, Object in pairs( self.Set ) do + for ObjectName, Object in pairs(self.Set) do ObjectNames = ObjectNames .. ObjectName .. ", " end - --self:F( { MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames } ) + --self:F({ MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames }) return ObjectNames end @@ -1005,7 +1005,7 @@ do -- SET_BASE local AliveSet = {} -- Clean the Set before returning with only the alive Objects. - for ObjectName, Object in pairs( self.Set ) do + for ObjectName, Object in pairs(self.Set) do if Object then if Object:IsAlive() then AliveSet[#AliveSet+1] = Object @@ -1098,12 +1098,12 @@ do -- -- -- Create the SetCarrier SET_GROUP collection. -- - -- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() + -- local SetHelicopter = SET_GROUP:New():FilterPrefixes("Helicopter"):FilterStart() -- -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. -- - -- function SetHelicopter:OnAfterDead( From, Event, To, GroupObject ) - -- --self:F( { GroupObject = GroupObject:GetName() } ) + -- function SetHelicopter:OnAfterDead(From, Event, To, GroupObject) + -- --self:F({ GroupObject = GroupObject:GetName() }) -- end -- -- While this is a good example, there is a catch. @@ -1115,15 +1115,15 @@ do -- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter. -- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER. -- - -- function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) + -- function AI_CARGO_DISPATCHER:New(SetCarrier, SetCargo, SetDeployZones) -- - -- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER + -- local self = BASE:Inherit(self, FSM:New()) -- #AI_CARGO_DISPATCHER -- -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. -- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration. -- - -- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, GroupObject ) - -- SetHelicopter:F( { GroupObject = GroupObject:GetName() } ) + -- function SetHelicopter.OnAfterDead(SetHelicopter, From, Event, To, GroupObject) + -- SetHelicopter:F({ GroupObject = GroupObject:GetName() }) -- self.PickupCargo[GroupObject] = nil -- So here I clear the PickupCargo table entry of the self object AI_CARGO_DISPATCHER. -- self.CarrierHome[GroupObject] = nil -- end @@ -1168,9 +1168,9 @@ do function SET_GROUP:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.GROUPS ) ) -- #SET_GROUP + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.GROUPS)) -- #SET_GROUP - self:FilterActive( false ) + self:FilterActive(false) return self @@ -1191,7 +1191,7 @@ do --local AliveSet = SET_GROUP:New() local AliveSet = {} -- Clean the Set before returning with only the alive Groups. - for GroupName, GroupObject in pairs( self.Set ) do + for GroupName, GroupObject in pairs(self.Set) do local GroupObject = GroupObject -- Wrapper.Group#GROUP if GroupObject then if GroupObject:IsAlive() then @@ -1214,9 +1214,9 @@ do local ReportUnitTypes = REPORT:New() - for GroupID, GroupData in pairs( self:GetSet() ) do + for GroupID, GroupData in pairs(self:GetSet()) do local Units = GroupData:GetUnits() - for UnitID, UnitData in pairs( Units ) do + for UnitID, UnitData in pairs(Units) do if UnitData:IsAlive() then local UnitType = UnitData:GetTypeName() @@ -1229,8 +1229,8 @@ do end end - for UnitTypeID, UnitType in pairs( UnitTypes ) do - ReportUnitTypes:Add( UnitType .. " of " .. UnitTypeID ) + for UnitTypeID, UnitType in pairs(UnitTypes) do + ReportUnitTypes:Add(UnitType .. " of " .. UnitTypeID) end return ReportUnitTypes @@ -1242,14 +1242,14 @@ do -- @param Wrapper.Group#GROUP group The group which should be added to the set. -- @param #boolean DontSetCargoBayLimit If true, do not attempt to auto-add the cargo bay limit per unit in this group. -- @return Core.Set#SET_GROUP self - function SET_GROUP:AddGroup( group, DontSetCargoBayLimit ) + function SET_GROUP:AddGroup(group, DontSetCargoBayLimit) - self:Add( group:GetName(), group ) + self:Add(group:GetName(), group) if not DontSetCargoBayLimit then -- I set the default cargo bay weight limit each time a new group is added to the set. -- TODO Why is this here in the first place? - for UnitID, UnitData in pairs( group:GetUnits() or {} ) do + for UnitID, UnitData in pairs(group:GetUnits() or {}) do if UnitData and UnitData:IsAlive() then UnitData:SetCargoBayWeightLimit() end @@ -1263,12 +1263,12 @@ do -- @param Core.Set#SET_GROUP self -- @param #string AddGroupNames A single name or an array of GROUP names. -- @return Core.Set#SET_GROUP self - function SET_GROUP:AddGroupsByName( AddGroupNames ) + function SET_GROUP:AddGroupsByName(AddGroupNames) - local AddGroupNamesArray = (type( AddGroupNames ) == "table") and AddGroupNames or { AddGroupNames } + local AddGroupNamesArray = (type(AddGroupNames) == "table") and AddGroupNames or { AddGroupNames } - for AddGroupID, AddGroupName in pairs( AddGroupNamesArray ) do - self:Add( AddGroupName, GROUP:FindByName( AddGroupName ) ) + for AddGroupID, AddGroupName in pairs(AddGroupNamesArray) do + self:Add(AddGroupName, GROUP:FindByName(AddGroupName)) end return self @@ -1278,12 +1278,12 @@ do -- @param Core.Set#SET_GROUP self -- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names. -- @return Core.Set#SET_GROUP self - function SET_GROUP:RemoveGroupsByName( RemoveGroupNames ) + function SET_GROUP:RemoveGroupsByName(RemoveGroupNames) - local RemoveGroupNamesArray = (type( RemoveGroupNames ) == "table") and RemoveGroupNames or { RemoveGroupNames } + local RemoveGroupNamesArray = (type(RemoveGroupNames) == "table") and RemoveGroupNames or { RemoveGroupNames } - for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do - self:Remove( RemoveGroupName ) + for RemoveGroupID, RemoveGroupName in pairs(RemoveGroupNamesArray) do + self:Remove(RemoveGroupName) end return self @@ -1293,7 +1293,7 @@ do -- @param #SET_GROUP self -- @param #string GroupName -- @return Wrapper.Group#GROUP The found Group. - function SET_GROUP:FindGroup( GroupName ) + function SET_GROUP:FindGroup(GroupName) local GroupFound = self.Set[GroupName] return GroupFound @@ -1303,20 +1303,20 @@ do -- @param #SET_GROUP self -- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest object in the set. -- @return Wrapper.Group#GROUP The closest group. - function SET_GROUP:FindNearestGroupFromPointVec2( Coordinate ) - --self:F2( Coordinate ) + function SET_GROUP:FindNearestGroupFromPointVec2(Coordinate) + --self:F2(Coordinate) local NearestGroup = nil -- Wrapper.Group#GROUP local ClosestDistance = nil local Set = self:GetAliveSet() - for ObjectID, ObjectData in pairs( Set ) do + for ObjectID, ObjectData in pairs(Set) do if NearestGroup == nil then NearestGroup = ObjectData - ClosestDistance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + ClosestDistance = Coordinate:DistanceFromPointVec2(ObjectData:GetCoordinate()) else - local Distance = Coordinate:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + local Distance = Coordinate:DistanceFromPointVec2(ObjectData:GetCoordinate()) if Distance < ClosestDistance then NearestGroup = ObjectData ClosestDistance = Distance @@ -1332,7 +1332,7 @@ do -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_GROUP self - function SET_GROUP:FilterZones( Zones, Clear ) + function SET_GROUP:FilterZones(Zones, Clear) if Clear or not self.Filter.Zones then self.Filter.Zones = {} @@ -1341,14 +1341,14 @@ do local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName) then - self:E( "***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!" ) + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then + self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _, Zone in pairs( zones ) do + for _, Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -1371,7 +1371,7 @@ do -- if grp:GetName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(groundset:Flush()) @@ -1388,17 +1388,17 @@ do -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_GROUP self - function SET_GROUP:FilterCategories( Categories, Clear ) + function SET_GROUP:FilterCategories(Categories, Clear) if Clear or not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end @@ -1409,7 +1409,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryGround() - self:FilterCategories( "ground" ) + self:FilterCategories("ground") return self end @@ -1417,7 +1417,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryAirplane() - self:FilterCategories( "plane" ) + self:FilterCategories("plane") return self end @@ -1425,7 +1425,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryHelicopter() - self:FilterCategories( "helicopter" ) + self:FilterCategories("helicopter") return self end @@ -1433,7 +1433,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryShip() - self:FilterCategories( "ship" ) + self:FilterCategories("ship") return self end @@ -1441,7 +1441,7 @@ do -- @param #SET_GROUP self -- @return #SET_GROUP self function SET_GROUP:FilterCategoryStructure() - self:FilterCategories( "structure" ) + self:FilterCategories("structure") return self end @@ -1450,14 +1450,14 @@ do -- @param #SET_GROUP self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_GROUP self - function SET_GROUP:FilterCountries( Countries ) + function SET_GROUP:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -1468,14 +1468,14 @@ do -- @param #SET_GROUP self -- @param #string Prefixes The string pattern(s) that needs to be contained in the group name. Can also be passed as a `#table` of strings. -- @return #SET_GROUP self - function SET_GROUP:FilterPrefixes( Prefixes ) + function SET_GROUP:FilterPrefixes(Prefixes) if not self.Filter.GroupPrefixes then self.Filter.GroupPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.GroupPrefixes[Prefix] = Prefix end return self @@ -1488,10 +1488,10 @@ do local Database = _DATABASE.GROUPS - for ObjectName, Object in pairs( Database ) do - if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then - self:Add( ObjectName, Object ) - elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + for ObjectName, Object in pairs(Database) do + if self:IsIncludeObject(Object) and self:IsNotInSet(Object) then + self:Add(ObjectName, Object) + elseif (not self:IsIncludeObject(Object)) and self:IsInSet(Object) then self:Remove(ObjectName) end end @@ -1512,15 +1512,15 @@ do -- GroupSet = SET_GROUP:New():FilterActive():FilterStart() -- -- -- Include only active groups to the set of the blue coalition, and filter one time. - -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- -- -- Include only active groups to the set of the blue coalition, and filter one time. -- -- Later, reset to include back inactive groups to the set. - -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- ... logic ... - -- GroupSet = SET_GROUP:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- GroupSet = SET_GROUP:New():FilterActive(false):FilterCoalition("blue"):FilterOnce() -- - function SET_GROUP:FilterActive( Active ) + function SET_GROUP:FilterActive(Active) Active = Active or not (Active == false) self.Filter.Active = Active return self @@ -1541,12 +1541,12 @@ do if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.RemoveUnit, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.UnitLost, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) local timing = self.ZoneTimerInterval or 30 @@ -1593,11 +1593,11 @@ do -- Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP. -- @param #SET_GROUP self -- @param Core.Event#EVENTDATA Event - function SET_GROUP:_EventOnDeadOrCrash( Event ) - --self:F( { Event } ) + function SET_GROUP:_EventOnDeadOrCrash(Event) + --self:F({ Event }) if Event.IniDCSUnit then - local ObjectName, Object = self:FindInDatabase( Event ) + local ObjectName, Object = self:FindInDatabase(Event) if ObjectName then local size = 1 if Event.IniDCSGroup then @@ -1611,7 +1611,7 @@ do size = Object:CountAliveUnits() end if size == 1 then -- Only remove if the last unit of the group was destroyed. - self:Remove( ObjectName ) + self:Remove(ObjectName) end end end @@ -1623,13 +1623,13 @@ do -- @param Core.Event#EVENTDATA Event -- @return #string The name of the GROUP -- @return #table The GROUP - function SET_GROUP:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_GROUP:AddInDatabase(Event) + --self:F3({ Event }) if Event.IniObjectCategory == Object.Category.UNIT then if not self.Database[Event.IniDCSGroupName] then - self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName ) - --self:T(3( self.Database[Event.IniDCSGroupName] ) + self.Database[Event.IniDCSGroupName] = GROUP:Register(Event.IniDCSGroupName) + --self:T(3(self.Database[Event.IniDCSGroupName]) end end @@ -1642,8 +1642,8 @@ do -- @param Core.Event#EVENTDATA Event -- @return #string The name of the GROUP -- @return #table The GROUP - function SET_GROUP:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_GROUP:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] end @@ -1652,10 +1652,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called for all GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroup( IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroup(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -1664,10 +1664,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called for some GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForSomeGroup( IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForSomeGroup(IteratorFunction, ...) + --self:F2(arg) - self:ForSome( IteratorFunction, arg, self:GetSet() ) + self:ForSome(IteratorFunction, arg, self:GetSet()) return self end @@ -1676,10 +1676,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupAlive( IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupAlive(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetAliveSet() ) + self:ForEach(IteratorFunction, arg, self:GetAliveSet()) return self end @@ -1688,10 +1688,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForSomeGroupAlive( IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForSomeGroupAlive(IteratorFunction, ...) + --self:F2(arg) - self:ForSome( IteratorFunction, arg, self:GetAliveSet() ) + self:ForSome(IteratorFunction, arg, self:GetAliveSet()) return self end @@ -1717,19 +1717,19 @@ do -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupCompletelyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupCompletelyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsCompletelyInZone( ZoneObject ) then + function(ZoneObject, GroupObject) + if GroupObject:IsCompletelyInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -1739,19 +1739,19 @@ do -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupPartlyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupPartlyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsPartlyInZone( ZoneObject ) then + function(ZoneObject, GroupObject) + if GroupObject:IsPartlyInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -1761,19 +1761,19 @@ do -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, GroupObject) + if GroupObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -1792,11 +1792,11 @@ do -- else -- MESSAGE:New("Some or all SET's GROUP are outside zone !", 10):ToAll() -- end - function SET_GROUP:AllCompletelyInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:AllCompletelyInZone(Zone) + --self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if not GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsCompletelyInZone(Zone) then return false end end @@ -1808,19 +1808,19 @@ do -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. -- @return #SET_GROUP self - function SET_GROUP:ForEachGroupAnyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_GROUP:ForEachGroupAnyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsAnyInZone( ZoneObject ) then + function(ZoneObject, GroupObject) + if GroupObject:IsAnyInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -1839,11 +1839,11 @@ do -- else -- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll() -- end - function SET_GROUP:AnyCompletelyInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:AnyCompletelyInZone(Zone) + --self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then return true end end @@ -1864,11 +1864,11 @@ do -- else -- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() -- end - function SET_GROUP:AnyInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:AnyInZone(Zone) + --self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsPartlyInZone( Zone ) or GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsPartlyInZone(Zone) or GroupData:IsCompletelyInZone(Zone) then return true end end @@ -1890,14 +1890,14 @@ do -- else -- MESSAGE:New("No GROUP are in zone, or one (or more) GROUP is completely in it !", 10):ToAll() -- end - function SET_GROUP:AnyPartlyInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:AnyPartlyInZone(Zone) + --self:F2(Zone) local IsPartlyInZone = false local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then return false - elseif GroupData:IsPartlyInZone( Zone ) then + elseif GroupData:IsPartlyInZone(Zone) then IsPartlyInZone = true -- at least one GROUP is partly in zone end end @@ -1925,11 +1925,11 @@ do -- else -- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() -- end - function SET_GROUP:NoneInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:NoneInZone(Zone) + --self:F2(Zone) local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if not GroupData:IsNotInZone( Zone ) then -- If the GROUP is in Zone in any way + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsNotInZone(Zone) then -- If the GROUP is in Zone in any way return false end end @@ -1948,12 +1948,12 @@ do -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) -- -- MESSAGE:New("There are " .. MySetGroup:CountInZone(MyZone) .. " GROUPs in the Zone !", 10):ToAll() - function SET_GROUP:CountInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:CountInZone(Zone) + --self:F2(Zone) local Count = 0 local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone( Zone ) then + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then Count = Count + 1 end end @@ -1970,12 +1970,12 @@ do -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) -- -- MESSAGE:New("There are " .. MySetGroup:CountUnitInZone(MyZone) .. " UNITs in the Zone !", 10):ToAll() - function SET_GROUP:CountUnitInZone( Zone ) - --self:F2( Zone ) + function SET_GROUP:CountUnitInZone(Zone) + --self:F2(Zone) local Count = 0 local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - Count = Count + GroupData:CountInZone( Zone ) + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + Count = Count + GroupData:CountInZone(Zone) end return Count end @@ -1990,13 +1990,13 @@ do local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP if GroupData and GroupData:IsAlive() then CountG = CountG + 1 -- Count Units. - for _, _unit in pairs( GroupData:GetUnits() ) do + for _, _unit in pairs(GroupData:GetUnits()) do local unit = _unit -- Wrapper.Unit#UNIT if unit and unit:IsAlive() then CountU = CountU + 1 @@ -2013,10 +2013,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. ---- @return #SET_GROUP self - -- function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) - -- --self:F2( arg ) + -- function SET_GROUP:ForEachPlayer(IteratorFunction, ...) + -- --self:F2(arg) -- - -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) + -- self:ForEach(IteratorFunction, arg, self.PlayersAlive) -- -- return self -- end @@ -2026,10 +2026,10 @@ do -- @param #SET_GROUP self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. ---- @return #SET_GROUP self - -- function SET_GROUP:ForEachClient( IteratorFunction, ... ) - -- --self:F2( arg ) + -- function SET_GROUP:ForEachClient(IteratorFunction, ...) + -- --self:F2(arg) -- - -- self:ForEach( IteratorFunction, arg, self.Clients ) + -- self:ForEach(IteratorFunction, arg, self.Clients) -- -- return self -- end @@ -2038,13 +2038,13 @@ do -- @param #SET_GROUP self -- @param Wrapper.Group#GROUP MGroup The group that is checked for inclusion. -- @return #SET_GROUP self - function SET_GROUP:IsIncludeObject( MGroup ) - --self:F2( MGroup ) + function SET_GROUP:IsIncludeObject(MGroup) + --self:F2(MGroup) local MGroupInclude = true if self.Filter.Alive == true then local MGroupAlive = false - --self:F( { Active = self.Filter.Active } ) + --self:F({ Active = self.Filter.Active }) if MGroup and MGroup:IsAlive() then MGroupAlive = true end @@ -2053,7 +2053,7 @@ do if self.Filter.Active ~= nil then local MGroupActive = false - --self:F( { Active = self.Filter.Active } ) + --self:F({ Active = self.Filter.Active }) if self.Filter.Active == false or (self.Filter.Active == true and MGroup:IsActive() == true) then MGroupActive = true end @@ -2062,8 +2062,8 @@ do if self.Filter.Coalitions and MGroupInclude then local MGroupCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - --self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + --self:T3({ "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MGroup:GetCoalition() then MGroupCoalition = true end @@ -2073,8 +2073,8 @@ do if self.Filter.Categories and MGroupInclude then local MGroupCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - --self:I( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + --self:I({ "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MGroup:GetCategory() then MGroupCategory = true end @@ -2085,8 +2085,8 @@ do if self.Filter.Countries and MGroupInclude then local MGroupCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - --self:T3( { "Country:", MGroup:GetCountry(), CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + --self:T3({ "Country:", MGroup:GetCountry(), CountryName }) if country.id[CountryName] == MGroup:GetCountry() then MGroupCountry = true end @@ -2096,8 +2096,8 @@ do if self.Filter.GroupPrefixes and MGroupInclude then local MGroupPrefix = false - for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do - --self:I( { "Prefix:", MGroup:GetName(), GroupPrefix } ) + for GroupPrefixId, GroupPrefix in pairs(self.Filter.GroupPrefixes) do + --self:I({ "Prefix:", MGroup:GetName(), GroupPrefix }) if string.find(MGroup:GetName(), string.gsub(GroupPrefix,"-","%%-"),1) then MGroupPrefix = true end @@ -2108,8 +2108,8 @@ do if self.Filter.Zones and MGroupInclude then local MGroupZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T("Zone:", ZoneName) if MGroup:IsInZone(Zone) then MGroupZone = true end @@ -2123,7 +2123,7 @@ do MGroupInclude = MGroupInclude and MGroupFunc end - --self:I( MGroupInclude ) + --self:I(MGroupInclude) return MGroupInclude end @@ -2140,7 +2140,7 @@ do local dmin=math.huge local gmin=nil - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP local group=GroupData --Wrapper.Group#GROUP if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then @@ -2174,8 +2174,8 @@ do -- MySetGroup:SetCargoBayWeightLimit() function SET_GROUP:SetCargoBayWeightLimit() local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - for UnitName, UnitData in pairs( GroupData:GetUnits() ) do + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + for UnitName, UnitData in pairs(GroupData:GetUnits()) do -- local UnitData = UnitData -- Wrapper.Unit#UNIT UnitData:SetCargoBayWeightLimit() end @@ -2261,12 +2261,12 @@ do -- SET_UNIT -- -- -- Create the SetCarrier SET_UNIT collection. -- - -- local SetHelicopter = SET_UNIT:New():FilterPrefixes( "Helicopter" ):FilterStart() + -- local SetHelicopter = SET_UNIT:New():FilterPrefixes("Helicopter"):FilterStart() -- -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier unit is destroyed, that all internal parameters are reset. -- - -- function SetHelicopter:OnAfterDead( From, Event, To, UnitObject ) - -- --self:F( { UnitObject = UnitObject:GetName() } ) + -- function SetHelicopter:OnAfterDead(From, Event, To, UnitObject) + -- --self:F({ UnitObject = UnitObject:GetName() }) -- end -- -- While this is a good example, there is a catch. @@ -2278,15 +2278,15 @@ do -- SET_UNIT -- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter. -- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER. -- - -- function ACLASS:New( SetCarrier, SetCargo, SetDeployZones ) + -- function ACLASS:New(SetCarrier, SetCargo, SetDeployZones) -- - -- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER + -- local self = BASE:Inherit(self, FSM:New()) -- #AI_CARGO_DISPATCHER -- -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. -- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration. -- - -- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, UnitObject ) - -- SetHelicopter:F( { UnitObject = UnitObject:GetName() } ) + -- function SetHelicopter.OnAfterDead(SetHelicopter, From, Event, To, UnitObject) + -- SetHelicopter:F({ UnitObject = UnitObject:GetName() }) -- self.array[UnitObject] = nil -- So here I clear the array table entry of the self object ACLASS. -- end -- @@ -2335,9 +2335,9 @@ do -- SET_UNIT function SET_UNIT:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) -- #SET_UNIT + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.UNITS)) -- #SET_UNIT - self:FilterActive( false ) + self:FilterActive(false) --- Count Alive Units -- @function [parent=#SET_UNIT] CountAlive @@ -2351,10 +2351,10 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param Wrapper.Unit#UNIT Unit A single UNIT. -- @return #SET_UNIT self - function SET_UNIT:AddUnit( Unit ) - --self:F2( Unit:GetName() ) + function SET_UNIT:AddUnit(Unit) + --self:F2(Unit:GetName()) - self:Add( Unit:GetName(), Unit ) + self:Add(Unit:GetName(), Unit) if Unit:IsInstanceOf("UNIT") then -- Set the default cargo bay limit each time a new unit is added to the set. @@ -2368,13 +2368,13 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string AddUnitNames A single name or an array of UNIT names. -- @return #SET_UNIT self - function SET_UNIT:AddUnitsByName( AddUnitNames ) + function SET_UNIT:AddUnitsByName(AddUnitNames) - local AddUnitNamesArray = (type( AddUnitNames ) == "table") and AddUnitNames or { AddUnitNames } + local AddUnitNamesArray = (type(AddUnitNames) == "table") and AddUnitNames or { AddUnitNames } - --self:T( AddUnitNamesArray ) - for AddUnitID, AddUnitName in pairs( AddUnitNamesArray ) do - self:Add( AddUnitName, UNIT:FindByName( AddUnitName ) ) + --self:T(AddUnitNamesArray) + for AddUnitID, AddUnitName in pairs(AddUnitNamesArray) do + self:Add(AddUnitName, UNIT:FindByName(AddUnitName)) end return self @@ -2384,12 +2384,12 @@ do -- SET_UNIT -- @param Core.Set#SET_UNIT self -- @param #table RemoveUnitNames A single name or an array of UNIT names. -- @return Core.Set#SET_UNIT self - function SET_UNIT:RemoveUnitsByName( RemoveUnitNames ) + function SET_UNIT:RemoveUnitsByName(RemoveUnitNames) - local RemoveUnitNamesArray = (type( RemoveUnitNames ) == "table") and RemoveUnitNames or { RemoveUnitNames } + local RemoveUnitNamesArray = (type(RemoveUnitNames) == "table") and RemoveUnitNames or { RemoveUnitNames } - for RemoveUnitID, RemoveUnitName in pairs( RemoveUnitNamesArray ) do - self:Remove( RemoveUnitName ) + for RemoveUnitID, RemoveUnitName in pairs(RemoveUnitNamesArray) do + self:Remove(RemoveUnitName) end return self @@ -2399,7 +2399,7 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string UnitName -- @return Wrapper.Unit#UNIT The found Unit. - function SET_UNIT:FindUnit( UnitName ) + function SET_UNIT:FindUnit(UnitName) local UnitFound = self.Set[UnitName] return UnitFound @@ -2417,14 +2417,14 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @return #SET_UNIT self - function SET_UNIT:FilterCategories( Categories ) + function SET_UNIT:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -2435,14 +2435,14 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_UNIT self - function SET_UNIT:FilterTypes( Types ) + function SET_UNIT:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -2453,14 +2453,14 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_UNIT self - function SET_UNIT:FilterCountries( Countries ) + function SET_UNIT:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -2471,14 +2471,14 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string Prefixes The string pattern(s) that needs to be contained in the unit name. Can also be passed as a `#table` of strings. -- @return #SET_UNIT self - function SET_UNIT:FilterPrefixes( Prefixes ) + function SET_UNIT:FilterPrefixes(Prefixes) if not self.Filter.UnitPrefixes then self.Filter.UnitPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.UnitPrefixes[Prefix] = Prefix end return self @@ -2488,20 +2488,20 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_UNIT self - function SET_UNIT:FilterZones( Zones ) + function SET_UNIT:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -2520,15 +2520,15 @@ do -- SET_UNIT -- UnitSet = SET_UNIT:New():FilterActive():FilterStart() -- -- -- Include only active units to the set of the blue coalition, and filter one time. - -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- -- -- Include only active units to the set of the blue coalition, and filter one time. -- -- Later, reset to include back inactive units to the set. - -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- ... logic ... - -- UnitSet = SET_UNIT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- UnitSet = SET_UNIT:New():FilterActive(false):FilterCoalition("blue"):FilterOnce() -- - function SET_UNIT:FilterActive( Active ) + function SET_UNIT:FilterActive(Active) Active = Active or not (Active == false) self.Filter.Active = Active return self @@ -2546,7 +2546,7 @@ do -- SET_UNIT return false end end - ) + ) return self end @@ -2575,7 +2575,7 @@ do -- SET_UNIT end return outcome end, Prefixes - ) + ) return self end @@ -2584,13 +2584,13 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #table RadarTypes The radar types. -- @return #SET_UNIT self - function SET_UNIT:FilterHasRadar( RadarTypes ) + function SET_UNIT:FilterHasRadar(RadarTypes) self.Filter.RadarTypes = self.Filter.RadarTypes or {} - if type( RadarTypes ) ~= "table" then + if type(RadarTypes) ~= "table" then RadarTypes = { RadarTypes } end - for RadarTypeID, RadarType in pairs( RadarTypes ) do + for RadarTypeID, RadarType in pairs(RadarTypes) do self.Filter.RadarTypes[RadarType] = RadarType end return self @@ -2613,7 +2613,7 @@ do -- SET_UNIT local Set = self:GetSet() local CountU = 0 - for UnitID, UnitData in pairs( Set ) do -- For each GROUP in SET_GROUP + for UnitID, UnitData in pairs(Set) do -- For each GROUP in SET_GROUP if UnitData and UnitData:IsAlive() then CountU = CountU + 1 end @@ -2648,10 +2648,10 @@ do -- SET_UNIT local Database = _DATABASE.UNITS - for ObjectName, Object in pairs( Database ) do - if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then - self:Add( ObjectName, Object ) - elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + for ObjectName, Object in pairs(Database) do + if self:IsIncludeObject(Object) and self:IsNotInSet(Object) then + self:Add(ObjectName, Object) + elseif (not self:IsIncludeObject(Object)) and self:IsInSet(Object) then self:Remove(ObjectName) end end @@ -2697,11 +2697,11 @@ do -- SET_UNIT if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.RemoveUnit, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.UnitLost, self._EventOnDeadOrCrash) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) local timing = self.ZoneTimerInterval or 30 @@ -2727,7 +2727,7 @@ do -- SET_UNIT -- if unit:GetName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(groundset:Flush()) @@ -2737,13 +2737,13 @@ do -- SET_UNIT -- @param Core.Event#EVENTDATA Event -- @return #string The name of the UNIT -- @return #table The UNIT - function SET_UNIT:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_UNIT:AddInDatabase(Event) + --self:F3({ Event }) if Event.IniObjectCategory == Object.Category.UNIT then if not self.Database[Event.IniDCSUnitName] then - self.Database[Event.IniDCSUnitName] = UNIT:Register( Event.IniDCSUnitName ) - --self:T3( self.Database[Event.IniDCSUnitName] ) + self.Database[Event.IniDCSUnitName] = UNIT:Register(Event.IniDCSUnitName) + --self:T3(self.Database[Event.IniDCSUnitName]) end end @@ -2756,8 +2756,8 @@ do -- SET_UNIT -- @param Core.Event#EVENTDATA Event -- @return #string The name of the UNIT -- @return #table The UNIT - function SET_UNIT:FindInDatabase( Event ) - --self:F2( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) + function SET_UNIT:FindInDatabase(Event) + --self:F2({ Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event }) return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] end @@ -2768,24 +2768,24 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param Core.Zone#ZONE ZoneTest The Zone to be tested for. -- @return #boolean - function SET_UNIT:IsPartiallyInZone( ZoneTest ) + function SET_UNIT:IsPartiallyInZone(ZoneTest) local IsPartiallyInZone = false - local function EvaluateZone( ZoneUnit ) + local function EvaluateZone(ZoneUnit) local ZoneUnitName = ZoneUnit:GetName() - --self:F( { ZoneUnitName = ZoneUnitName } ) - if self:FindUnit( ZoneUnitName ) then + --self:F({ ZoneUnitName = ZoneUnitName }) + if self:FindUnit(ZoneUnitName) then IsPartiallyInZone = true - --self:F( { Found = true } ) + --self:F({ Found = true }) return false end return true end - ZoneTest:SearchZone( EvaluateZone ) + ZoneTest:SearchZone(EvaluateZone) return IsPartiallyInZone end @@ -2794,14 +2794,14 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param Core.Zone#ZONE Zone The Zone to be tested for. -- @return #boolean - function SET_UNIT:IsNotInZone( Zone ) + function SET_UNIT:IsNotInZone(Zone) local IsNotInZone = true - local function EvaluateZone( ZoneUnit ) + local function EvaluateZone(ZoneUnit) local ZoneUnitName = ZoneUnit:GetName() - if self:FindUnit( ZoneUnitName ) then + if self:FindUnit(ZoneUnitName) then IsNotInZone = false return false end @@ -2809,7 +2809,7 @@ do -- SET_UNIT return true end - Zone:SearchZone( EvaluateZone ) + Zone:SearchZone(EvaluateZone) return IsNotInZone end @@ -2821,10 +2821,10 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. -- @return #SET_UNIT self - function SET_UNIT:ForEachUnit( IteratorFunction, ... ) - --self:F2( arg ) + function SET_UNIT:ForEachUnit(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -2835,20 +2835,20 @@ do -- SET_UNIT -- @param #number FromThreatLevel The TreatLevel to start the evaluation **From** (this must be a value between 0 and 10). -- @param #number ToThreatLevel The TreatLevel to stop the evaluation **To** (this must be a value between 0 and 10). -- @return #SET_UNIT self - function SET_UNIT:GetSetPerThreatLevel( FromThreatLevel, ToThreatLevel ) - --self:F2( arg ) + function SET_UNIT:GetSetPerThreatLevel(FromThreatLevel, ToThreatLevel) + --self:F2(arg) local ThreatLevelSet = {} if self:Count() ~= 0 then - for UnitName, UnitObject in pairs( self.Set ) do + for UnitName, UnitObject in pairs(self.Set) do local Unit = UnitObject -- Wrapper.Unit#UNIT local ThreatLevel = Unit:GetThreatLevel() ThreatLevelSet[ThreatLevel] = ThreatLevelSet[ThreatLevel] or {} ThreatLevelSet[ThreatLevel].Set = ThreatLevelSet[ThreatLevel].Set or {} ThreatLevelSet[ThreatLevel].Set[UnitName] = UnitObject - --self:F( { ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set } ) + --self:F({ ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set }) end local OrderedPerThreatLevelSet = {} @@ -2856,11 +2856,11 @@ do -- SET_UNIT local ThreatLevelIncrement = FromThreatLevel <= ToThreatLevel and 1 or -1 for ThreatLevel = FromThreatLevel, ToThreatLevel, ThreatLevelIncrement do - --self:F( { ThreatLevel = ThreatLevel } ) + --self:F({ ThreatLevel = ThreatLevel }) local ThreatLevelItem = ThreatLevelSet[ThreatLevel] if ThreatLevelItem then - for UnitName, UnitObject in pairs( ThreatLevelItem.Set ) do - table.insert( OrderedPerThreatLevelSet, UnitObject ) + for UnitName, UnitObject in pairs(ThreatLevelItem.Set) do + table.insert(OrderedPerThreatLevelSet, UnitObject) end end end @@ -2880,36 +2880,36 @@ do -- SET_UNIT -- @return #SET_UNIT self -- @usage -- - -- UnitSet:ForEachUnitPerThreatLevel( 10, 0, + -- UnitSet:ForEachUnitPerThreatLevel(10, 0, -- -- @param Wrapper.Unit#UNIT UnitObject The UNIT object in the UnitSet, that will be passed to the local function for evaluation. - -- function( UnitObject ) + -- function(UnitObject) -- .. logic .. -- end - -- ) + -- ) -- - function SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) -- R2.1 Threat Level implementation - --self:F2( arg ) + function SET_UNIT:ForEachUnitPerThreatLevel(FromThreatLevel, ToThreatLevel, IteratorFunction, ...) -- R2.1 Threat Level implementation + --self:F2(arg) local ThreatLevelSet = {} if self:Count() ~= 0 then - for UnitName, UnitObject in pairs( self.Set ) do + for UnitName, UnitObject in pairs(self.Set) do local Unit = UnitObject -- Wrapper.Unit#UNIT local ThreatLevel = Unit:GetThreatLevel() ThreatLevelSet[ThreatLevel] = ThreatLevelSet[ThreatLevel] or {} ThreatLevelSet[ThreatLevel].Set = ThreatLevelSet[ThreatLevel].Set or {} ThreatLevelSet[ThreatLevel].Set[UnitName] = UnitObject - --self:F( { ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set } ) + --self:F({ ThreatLevel = ThreatLevel, ThreatLevelSet = ThreatLevelSet[ThreatLevel].Set }) end local ThreatLevelIncrement = FromThreatLevel <= ToThreatLevel and 1 or -1 for ThreatLevel = FromThreatLevel, ToThreatLevel, ThreatLevelIncrement do - --self:F( { ThreatLevel = ThreatLevel } ) + --self:F({ ThreatLevel = ThreatLevel }) local ThreatLevelItem = ThreatLevelSet[ThreatLevel] if ThreatLevelItem then - self:ForEach( IteratorFunction, arg, ThreatLevelItem.Set ) + self:ForEach(IteratorFunction, arg, ThreatLevelItem.Set) end end end @@ -2922,19 +2922,19 @@ do -- SET_UNIT -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. -- @return #SET_UNIT self - function SET_UNIT:ForEachUnitCompletelyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_UNIT:ForEachUnitCompletelyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsInZone( ZoneObject ) then + function(ZoneObject, UnitObject) + if UnitObject:IsInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -2944,19 +2944,19 @@ do -- SET_UNIT -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive UNIT in the SET_UNIT. The function needs to accept a UNIT parameter. -- @return #SET_UNIT self - function SET_UNIT:ForEachUnitNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_UNIT:ForEachUnitNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject - function( ZoneObject, UnitObject ) - if UnitObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, UnitObject) + if UnitObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -2970,7 +2970,7 @@ do -- SET_UNIT local MT = {} -- Message Text local UnitTypes = {} - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local TextUnit = UnitData -- Wrapper.Unit#UNIT if TextUnit:IsAlive() then local UnitType = TextUnit:GetTypeName() @@ -2983,7 +2983,7 @@ do -- SET_UNIT end end - for UnitTypeID, UnitType in pairs( UnitTypes ) do + for UnitTypeID, UnitType in pairs(UnitTypes) do MT[#MT + 1] = UnitType .. " of " .. UnitTypeID end @@ -2999,11 +2999,11 @@ do -- SET_UNIT local MT = {} -- Message Text local UnitTypes = self:GetUnitTypes() - for UnitTypeID, UnitType in pairs( UnitTypes ) do + for UnitTypeID, UnitType in pairs(UnitTypes) do MT[#MT + 1] = UnitType .. " of " .. UnitTypeID end - return table.concat( MT, ", " ) + return table.concat(MT, ", ") end --- Returns map of unit threat levels. @@ -3014,7 +3014,7 @@ do -- SET_UNIT local UnitThreatLevels = {} - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local ThreatUnit = UnitData -- Wrapper.Unit#UNIT if ThreatUnit:IsAlive() then local UnitThreatLevel, UnitThreatLevelText = ThreatUnit:GetThreatLevel() @@ -3037,7 +3037,7 @@ do -- SET_UNIT local MaxThreatLevelA2G = 0 local MaxThreatText = "" - for UnitName, UnitData in pairs( self:GetSet() ) do + for UnitName, UnitData in pairs(self:GetSet()) do local ThreatUnit = UnitData -- Wrapper.Unit#UNIT local ThreatLevelA2G, ThreatText = ThreatUnit:GetThreatLevel() if ThreatLevelA2G > MaxThreatLevelA2G then @@ -3046,7 +3046,7 @@ do -- SET_UNIT end end - --self:F( { MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText } ) + --self:F({ MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText }) return MaxThreatLevelA2G, MaxThreatText end @@ -3094,8 +3094,8 @@ do -- SET_UNIT if Coordinate then local heading = self:GetHeading() or 0 local velocity = self:GetVelocity() or 0 - Coordinate:SetHeading( heading ) - Coordinate:SetVelocity( velocity ) + Coordinate:SetHeading(heading) + Coordinate:SetVelocity(velocity) --self:T(UTILS.PrintTableToLog(Coordinate)) end @@ -3111,7 +3111,7 @@ do -- SET_UNIT local MaxVelocity = 0 - for UnitName, UnitData in pairs( self:GetSet() ) do + for UnitName, UnitData in pairs(self:GetSet()) do local Unit = UnitData -- Wrapper.Unit#UNIT local Coordinate = Unit:GetCoordinate() @@ -3122,7 +3122,7 @@ do -- SET_UNIT end end - --self:F( { MaxVelocity = MaxVelocity } ) + --self:F({ MaxVelocity = MaxVelocity }) return MaxVelocity end @@ -3135,7 +3135,7 @@ do -- SET_UNIT local HeadingSet = nil local MovingCount = 0 - for UnitName, UnitData in pairs( self:GetSet() ) do + for UnitName, UnitData in pairs(self:GetSet()) do local Unit = UnitData -- Wrapper.Unit#UNIT local Coordinate = Unit:GetCoordinate() @@ -3147,7 +3147,7 @@ do -- SET_UNIT HeadingSet = Heading else local HeadingDiff = (HeadingSet - Heading + 180 + 360) % 360 - 180 - HeadingDiff = math.abs( HeadingDiff ) + HeadingDiff = math.abs(HeadingDiff) if HeadingDiff > 5 then HeadingSet = nil break @@ -3164,19 +3164,19 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param DCS#Unit.RadarType RadarType -- @return #number The amount of radars in the Set with the given type - function SET_UNIT:HasRadar( RadarType ) - --self:F2( RadarType ) + function SET_UNIT:HasRadar(RadarType) + --self:F2(RadarType) local RadarCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitSensorTest = UnitData -- Wrapper.Unit#UNIT local HasSensors if RadarType then - HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR, RadarType ) + HasSensors = UnitSensorTest:HasSensors(Unit.SensorType.RADAR, RadarType) else - HasSensors = UnitSensorTest:HasSensors( Unit.SensorType.RADAR ) + HasSensors = UnitSensorTest:HasSensors(Unit.SensorType.RADAR) end - --self:T3( HasSensors ) + --self:T3(HasSensors) if HasSensors then RadarCount = RadarCount + 1 end @@ -3192,14 +3192,14 @@ do -- SET_UNIT --self:F2() local SEADCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitSEAD = UnitData -- Wrapper.Unit#UNIT if UnitSEAD:IsAlive() then local UnitSEADAttributes = UnitSEAD:GetDesc().attributes local HasSEAD = UnitSEAD:HasSEAD() - --self:T3( HasSEAD ) + --self:T3(HasSEAD) if HasSEAD then SEADCount = SEADCount + 1 end @@ -3216,7 +3216,7 @@ do -- SET_UNIT --self:F2() local GroundUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitTest = UnitData -- Wrapper.Unit#UNIT if UnitTest:IsGround() then GroundUnitCount = GroundUnitCount + 1 @@ -3233,7 +3233,7 @@ do -- SET_UNIT --self:F2() local AirUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitTest = UnitData -- Wrapper.Unit#UNIT if UnitTest:IsAir() then AirUnitCount = AirUnitCount + 1 @@ -3246,13 +3246,13 @@ do -- SET_UNIT --- Returns if the @{Core.Set} has friendly ground units. -- @param #SET_UNIT self -- @return #number The amount of ground targets in the Set. - function SET_UNIT:HasFriendlyUnits( FriendlyCoalition ) + function SET_UNIT:HasFriendlyUnits(FriendlyCoalition) --self:F2() local FriendlyUnitCount = 0 - for UnitID, UnitData in pairs( self:GetSet() ) do + for UnitID, UnitData in pairs(self:GetSet()) do local UnitTest = UnitData -- Wrapper.Unit#UNIT - if UnitTest:IsFriendly( FriendlyCoalition ) then + if UnitTest:IsFriendly(FriendlyCoalition) then FriendlyUnitCount = FriendlyUnitCount + 1 end end @@ -3266,10 +3266,10 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. ---- @return #SET_UNIT self - -- function SET_UNIT:ForEachPlayer( IteratorFunction, ... ) - -- --self:F2( arg ) + -- function SET_UNIT:ForEachPlayer(IteratorFunction, ...) + -- --self:F2(arg) -- - -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) + -- self:ForEach(IteratorFunction, arg, self.PlayersAlive) -- -- return self -- end @@ -3279,10 +3279,10 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. ---- @return #SET_UNIT self - -- function SET_UNIT:ForEachClient( IteratorFunction, ... ) - -- --self:F2( arg ) + -- function SET_UNIT:ForEachClient(IteratorFunction, ...) + -- --self:F2(arg) -- - -- self:ForEach( IteratorFunction, arg, self.Clients ) + -- self:ForEach(IteratorFunction, arg, self.Clients) -- -- return self -- end @@ -3291,8 +3291,8 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param Wrapper.Unit#UNIT MUnit -- @return #SET_UNIT self - function SET_UNIT:IsIncludeObject( MUnit ) - --self:F2( {MUnit} ) + function SET_UNIT:IsIncludeObject(MUnit) + --self:F2({MUnit}) local MUnitInclude = false @@ -3310,8 +3310,8 @@ do -- SET_UNIT if self.Filter.Coalitions and MUnitInclude then local MUnitCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - --self:F( { "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + --self:F({ "Coalition:", MUnit:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MUnit:GetCoalition() then MUnitCoalition = true end @@ -3321,8 +3321,8 @@ do -- SET_UNIT if self.Filter.Categories and MUnitInclude then local MUnitCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - --self:T3( { "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + --self:T3({ "Category:", MUnit:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MUnit:GetDesc().category then MUnitCategory = true end @@ -3332,8 +3332,8 @@ do -- SET_UNIT if self.Filter.Types and MUnitInclude then local MUnitType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T3( { "Type:", MUnit:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T3({ "Type:", MUnit:GetTypeName(), TypeName }) if TypeName == MUnit:GetTypeName() then MUnitType = true end @@ -3343,8 +3343,8 @@ do -- SET_UNIT if self.Filter.Countries and MUnitInclude then local MUnitCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - --self:T3( { "Country:", MUnit:GetCountry(), CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + --self:T3({ "Country:", MUnit:GetCountry(), CountryName }) if country.id[CountryName] == MUnit:GetCountry() then MUnitCountry = true end @@ -3354,9 +3354,9 @@ do -- SET_UNIT if self.Filter.UnitPrefixes and MUnitInclude then local MUnitPrefix = false - for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do - --self:T3( { "Prefix:", string.find( MUnit:GetName(), UnitPrefix, 1 ), UnitPrefix } ) - if string.find( MUnit:GetName(), UnitPrefix, 1 ) then + for UnitPrefixId, UnitPrefix in pairs(self.Filter.UnitPrefixes) do + --self:T3({ "Prefix:", string.find(MUnit:GetName(), UnitPrefix, 1), UnitPrefix }) + if string.find(MUnit:GetName(), UnitPrefix, 1) then MUnitPrefix = true end end @@ -3365,11 +3365,11 @@ do -- SET_UNIT if self.Filter.RadarTypes and MUnitInclude then local MUnitRadar = false - for RadarTypeID, RadarType in pairs( self.Filter.RadarTypes ) do - --self:T3( { "Radar:", RadarType } ) - if MUnit:HasSensors( Unit.SensorType.RADAR, RadarType ) == true then + for RadarTypeID, RadarType in pairs(self.Filter.RadarTypes) do + --self:T3({ "Radar:", RadarType }) + if MUnit:HasSensors(Unit.SensorType.RADAR, RadarType) == true then if MUnit:GetRadar() == true then -- This call is necessary to evaluate the SEAD capability. - --self:T3( "RADAR Found" ) + --self:T3("RADAR Found") end MUnitRadar = true end @@ -3380,7 +3380,7 @@ do -- SET_UNIT if self.Filter.SEAD and MUnitInclude then local MUnitSEAD = false if MUnit:HasSEAD() == true then - --self:T3( "SEAD Found" ) + --self:T3("SEAD Found") MUnitSEAD = true end MUnitInclude = MUnitInclude and MUnitSEAD @@ -3389,8 +3389,8 @@ do -- SET_UNIT if self.Filter.Zones and MUnitInclude then local MGroupZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T3( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T3("Zone:", ZoneName) if MUnit:IsInZone(Zone) then MGroupZone = true end @@ -3403,7 +3403,7 @@ do -- SET_UNIT MUnitInclude = MUnitInclude and MUnitFunc end - --self:T2( MUnitInclude ) + --self:T2(MUnitInclude) return MUnitInclude end @@ -3411,24 +3411,24 @@ do -- SET_UNIT -- @param #SET_UNIT self -- @param #string Delimiter (Optional) The delimiter, which is default a comma. -- @return #string The types of the @{Wrapper.Unit}s delimited. - function SET_UNIT:GetTypeNames( Delimiter ) + function SET_UNIT:GetTypeNames(Delimiter) Delimiter = Delimiter or ", " local TypeReport = REPORT:New() local Types = {} - for UnitName, UnitData in pairs( self:GetSet() ) do + for UnitName, UnitData in pairs(self:GetSet()) do local Unit = UnitData -- Wrapper.Unit#UNIT local UnitTypeName = Unit:GetTypeName() if not Types[UnitTypeName] then Types[UnitTypeName] = UnitTypeName - TypeReport:Add( UnitTypeName ) + TypeReport:Add(UnitTypeName) end end - return TypeReport:Text( Delimiter ) + return TypeReport:Text(Delimiter) end --- Iterate the SET_UNIT and set for each unit the default cargo bay weight limit. @@ -3439,7 +3439,7 @@ do -- SET_UNIT -- MySetUnit:SetCargoBayWeightLimit() function SET_UNIT:SetCargoBayWeightLimit() local Set = self:GetSet() - for UnitID, UnitData in pairs( Set ) do -- For each UNIT in SET_UNIT + for UnitID, UnitData in pairs(Set) do -- For each UNIT in SET_UNIT -- local UnitData = UnitData -- Wrapper.Unit#UNIT UnitData:SetCargoBayWeightLimit() end @@ -3551,7 +3551,7 @@ do -- SET_STATIC function SET_STATIC:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.STATICS ) ) -- Core.Set#SET_STATIC + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.STATICS)) -- Core.Set#SET_STATIC return self end @@ -3560,10 +3560,10 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param Wrapper.Static#STATIC AddStatic A single STATIC. -- @return #SET_STATIC self - function SET_STATIC:AddStatic( AddStatic ) - --self:F2( AddStatic:GetName() ) + function SET_STATIC:AddStatic(AddStatic) + --self:F2(AddStatic:GetName()) - self:Add( AddStatic:GetName(), AddStatic ) + self:Add(AddStatic:GetName(), AddStatic) return self end @@ -3572,13 +3572,13 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string AddStaticNames A single name or an array of STATIC names. -- @return #SET_STATIC self - function SET_STATIC:AddStaticsByName( AddStaticNames ) + function SET_STATIC:AddStaticsByName(AddStaticNames) - local AddStaticNamesArray = (type( AddStaticNames ) == "table") and AddStaticNames or { AddStaticNames } + local AddStaticNamesArray = (type(AddStaticNames) == "table") and AddStaticNames or { AddStaticNames } - --self:T(( AddStaticNamesArray ) - for AddStaticID, AddStaticName in pairs( AddStaticNamesArray ) do - self:Add( AddStaticName, STATIC:FindByName( AddStaticName ) ) + --self:T((AddStaticNamesArray) + for AddStaticID, AddStaticName in pairs(AddStaticNamesArray) do + self:Add(AddStaticName, STATIC:FindByName(AddStaticName)) end return self @@ -3588,12 +3588,12 @@ do -- SET_STATIC -- @param Core.Set#SET_STATIC self -- @param Wrapper.Static#STATIC RemoveStaticNames A single name or an array of STATIC names. -- @return self - function SET_STATIC:RemoveStaticsByName( RemoveStaticNames ) + function SET_STATIC:RemoveStaticsByName(RemoveStaticNames) - local RemoveStaticNamesArray = (type( RemoveStaticNames ) == "table") and RemoveStaticNames or { RemoveStaticNames } + local RemoveStaticNamesArray = (type(RemoveStaticNames) == "table") and RemoveStaticNames or { RemoveStaticNames } - for RemoveStaticID, RemoveStaticName in pairs( RemoveStaticNamesArray ) do - self:Remove( RemoveStaticName ) + for RemoveStaticID, RemoveStaticName in pairs(RemoveStaticNamesArray) do + self:Remove(RemoveStaticName) end return self @@ -3603,7 +3603,7 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string StaticName -- @return Wrapper.Static#STATIC The found Static. - function SET_STATIC:FindStatic( StaticName ) + function SET_STATIC:FindStatic(StaticName) local StaticFound = self.Set[StaticName] return StaticFound @@ -3620,20 +3620,20 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_STATIC self - function SET_STATIC:FilterZones( Zones ) + function SET_STATIC:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -3645,14 +3645,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @return #SET_STATIC self - function SET_STATIC:FilterCategories( Categories ) + function SET_STATIC:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -3663,14 +3663,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_STATIC self - function SET_STATIC:FilterTypes( Types ) + function SET_STATIC:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -3691,7 +3691,7 @@ do -- SET_STATIC -- if static:GetName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(groundset:Flush()) --- Builds a set of units of defined countries. @@ -3699,14 +3699,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_STATIC self - function SET_STATIC:FilterCountries( Countries ) + function SET_STATIC:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -3717,14 +3717,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Prefixes The string pattern(s) that need to be contained in the static name. Can also be passed as a `#table` of strings. -- @return #SET_STATIC self - function SET_STATIC:FilterPrefixes( Prefixes ) + function SET_STATIC:FilterPrefixes(Prefixes) if not self.Filter.StaticPrefixes then self.Filter.StaticPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.StaticPrefixes[Prefix] = Prefix end return self @@ -3737,9 +3737,9 @@ do -- SET_STATIC if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.UnitLost, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.UnitLost, self._EventOnDeadOrCrash) end return self @@ -3753,7 +3753,7 @@ do -- SET_STATIC local Set = self:GetSet() local CountU = 0 - for UnitID, UnitData in pairs( Set ) do + for UnitID, UnitData in pairs(Set) do if UnitData and UnitData:IsAlive() then CountU = CountU + 1 end @@ -3769,13 +3769,13 @@ do -- SET_STATIC -- @param Core.Event#EVENTDATA Event -- @return #string The name of the STATIC -- @return #table The STATIC - function SET_STATIC:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_STATIC:AddInDatabase(Event) + --self:F3({ Event }) if Event.IniObjectCategory == Object.Category.STATIC then if not self.Database[Event.IniDCSUnitName] then - self.Database[Event.IniDCSUnitName] = STATIC:Register( Event.IniDCSUnitName ) - --self:T(3( self.Database[Event.IniDCSUnitName] ) + self.Database[Event.IniDCSUnitName] = STATIC:Register(Event.IniDCSUnitName) + --self:T(3(self.Database[Event.IniDCSUnitName]) end end @@ -3788,8 +3788,8 @@ do -- SET_STATIC -- @param Core.Event#EVENTDATA Event -- @return #string The name of the STATIC -- @return #table The STATIC - function SET_STATIC:FindInDatabase( Event ) - --self:F2( { Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event } ) + function SET_STATIC:FindInDatabase(Event) + --self:F2({ Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName], Event }) return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] end @@ -3800,14 +3800,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param Core.Zone#ZONE Zone The Zone to be tested for. -- @return #boolean - function SET_STATIC:IsPartiallyInZone( Zone ) + function SET_STATIC:IsPartiallyInZone(Zone) local IsPartiallyInZone = false - local function EvaluateZone( ZoneStatic ) + local function EvaluateZone(ZoneStatic) local ZoneStaticName = ZoneStatic:GetName() - if self:FindStatic( ZoneStaticName ) then + if self:FindStatic(ZoneStaticName) then IsPartiallyInZone = true return false end @@ -3822,14 +3822,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param Core.Zone#ZONE Zone The Zone to be tested for. -- @return #boolean - function SET_STATIC:IsNotInZone( Zone ) + function SET_STATIC:IsNotInZone(Zone) local IsNotInZone = true - local function EvaluateZone( ZoneStatic ) + local function EvaluateZone(ZoneStatic) local ZoneStaticName = ZoneStatic:GetName() - if self:FindStatic( ZoneStaticName ) then + if self:FindStatic(ZoneStaticName) then IsNotInZone = false return false end @@ -3837,7 +3837,7 @@ do -- SET_STATIC return true end - Zone:Search( EvaluateZone ) + Zone:Search(EvaluateZone) return IsNotInZone end @@ -3846,10 +3846,10 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. -- @return #SET_STATIC self - function SET_STATIC:ForEachStaticInZone( IteratorFunction, ... ) - --self:F2( arg ) + function SET_STATIC:ForEachStaticInZone(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -3861,10 +3861,10 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. -- @return #SET_STATIC self - function SET_STATIC:ForEachStatic( IteratorFunction, ... ) - --self:F2( arg ) + function SET_STATIC:ForEachStatic(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -3874,19 +3874,19 @@ do -- SET_STATIC -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. -- @return #SET_STATIC self - function SET_STATIC:ForEachStaticCompletelyInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_STATIC:ForEachStaticCompletelyInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject - function( ZoneObject, StaticObject ) - if StaticObject:IsInZone( ZoneObject ) then + function(ZoneObject, StaticObject) + if StaticObject:IsInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -3896,19 +3896,19 @@ do -- SET_STATIC -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive STATIC in the SET_STATIC. The function needs to accept a STATIC parameter. -- @return #SET_STATIC self - function SET_STATIC:ForEachStaticNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_STATIC:ForEachStaticNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject - function( ZoneObject, StaticObject ) - if StaticObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, StaticObject) + if StaticObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -3922,7 +3922,7 @@ do -- SET_STATIC local MT = {} -- Message Text local StaticTypes = {} - for StaticID, StaticData in pairs( self:GetSet() ) do + for StaticID, StaticData in pairs(self:GetSet()) do local TextStatic = StaticData -- Wrapper.Static#STATIC if TextStatic:IsAlive() then local StaticType = TextStatic:GetTypeName() @@ -3935,7 +3935,7 @@ do -- SET_STATIC end end - for StaticTypeID, StaticType in pairs( StaticTypes ) do + for StaticTypeID, StaticType in pairs(StaticTypes) do MT[#MT + 1] = StaticType .. " of " .. StaticTypeID end @@ -3951,11 +3951,11 @@ do -- SET_STATIC local MT = {} -- Message Text local StaticTypes = self:GetStaticTypes() - for StaticTypeID, StaticType in pairs( StaticTypes ) do + for StaticTypeID, StaticType in pairs(StaticTypes) do MT[#MT + 1] = StaticType .. " of " .. StaticTypeID end - return table.concat( MT, ", " ) + return table.concat(MT, ", ") end --- Get the center coordinate of the SET_STATIC. @@ -3975,7 +3975,7 @@ do -- SET_STATIC local AvgHeading = nil local MovingCount = 0 - for StaticName, StaticData in pairs( self:GetSet() ) do + for StaticName, StaticData in pairs(self:GetSet()) do local Static = StaticData -- Wrapper.Static#STATIC local Coordinate = Static:GetCoordinate() @@ -4001,10 +4001,10 @@ do -- SET_STATIC Coordinate.x = (x2 - x1) / 2 + x1 Coordinate.y = (y2 - y1) / 2 + y1 Coordinate.z = (z2 - z1) / 2 + z1 - Coordinate:SetHeading( AvgHeading ) - Coordinate:SetVelocity( MaxVelocity ) + Coordinate:SetHeading(AvgHeading) + Coordinate:SetVelocity(MaxVelocity) - --self:F( { Coordinate = Coordinate } ) + --self:F({ Coordinate = Coordinate }) return Coordinate end @@ -4026,7 +4026,7 @@ do -- SET_STATIC local HeadingSet = nil local MovingCount = 0 - for StaticName, StaticData in pairs( self:GetSet() ) do + for StaticName, StaticData in pairs(self:GetSet()) do local Static = StaticData -- Wrapper.Static#STATIC local Coordinate = Static:GetCoordinate() @@ -4038,7 +4038,7 @@ do -- SET_STATIC HeadingSet = Heading else local HeadingDiff = (HeadingSet - Heading + 180 + 360) % 360 - 180 - HeadingDiff = math.abs( HeadingDiff ) + HeadingDiff = math.abs(HeadingDiff) if HeadingDiff > 5 then HeadingSet = nil break @@ -4058,7 +4058,7 @@ do -- SET_STATIC local MaxThreatLevelA2G = 0 local MaxThreatText = "" - for StaticName, StaticData in pairs( self:GetSet() ) do + for StaticName, StaticData in pairs(self:GetSet()) do local ThreatStatic = StaticData -- Wrapper.Static#STATIC local ThreatLevelA2G, ThreatText = ThreatStatic:GetThreatLevel() if ThreatLevelA2G > MaxThreatLevelA2G then @@ -4067,7 +4067,7 @@ do -- SET_STATIC end end - --self:F( { MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText } ) + --self:F({ MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText }) return MaxThreatLevelA2G, MaxThreatText end @@ -4076,14 +4076,14 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param Wrapper.Static#STATIC MStatic -- @return #SET_STATIC self - function SET_STATIC:IsIncludeObject( MStatic ) - --self:F2( MStatic ) + function SET_STATIC:IsIncludeObject(MStatic) + --self:F2(MStatic) local MStaticInclude = true if self.Filter.Coalitions then local MStaticCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - --self:T(3( { "Coalition:", MStatic:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + --self:T(3({ "Coalition:", MStatic:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MStatic:GetCoalition() then MStaticCoalition = true end @@ -4093,8 +4093,8 @@ do -- SET_STATIC if self.Filter.Categories then local MStaticCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - --self:T(3( { "Category:", MStatic:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + --self:T(3({ "Category:", MStatic:GetDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MStatic:GetDesc().category then MStaticCategory = true end @@ -4104,8 +4104,8 @@ do -- SET_STATIC if self.Filter.Types then local MStaticType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T(3( { "Type:", MStatic:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T(3({ "Type:", MStatic:GetTypeName(), TypeName }) if TypeName == MStatic:GetTypeName() then MStaticType = true end @@ -4115,8 +4115,8 @@ do -- SET_STATIC if self.Filter.Countries then local MStaticCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - --self:T(3( { "Country:", MStatic:GetCountry(), CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + --self:T(3({ "Country:", MStatic:GetCountry(), CountryName }) if country.id[CountryName] == MStatic:GetCountry() then MStaticCountry = true end @@ -4126,9 +4126,9 @@ do -- SET_STATIC if self.Filter.StaticPrefixes then local MStaticPrefix = false - for StaticPrefixId, StaticPrefix in pairs( self.Filter.StaticPrefixes ) do - --self:T(3( { "Prefix:", string.find( MStatic:GetName(), StaticPrefix, 1 ), StaticPrefix } ) - if string.find( MStatic:GetName(), StaticPrefix, 1 ) then + for StaticPrefixId, StaticPrefix in pairs(self.Filter.StaticPrefixes) do + --self:T(3({ "Prefix:", string.find(MStatic:GetName(), StaticPrefix, 1), StaticPrefix }) + if string.find(MStatic:GetName(), StaticPrefix, 1) then MStaticPrefix = true end end @@ -4137,8 +4137,8 @@ do -- SET_STATIC if self.Filter.Zones then local MStaticZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T(3( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T(3("Zone:", ZoneName) if MStatic and MStatic:IsInZone(Zone) then MStaticZone = true end @@ -4151,7 +4151,7 @@ do -- SET_STATIC MStaticInclude = MStaticInclude and MClientFunc end - --self:T(2( MStaticInclude ) + --self:T(2(MStaticInclude) return MStaticInclude end @@ -4159,24 +4159,24 @@ do -- SET_STATIC -- @param #SET_STATIC self -- @param #string Delimiter (Optional) The delimiter, which is default a comma. -- @return #string The types of the @{Wrapper.Static}s delimited. - function SET_STATIC:GetTypeNames( Delimiter ) + function SET_STATIC:GetTypeNames(Delimiter) Delimiter = Delimiter or ", " local TypeReport = REPORT:New() local Types = {} - for StaticName, StaticData in pairs( self:GetSet() ) do + for StaticName, StaticData in pairs(self:GetSet()) do local Static = StaticData -- Wrapper.Static#STATIC local StaticTypeName = Static:GetTypeName() if not Types[StaticTypeName] then Types[StaticTypeName] = StaticTypeName - TypeReport:Add( StaticTypeName ) + TypeReport:Add(StaticTypeName) end end - return TypeReport:Text( Delimiter ) + return TypeReport:Text(Delimiter) end --- Get the closest static of the set with respect to a given reference coordinate. Optionally, only statics of given coalitions are considered in the search. @@ -4191,7 +4191,7 @@ do -- SET_STATIC local dmin=math.huge local gmin=nil - for GroupID, GroupData in pairs( Set ) do -- For each STATIC in SET_STATIC + for GroupID, GroupData in pairs(Set) do -- For each STATIC in SET_STATIC local group=GroupData --Wrapper.Static#STATIC if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then @@ -4309,9 +4309,9 @@ do -- SET_CLIENT -- DBObject = SET_CLIENT:New() function SET_CLIENT:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CLIENTS ) ) -- #SET_CLIENT + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.CLIENTS)) -- #SET_CLIENT - self:FilterActive( false ) + self:FilterActive(false) return self end @@ -4320,12 +4320,12 @@ do -- SET_CLIENT -- @param Core.Set#SET_CLIENT self -- @param #string AddClientNames A single name or an array of CLIENT names. -- @return self - function SET_CLIENT:AddClientsByName( AddClientNames ) + function SET_CLIENT:AddClientsByName(AddClientNames) - local AddClientNamesArray = (type( AddClientNames ) == "table") and AddClientNames or { AddClientNames } + local AddClientNamesArray = (type(AddClientNames) == "table") and AddClientNames or { AddClientNames } - for AddClientID, AddClientName in pairs( AddClientNamesArray ) do - self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) + for AddClientID, AddClientName in pairs(AddClientNamesArray) do + self:Add(AddClientName, CLIENT:FindByName(AddClientName)) end return self @@ -4335,12 +4335,12 @@ do -- SET_CLIENT -- @param Core.Set#SET_CLIENT self -- @param Wrapper.Client#CLIENT RemoveClientNames A single object or an array of CLIENT objects. -- @return self - function SET_CLIENT:RemoveClientsByName( RemoveClientNames ) + function SET_CLIENT:RemoveClientsByName(RemoveClientNames) - local RemoveClientNamesArray = (type( RemoveClientNames ) == "table") and RemoveClientNames or { RemoveClientNames } + local RemoveClientNamesArray = (type(RemoveClientNames) == "table") and RemoveClientNames or { RemoveClientNames } - for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do - self:Remove( RemoveClientName.ClientName ) + for RemoveClientID, RemoveClientName in pairs(RemoveClientNamesArray) do + self:Remove(RemoveClientName.ClientName) end return self @@ -4350,7 +4350,7 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string ClientName -- @return Wrapper.Client#CLIENT The found Client. - function SET_CLIENT:FindClient( ClientName ) + function SET_CLIENT:FindClient(ClientName) local ClientFound = self.Set[ClientName] return ClientFound @@ -4360,14 +4360,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Callsigns Can be a single string e.g. "Ford", or a table of strings e.g. {"Uzi","Enfield","Chevy"}. Refers to the callsigns as they can be set in the mission editor. -- @return #SET_CLIENT self - function SET_CLIENT:FilterCallsigns( Callsigns ) + function SET_CLIENT:FilterCallsigns(Callsigns) if not self.Filter.Callsigns then self.Filter.Callsigns = {} end - if type( Callsigns ) ~= "table" then + if type(Callsigns) ~= "table" then Callsigns = { Callsigns } end - for callsignID, callsign in pairs( Callsigns ) do + for callsignID, callsign in pairs(Callsigns) do self.Filter.Callsigns[callsign] = callsign end return self @@ -4377,14 +4377,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Playernames Can be a single string e.g. "Apple", or a table of strings e.g. {"Walter","Hermann","Gonzo"}. Useful if you have e.g. a common squadron prefix. -- @return #SET_CLIENT self - function SET_CLIENT:FilterPlayernames( Playernames ) + function SET_CLIENT:FilterPlayernames(Playernames) if not self.Filter.Playernames then self.Filter.Playernames = {} end - if type( Playernames ) ~= "table" then + if type(Playernames) ~= "table" then Playernames = { Playernames } end - for PlayernameID, playername in pairs( Playernames ) do + for PlayernameID, playername in pairs(Playernames) do self.Filter.Playernames[playername] = playername end return self @@ -4401,14 +4401,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @return #SET_CLIENT self - function SET_CLIENT:FilterCategories( Categories ) + function SET_CLIENT:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -4419,14 +4419,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_CLIENT self - function SET_CLIENT:FilterTypes( Types ) + function SET_CLIENT:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -4437,14 +4437,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_CLIENT self - function SET_CLIENT:FilterCountries( Countries ) + function SET_CLIENT:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -4455,14 +4455,14 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings. -- @return #SET_CLIENT self - function SET_CLIENT:FilterPrefixes( Prefixes ) + function SET_CLIENT:FilterPrefixes(Prefixes) if not self.Filter.ClientPrefixes then self.Filter.ClientPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.ClientPrefixes[Prefix] = Prefix end return self @@ -4493,7 +4493,7 @@ do -- SET_CLIENT end return outcome end, Prefixes - ) + ) return self end @@ -4509,15 +4509,15 @@ do -- SET_CLIENT -- ClientSet = SET_CLIENT:New():FilterActive():FilterStart() -- -- -- Include only active clients to the set of the blue coalition, and filter one time. - -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- -- -- Include only active clients to the set of the blue coalition, and filter one time. -- -- Later, reset to include back inactive clients to the set. - -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition("blue"):FilterOnce() -- ... logic ... - -- ClientSet = SET_CLIENT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- ClientSet = SET_CLIENT:New():FilterActive(false):FilterCoalition("blue"):FilterOnce() -- - function SET_CLIENT:FilterActive( Active ) + function SET_CLIENT:FilterActive(Active) Active = Active or not (Active == false) self.Filter.Active = Active return self @@ -4535,7 +4535,7 @@ do -- SET_CLIENT return false end end - ) + ) return self end @@ -4544,20 +4544,20 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_CLIENT self - function SET_CLIENT:FilterZones( Zones ) + function SET_CLIENT:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -4571,10 +4571,10 @@ do -- SET_CLIENT local Database = _DATABASE.CLIENTS - for ObjectName, Object in pairs( Database ) do - if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then - self:Add( ObjectName, Object ) - elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + for ObjectName, Object in pairs(Database) do + if self:IsIncludeObject(Object) and self:IsNotInSet(Object) then + self:Add(ObjectName, Object) + elseif (not self:IsIncludeObject(Object)) and self:IsInSet(Object) then self:Remove(ObjectName) end end @@ -4620,11 +4620,11 @@ do -- SET_CLIENT function SET_CLIENT:FilterStart() if _DATABASE then - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventPlayerEnterUnit) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventPlayerLeaveUnit) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) + --self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventPlayerEnterUnit) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventPlayerLeaveUnit) --self:SetEventPriority(1) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) @@ -4642,14 +4642,14 @@ do -- SET_CLIENT -- @param Core.Event#EVENTDATA Event -- @return #SET_CLIENT self function SET_CLIENT:_EventPlayerEnterUnit(Event) - --self:I( "_EventPlayerEnterUnit" ) + --self:I("_EventPlayerEnterUnit") if Event.IniDCSUnit then if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup and Event.IniGroup:IsGround() then -- CA Slot entered - local ObjectName, Object = self:AddInDatabase( Event ) - --self:T(( ObjectName, UTILS.PrintTableToLog(Object) ) - if Object and self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + local ObjectName, Object = self:AddInDatabase(Event) + --self:T((ObjectName, UTILS.PrintTableToLog(Object)) + if Object and self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) end end end @@ -4661,13 +4661,13 @@ do -- SET_CLIENT -- @param Core.Event#EVENTDATA Event -- @return #SET_CLIENT self function SET_CLIENT:_EventPlayerLeaveUnit(Event) - --self:I( "_EventPlayerLeaveUnit" ) + --self:I("_EventPlayerLeaveUnit") if Event.IniDCSUnit then if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup then --and Event.IniGroup:IsGround() then -- CA Slot left - local ObjectName, Object = self:FindInDatabase( Event ) + local ObjectName, Object = self:FindInDatabase(Event) if ObjectName then - self:Remove( ObjectName ) + self:Remove(ObjectName) end end end @@ -4690,8 +4690,8 @@ do -- SET_CLIENT -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CLIENT -- @return #table The CLIENT - function SET_CLIENT:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_CLIENT:AddInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -4702,8 +4702,8 @@ do -- SET_CLIENT -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CLIENT -- @return #table The CLIENT - function SET_CLIENT:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_CLIENT:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -4712,10 +4712,10 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. -- @return #SET_CLIENT self - function SET_CLIENT:ForEachClient( IteratorFunction, ... ) - --self:F2( arg ) + function SET_CLIENT:ForEachClient(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -4725,19 +4725,19 @@ do -- SET_CLIENT -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. -- @return #SET_CLIENT self - function SET_CLIENT:ForEachClientInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_CLIENT:ForEachClientInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsInZone( ZoneObject ) then + function(ZoneObject, ClientObject) + if ClientObject:IsInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -4747,19 +4747,19 @@ do -- SET_CLIENT -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. -- @return #SET_CLIENT self - function SET_CLIENT:ForEachClientNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_CLIENT:ForEachClientNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, ClientObject) + if ClientObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -4772,7 +4772,7 @@ do -- SET_CLIENT local Set = self:GetSet() local CountU = 0 - for UnitID, UnitData in pairs( Set ) do -- For each GROUP in SET_GROUP + for UnitID, UnitData in pairs(Set) do -- For each GROUP in SET_GROUP if UnitData and UnitData:IsAlive() then CountU = CountU + 1 end @@ -4815,7 +4815,7 @@ do -- SET_CLIENT -- if client:GetPlayerName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(groundset:Flush()) @@ -4823,8 +4823,8 @@ do -- SET_CLIENT -- @param #SET_CLIENT self -- @param Wrapper.Client#CLIENT MClient -- @return #SET_CLIENT self - function SET_CLIENT:IsIncludeObject( MClient ) - --self:F2( MClient ) + function SET_CLIENT:IsIncludeObject(MClient) + --self:F2(MClient) local MClientInclude = true @@ -4836,94 +4836,94 @@ do -- SET_CLIENT if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true and MClient:IsAlive() == true) then MClientActive = true end - --self:T( { "Evaluated Active", MClientActive } ) + --self:T({ "Evaluated Active", MClientActive }) MClientInclude = MClientInclude and MClientActive end if self.Filter.Coalitions and MClientInclude then local MClientCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate(MClientName) if ClientCoalitionID==nil and MClient:IsAlive()~=nil then ClientCoalitionID=MClient:GetCoalition() end - --self:T3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + --self:T3({ "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and ClientCoalitionID and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then MClientCoalition = true end end - --self:T( { "Evaluated Coalition", MClientCoalition } ) + --self:T({ "Evaluated Coalition", MClientCoalition }) MClientInclude = MClientInclude and MClientCoalition end if self.Filter.Categories and MClientInclude then local MClientCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate(MClientName) local UnitCategory = 0 if ClientCategoryID==nil and MClient:IsExist() then ClientCategoryID,UnitCategory=MClient:GetCategory() --self:T3("Applying Category Workaround .. Outcome: Obj is "..tostring(ClientCategoryID).." Unit is "..tostring(UnitCategory)) - --self:T(3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) + --self:T(3({ "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then MClientCategory = true end --self:T3("Filter Outcome is "..tostring(MClientCategory)) else - --self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + --self:T3({ "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and ClientCategoryID and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then MClientCategory = true end end end - --self:T( { "Evaluated Category", MClientCategory } ) + --self:T({ "Evaluated Category", MClientCategory }) MClientInclude = MClientInclude and MClientCategory end if self.Filter.Types and MClientInclude then local MClientType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T3( { "Type:", MClient:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T3({ "Type:", MClient:GetTypeName(), TypeName }) if TypeName == MClient:GetTypeName() then MClientType = true end end - --self:T(( { "Evaluated Type", MClientType } ) + --self:T(({ "Evaluated Type", MClientType }) MClientInclude = MClientInclude and MClientType end if self.Filter.Countries and MClientInclude then local MClientCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + local ClientCountryID = _DATABASE:GetCountryFromClientTemplate(MClientName) if ClientCountryID==nil and MClient:IsAlive()~=nil then ClientCountryID=MClient:GetCountry() end - --self:T(3( { "Country:", ClientCountryID, country.id[CountryName], CountryName } ) + --self:T(3({ "Country:", ClientCountryID, country.id[CountryName], CountryName }) if country.id[CountryName] and ClientCountryID and country.id[CountryName] == ClientCountryID then MClientCountry = true end end - --self:T(( { "Evaluated Country", MClientCountry } ) + --self:T(({ "Evaluated Country", MClientCountry }) MClientInclude = MClientInclude and MClientCountry end if self.Filter.ClientPrefixes and MClientInclude then local MClientPrefix = false - for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do - --self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } ) - if string.find( MClient.UnitName, ClientPrefix, 1 ) then + for ClientPrefixId, ClientPrefix in pairs(self.Filter.ClientPrefixes) do + --self:T3({ "Prefix:", string.find(MClient.UnitName, ClientPrefix, 1), ClientPrefix }) + if string.find(MClient.UnitName, ClientPrefix, 1) then MClientPrefix = true end end - --self:T( { "Evaluated Prefix", MClientPrefix } ) + --self:T({ "Evaluated Prefix", MClientPrefix }) MClientInclude = MClientInclude and MClientPrefix end if self.Filter.Zones and MClientInclude then local MClientZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T3( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T3("Zone:", ZoneName) local unit = MClient:GetClientGroupUnit() if unit and unit:IsInZone(Zone) then MClientZone = true @@ -4941,7 +4941,7 @@ do -- SET_CLIENT MClientPlayername = true end end - --self:T( { "Evaluated Playername", MClientPlayername } ) + --self:T({ "Evaluated Playername", MClientPlayername }) MClientInclude = MClientInclude and MClientPlayername end @@ -4954,7 +4954,7 @@ do -- SET_CLIENT MClientCallsigns = true end end - --self:T( { "Evaluated Callsign", MClientCallsigns } ) + --self:T({ "Evaluated Callsign", MClientCallsigns }) MClientInclude = MClientInclude and MClientCallsigns end @@ -4964,7 +4964,7 @@ do -- SET_CLIENT end end - --self:T2( MClientInclude ) + --self:T2(MClientInclude) return MClientInclude end @@ -5049,7 +5049,7 @@ do -- SET_PLAYER -- DBObject = SET_PLAYER:New() function SET_PLAYER:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.PLAYERS ) ) + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.PLAYERS)) return self end @@ -5058,12 +5058,12 @@ do -- SET_PLAYER -- @param Core.Set#SET_PLAYER self -- @param #string AddClientNames A single name or an array of CLIENT names. -- @return self - function SET_PLAYER:AddClientsByName( AddClientNames ) + function SET_PLAYER:AddClientsByName(AddClientNames) - local AddClientNamesArray = (type( AddClientNames ) == "table") and AddClientNames or { AddClientNames } + local AddClientNamesArray = (type(AddClientNames) == "table") and AddClientNames or { AddClientNames } - for AddClientID, AddClientName in pairs( AddClientNamesArray ) do - self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) + for AddClientID, AddClientName in pairs(AddClientNamesArray) do + self:Add(AddClientName, CLIENT:FindByName(AddClientName)) end return self @@ -5073,12 +5073,12 @@ do -- SET_PLAYER -- @param Core.Set#SET_PLAYER self -- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names. -- @return self - function SET_PLAYER:RemoveClientsByName( RemoveClientNames ) + function SET_PLAYER:RemoveClientsByName(RemoveClientNames) - local RemoveClientNamesArray = (type( RemoveClientNames ) == "table") and RemoveClientNames or { RemoveClientNames } + local RemoveClientNamesArray = (type(RemoveClientNames) == "table") and RemoveClientNames or { RemoveClientNames } - for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do - self:Remove( RemoveClientName.ClientName ) + for RemoveClientID, RemoveClientName in pairs(RemoveClientNamesArray) do + self:Remove(RemoveClientName.ClientName) end return self @@ -5088,7 +5088,7 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string PlayerName -- @return Wrapper.Client#CLIENT The found Client. - function SET_PLAYER:FindClient( PlayerName ) + function SET_PLAYER:FindClient(PlayerName) local ClientFound = self.Set[PlayerName] return ClientFound @@ -5104,20 +5104,20 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_PLAYER self - function SET_PLAYER:FilterZones( Zones ) + function SET_PLAYER:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -5130,14 +5130,14 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". -- @return #SET_PLAYER self - function SET_PLAYER:FilterCategories( Categories ) + function SET_PLAYER:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -5148,14 +5148,14 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_PLAYER self - function SET_PLAYER:FilterTypes( Types ) + function SET_PLAYER:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -5166,14 +5166,14 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_PLAYER self - function SET_PLAYER:FilterCountries( Countries ) + function SET_PLAYER:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -5184,14 +5184,14 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #string Prefixes The string pattern(s) that needs to be contained in the unit/pilot name. Can also be passed as a `#table` of strings. -- @return #SET_PLAYER self - function SET_PLAYER:FilterPrefixes( Prefixes ) + function SET_PLAYER:FilterPrefixes(Prefixes) if not self.Filter.ClientPrefixes then self.Filter.ClientPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.ClientPrefixes[Prefix] = Prefix end return self @@ -5204,10 +5204,10 @@ do -- SET_PLAYER if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash ) + self:HandleEvent(EVENTS.Birth, self._EventOnBirth) + self:HandleEvent(EVENTS.Dead, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.Crash, self._EventOnDeadOrCrash) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventOnDeadOrCrash) end return self @@ -5219,8 +5219,8 @@ do -- SET_PLAYER -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CLIENT -- @return #table The CLIENT - function SET_PLAYER:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_PLAYER:AddInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -5231,8 +5231,8 @@ do -- SET_PLAYER -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CLIENT -- @return #table The CLIENT - function SET_PLAYER:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_PLAYER:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -5241,10 +5241,10 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. -- @return #SET_PLAYER self - function SET_PLAYER:ForEachPlayer( IteratorFunction, ... ) - --self:F2( arg ) + function SET_PLAYER:ForEachPlayer(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -5254,19 +5254,19 @@ do -- SET_PLAYER -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. -- @return #SET_PLAYER self - function SET_PLAYER:ForEachPlayerInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_PLAYER:ForEachPlayerInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsInZone( ZoneObject ) then + function(ZoneObject, ClientObject) + if ClientObject:IsInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -5276,19 +5276,19 @@ do -- SET_PLAYER -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. -- @return #SET_PLAYER self - function SET_PLAYER:ForEachPlayerNotInZone( ZoneObject, IteratorFunction, ... ) - --self:F2( arg ) + function SET_PLAYER:ForEachPlayerNotInZone(ZoneObject, IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet(), + self:ForEach(IteratorFunction, arg, self:GetSet(), -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject - function( ZoneObject, ClientObject ) - if ClientObject:IsNotInZone( ZoneObject ) then + function(ZoneObject, ClientObject) + if ClientObject:IsNotInZone(ZoneObject) then return true else return false end - end, { ZoneObject } ) + end, { ZoneObject }) return self end @@ -5297,8 +5297,8 @@ do -- SET_PLAYER -- @param #SET_PLAYER self -- @param Wrapper.Client#CLIENT MClient -- @return #SET_PLAYER self - function SET_PLAYER:IsIncludeObject( MClient ) - --self:F2( MClient ) + function SET_PLAYER:IsIncludeObject(MClient) + --self:F2(MClient) local MClientInclude = true @@ -5307,84 +5307,84 @@ do -- SET_PLAYER if self.Filter.Coalitions and MClientInclude then local MClientCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate(MClientName) if ClientCoalitionID==nil and MClient:IsAlive()~=nil then ClientCoalitionID=MClient:GetCoalition() end - --self:T(3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + --self:T(3({ "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and ClientCoalitionID and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then MClientCoalition = true end end - --self:T(( { "Evaluated Coalition", MClientCoalition } ) + --self:T(({ "Evaluated Coalition", MClientCoalition }) MClientInclude = MClientInclude and MClientCoalition end if self.Filter.Categories and MClientInclude then local MClientCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate(MClientName) local UnitCategory = 0 if ClientCategoryID==nil and MClient:IsExist() then ClientCategoryID,UnitCategory=MClient:GetCategory() - --self:T(3( { "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName } ) + --self:T(3({ "Category:", UnitCategory, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and UnitCategory and self.FilterMeta.Categories[CategoryName] == UnitCategory then MClientCategory = true end else - --self:T(3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + --self:T(3({ "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and ClientCategoryID and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then MClientCategory = true end end end - --self:T(( { "Evaluated Category", MClientCategory } ) + --self:T(({ "Evaluated Category", MClientCategory }) MClientInclude = MClientInclude and MClientCategory end if self.Filter.Types then local MClientType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T(3( { "Type:", MClient:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T(3({ "Type:", MClient:GetTypeName(), TypeName }) if TypeName == MClient:GetTypeName() then MClientType = true end end - --self:T(( { "Evaluated Type", MClientType } ) + --self:T(({ "Evaluated Type", MClientType }) MClientInclude = MClientInclude and MClientType end if self.Filter.Countries then local MClientCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - local ClientCountryID = _DATABASE:GetCountryFromClientTemplate( MClientName ) - --self:T(3( { "Country:", ClientCountryID, country.id[CountryName], CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + local ClientCountryID = _DATABASE:GetCountryFromClientTemplate(MClientName) + --self:T(3({ "Country:", ClientCountryID, country.id[CountryName], CountryName }) if country.id[CountryName] and country.id[CountryName] == ClientCountryID then MClientCountry = true end end - --self:T(( { "Evaluated Country", MClientCountry } ) + --self:T(({ "Evaluated Country", MClientCountry }) MClientInclude = MClientInclude and MClientCountry end if self.Filter.ClientPrefixes then local MClientPrefix = false - for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do - --self:T(3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } ) - if string.find( MClient.UnitName, ClientPrefix, 1 ) then + for ClientPrefixId, ClientPrefix in pairs(self.Filter.ClientPrefixes) do + --self:T(3({ "Prefix:", string.find(MClient.UnitName, ClientPrefix, 1), ClientPrefix }) + if string.find(MClient.UnitName, ClientPrefix, 1) then MClientPrefix = true end end - --self:T(( { "Evaluated Prefix", MClientPrefix } ) + --self:T(({ "Evaluated Prefix", MClientPrefix }) MClientInclude = MClientInclude and MClientPrefix end end if self.Filter.Zones then local MClientZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T(3( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T(3("Zone:", ZoneName) local unit = MClient:GetClientGroupUnit() if unit and unit:IsInZone(Zone) then MClientZone = true @@ -5398,7 +5398,7 @@ do -- SET_PLAYER MClientInclude = MClientInclude and MClientFunc end - --self:T(2( MClientInclude ) + --self:T(2(MClientInclude) return MClientInclude end @@ -5475,7 +5475,7 @@ do -- SET_AIRBASE -- DatabaseSet = SET_AIRBASE:New() function SET_AIRBASE:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.AIRBASES ) ) + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.AIRBASES)) return self end @@ -5484,9 +5484,9 @@ do -- SET_AIRBASE -- @param Core.Set#SET_AIRBASE self -- @param Wrapper.Airbase#AIRBASE airbase Airbase that should be added to the set. -- @return self - function SET_AIRBASE:AddAirbase( airbase ) + function SET_AIRBASE:AddAirbase(airbase) - self:Add( airbase:GetName(), airbase ) + self:Add(airbase:GetName(), airbase) return self end @@ -5495,12 +5495,12 @@ do -- SET_AIRBASE -- @param Core.Set#SET_AIRBASE self -- @param #string AddAirbaseNames A single name or an array of AIRBASE names. -- @return self - function SET_AIRBASE:AddAirbasesByName( AddAirbaseNames ) + function SET_AIRBASE:AddAirbasesByName(AddAirbaseNames) - local AddAirbaseNamesArray = (type( AddAirbaseNames ) == "table") and AddAirbaseNames or { AddAirbaseNames } + local AddAirbaseNamesArray = (type(AddAirbaseNames) == "table") and AddAirbaseNames or { AddAirbaseNames } - for AddAirbaseID, AddAirbaseName in pairs( AddAirbaseNamesArray ) do - self:Add( AddAirbaseName, AIRBASE:FindByName( AddAirbaseName ) ) + for AddAirbaseID, AddAirbaseName in pairs(AddAirbaseNamesArray) do + self:Add(AddAirbaseName, AIRBASE:FindByName(AddAirbaseName)) end return self @@ -5510,12 +5510,12 @@ do -- SET_AIRBASE -- @param Core.Set#SET_AIRBASE self -- @param Wrapper.Airbase#AIRBASE RemoveAirbaseNames A single name or an array of AIRBASE names. -- @return self - function SET_AIRBASE:RemoveAirbasesByName( RemoveAirbaseNames ) + function SET_AIRBASE:RemoveAirbasesByName(RemoveAirbaseNames) - local RemoveAirbaseNamesArray = (type( RemoveAirbaseNames ) == "table") and RemoveAirbaseNames or { RemoveAirbaseNames } + local RemoveAirbaseNamesArray = (type(RemoveAirbaseNames) == "table") and RemoveAirbaseNames or { RemoveAirbaseNames } - for RemoveAirbaseID, RemoveAirbaseName in pairs( RemoveAirbaseNamesArray ) do - self:Remove( RemoveAirbaseName ) + for RemoveAirbaseID, RemoveAirbaseName in pairs(RemoveAirbaseNamesArray) do + self:Remove(RemoveAirbaseName) end return self @@ -5525,7 +5525,7 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param #string AirbaseName -- @return Wrapper.Airbase#AIRBASE The found Airbase. - function SET_AIRBASE:FindAirbase( AirbaseName ) + function SET_AIRBASE:FindAirbase(AirbaseName) local AirbaseFound = self.Set[AirbaseName] return AirbaseFound @@ -5536,16 +5536,16 @@ do -- SET_AIRBASE -- @param Core.Point#COORDINATE Coordinate -- @param #number Range -- @return Wrapper.Airbase#AIRBASE The found Airbase. - function SET_AIRBASE:FindAirbaseInRange( Coordinate, Range ) + function SET_AIRBASE:FindAirbaseInRange(Coordinate, Range) local AirbaseFound = nil - for AirbaseName, AirbaseObject in pairs( self.Set ) do + for AirbaseName, AirbaseObject in pairs(self.Set) do local AirbaseCoordinate = AirbaseObject:GetCoordinate() - local Distance = Coordinate:Get2DDistance( AirbaseCoordinate ) + local Distance = Coordinate:Get2DDistance(AirbaseCoordinate) - --self:F( { Distance = Distance } ) + --self:F({ Distance = Distance }) if Distance <= Range then AirbaseFound = AirbaseObject @@ -5563,7 +5563,7 @@ do -- SET_AIRBASE function SET_AIRBASE:GetRandomAirbase() local RandomAirbase = self:GetRandom() - --self:F( { RandomAirbase = RandomAirbase:GetName() } ) + --self:F({ RandomAirbase = RandomAirbase:GetName() }) return RandomAirbase end @@ -5579,14 +5579,14 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param #string Categories Can take the following values: "airdrome", "helipad", "ship". -- @return #SET_AIRBASE self - function SET_AIRBASE:FilterCategories( Categories ) + function SET_AIRBASE:FilterCategories(Categories) if not self.Filter.Categories then self.Filter.Categories = {} end - if type( Categories ) ~= "table" then + if type(Categories) ~= "table" then Categories = { Categories } end - for CategoryID, Category in pairs( Categories ) do + for CategoryID, Category in pairs(Categories) do self.Filter.Categories[Category] = Category end return self @@ -5596,20 +5596,20 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_AIRBASE self - function SET_AIRBASE:FilterZones( Zones ) + function SET_AIRBASE:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() --self:T((zonename) self.Filter.Zones[zonename] = Zone @@ -5625,15 +5625,15 @@ do -- SET_AIRBASE if _DATABASE then -- We use the BaseCaptured event, which is generated by DCS when a base got captured. - self:HandleEvent( EVENTS.BaseCaptured ) - self:HandleEvent( EVENTS.Dead ) + self:HandleEvent(EVENTS.BaseCaptured) + self:HandleEvent(EVENTS.Dead) -- We initialize the first set. - for ObjectName, Object in pairs( self.Database ) do - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + for ObjectName, Object in pairs(self.Database) do + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) else - self:RemoveAirbasesByName( ObjectName ) + self:RemoveAirbasesByName(ObjectName) end end end @@ -5644,16 +5644,16 @@ do -- SET_AIRBASE --- Base capturing event. -- @param #SET_AIRBASE self -- @param Core.Event#EVENT EventData - function SET_AIRBASE:OnEventBaseCaptured( EventData ) + function SET_AIRBASE:OnEventBaseCaptured(EventData) -- When a base got captured, we reevaluate the set. - for ObjectName, Object in pairs( self.Database ) do - if self:IsIncludeObject( Object ) then + for ObjectName, Object in pairs(self.Database) do + if self:IsIncludeObject(Object) then -- We add captured bases on yet in the set. - self:Add( ObjectName, Object ) + self:Add(ObjectName, Object) else -- We remove captured bases that are not anymore part of the set. - self:RemoveAirbasesByName( ObjectName ) + self:RemoveAirbasesByName(ObjectName) end end @@ -5662,12 +5662,12 @@ do -- SET_AIRBASE --- Dead event. -- @param #SET_AIRBASE self -- @param Core.Event#EVENT EventData - function SET_AIRBASE:OnEventDead( EventData ) + function SET_AIRBASE:OnEventDead(EventData) - local airbaseName, airbase = self:FindInDatabase( EventData ) + local airbaseName, airbase = self:FindInDatabase(EventData) if airbase and (airbase:IsShip() or airbase:IsHelipad()) then - self:RemoveAirbasesByName( airbaseName ) + self:RemoveAirbasesByName(airbaseName) end end @@ -5678,7 +5678,7 @@ do -- SET_AIRBASE -- @param Core.Event#EVENTDATA Event Event data. -- @return #string The name of the AIRBASE. -- @return Wrapper.Airbase#AIRBASE The AIRBASE object. - function SET_AIRBASE:AddInDatabase( Event ) + function SET_AIRBASE:AddInDatabase(Event) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -5688,8 +5688,8 @@ do -- SET_AIRBASE -- @param Core.Event#EVENTDATA Event Event data. -- @return #string The name of the AIRBASE. -- @return Wrapper.Airbase#AIRBASE The AIRBASE object. - function SET_AIRBASE:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_AIRBASE:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -5698,10 +5698,10 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param #function IteratorFunction The function that will be called when there is an alive AIRBASE in the SET_AIRBASE. The function needs to accept a AIRBASE parameter. -- @return #SET_AIRBASE self - function SET_AIRBASE:ForEachAirbase( IteratorFunction, ... ) - --self:F2( arg ) + function SET_AIRBASE:ForEachAirbase(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -5710,10 +5710,10 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}. -- @return Wrapper.Airbase#AIRBASE The closest @{Wrapper.Airbase#AIRBASE}. - function SET_AIRBASE:FindNearestAirbaseFromPointVec2( Coordinate ) - --self:F2( Coordinate ) + function SET_AIRBASE:FindNearestAirbaseFromPointVec2(Coordinate) + --self:F2(Coordinate) - local NearestAirbase = self:FindNearestObjectFromPointVec2( Coordinate ) + local NearestAirbase = self:FindNearestObjectFromPointVec2(Coordinate) return NearestAirbase end @@ -5721,8 +5721,8 @@ do -- SET_AIRBASE -- @param #SET_AIRBASE self -- @param Wrapper.Airbase#AIRBASE MAirbase -- @return #SET_AIRBASE self - function SET_AIRBASE:IsIncludeObject( MAirbase ) - --self:F2( MAirbase ) + function SET_AIRBASE:IsIncludeObject(MAirbase) + --self:F2(MAirbase) local MAirbaseInclude = true @@ -5731,39 +5731,39 @@ do -- SET_AIRBASE if self.Filter.Coalitions then local MAirbaseCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local AirbaseCoalitionID = _DATABASE:GetCoalitionFromAirbase( MAirbaseName ) - --self:T(3( { "Coalition:", AirbaseCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + local AirbaseCoalitionID = _DATABASE:GetCoalitionFromAirbase(MAirbaseName) + --self:T(3({ "Coalition:", AirbaseCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == AirbaseCoalitionID then MAirbaseCoalition = true end end - --self:T(( { "Evaluated Coalition", MAirbaseCoalition } ) + --self:T(({ "Evaluated Coalition", MAirbaseCoalition }) MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition end if self.Filter.Categories and MAirbaseInclude then local MAirbaseCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName ) - --self:T(3( { "Category:", AirbaseCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + for CategoryID, CategoryName in pairs(self.Filter.Categories) do + local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase(MAirbaseName) + --self:T(3({ "Category:", AirbaseCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName }) if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == AirbaseCategoryID then MAirbaseCategory = true end end - --self:T(( { "Evaluated Category", MAirbaseCategory } ) + --self:T(({ "Evaluated Category", MAirbaseCategory }) MAirbaseInclude = MAirbaseInclude and MAirbaseCategory end if self.Filter.Zones and MAirbaseInclude then local MAirbaseZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T(( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T(("Zone:", ZoneName) local coord = MAirbase:GetCoordinate() if coord and Zone:IsCoordinateInZone(coord) then MAirbaseZone = true end - --self:T(( { "Evaluated Zone", MSceneryZone } ) + --self:T(({ "Evaluated Zone", MSceneryZone }) end MAirbaseInclude = MAirbaseInclude and MAirbaseZone end @@ -5775,7 +5775,7 @@ do -- SET_AIRBASE MAirbaseInclude = MAirbaseInclude and MClientFunc end - --self:T(2( MAirbaseInclude ) + --self:T(2(MAirbaseInclude) return MAirbaseInclude end @@ -5854,7 +5854,7 @@ do -- SET_CARGO -- DatabaseSet = SET_CARGO:New() function SET_CARGO:New() -- R2.1 -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CARGOS ) ) -- #SET_CARGO + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.CARGOS)) -- #SET_CARGO return self end @@ -5863,9 +5863,9 @@ do -- SET_CARGO -- @param Core.Set#SET_CARGO self -- @param Cargo.Cargo#CARGO Cargo A single cargo. -- @return Core.Set#SET_CARGO self - function SET_CARGO:AddCargo( Cargo ) -- R2.4 + function SET_CARGO:AddCargo(Cargo) -- R2.4 - self:Add( Cargo:GetName(), Cargo ) + self:Add(Cargo:GetName(), Cargo) return self end @@ -5874,12 +5874,12 @@ do -- SET_CARGO -- @param Core.Set#SET_CARGO self -- @param #string AddCargoNames A single name or an array of CARGO names. -- @return Core.Set#SET_CARGO self - function SET_CARGO:AddCargosByName( AddCargoNames ) -- R2.1 + function SET_CARGO:AddCargosByName(AddCargoNames) -- R2.1 - local AddCargoNamesArray = (type( AddCargoNames ) == "table") and AddCargoNames or { AddCargoNames } + local AddCargoNamesArray = (type(AddCargoNames) == "table") and AddCargoNames or { AddCargoNames } - for AddCargoID, AddCargoName in pairs( AddCargoNamesArray ) do - self:Add( AddCargoName, CARGO:FindByName( AddCargoName ) ) + for AddCargoID, AddCargoName in pairs(AddCargoNamesArray) do + self:Add(AddCargoName, CARGO:FindByName(AddCargoName)) end return self @@ -5889,12 +5889,12 @@ do -- SET_CARGO -- @param Core.Set#SET_CARGO self -- @param Cargo.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names. -- @return Core.Set#SET_CARGO self - function SET_CARGO:RemoveCargosByName( RemoveCargoNames ) -- R2.1 + function SET_CARGO:RemoveCargosByName(RemoveCargoNames) -- R2.1 - local RemoveCargoNamesArray = (type( RemoveCargoNames ) == "table") and RemoveCargoNames or { RemoveCargoNames } + local RemoveCargoNamesArray = (type(RemoveCargoNames) == "table") and RemoveCargoNames or { RemoveCargoNames } - for RemoveCargoID, RemoveCargoName in pairs( RemoveCargoNamesArray ) do - self:Remove( RemoveCargoName.CargoName ) + for RemoveCargoID, RemoveCargoName in pairs(RemoveCargoNamesArray) do + self:Remove(RemoveCargoName.CargoName) end return self @@ -5904,7 +5904,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param #string CargoName -- @return Cargo.Cargo#CARGO The found Cargo. - function SET_CARGO:FindCargo( CargoName ) -- R2.1 + function SET_CARGO:FindCargo(CargoName) -- R2.1 local CargoFound = self.Set[CargoName] return CargoFound @@ -5921,14 +5921,14 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param #string Types Can take those type strings known within DCS world. -- @return #SET_CARGO self - function SET_CARGO:FilterTypes( Types ) -- R2.1 + function SET_CARGO:FilterTypes(Types) -- R2.1 if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -5939,14 +5939,14 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_CARGO self - function SET_CARGO:FilterCountries( Countries ) -- R2.1 + function SET_CARGO:FilterCountries(Countries) -- R2.1 if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -5957,14 +5957,14 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param #string Prefixes The string pattern(s) that need to be in the cargo name. Can also be passed as a `#table` of strings. -- @return #SET_CARGO self - function SET_CARGO:FilterPrefixes( Prefixes ) -- R2.1 + function SET_CARGO:FilterPrefixes(Prefixes) -- R2.1 if not self.Filter.CargoPrefixes then self.Filter.CargoPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.CargoPrefixes[Prefix] = Prefix end return self @@ -5977,8 +5977,8 @@ do -- SET_CARGO if _DATABASE then self:_FilterStart() - self:HandleEvent( EVENTS.NewCargo ) - self:HandleEvent( EVENTS.DeleteCargo ) + self:HandleEvent(EVENTS.NewCargo) + self:HandleEvent(EVENTS.DeleteCargo) end return self @@ -5989,8 +5989,8 @@ do -- SET_CARGO -- @return #SET_CARGO self function SET_CARGO:FilterStop() - self:UnHandleEvent( EVENTS.NewCargo ) - self:UnHandleEvent( EVENTS.DeleteCargo ) + self:UnHandleEvent(EVENTS.NewCargo) + self:UnHandleEvent(EVENTS.DeleteCargo) return self end @@ -6001,8 +6001,8 @@ do -- SET_CARGO -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CARGO -- @return #table The CARGO - function SET_CARGO:AddInDatabase( Event ) -- R2.1 - --self:F3( { Event } ) + function SET_CARGO:AddInDatabase(Event) -- R2.1 + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -6013,8 +6013,8 @@ do -- SET_CARGO -- @param Core.Event#EVENTDATA Event -- @return #string The name of the CARGO -- @return #table The CARGO - function SET_CARGO:FindInDatabase( Event ) -- R2.1 - --self:F3( { Event } ) + function SET_CARGO:FindInDatabase(Event) -- R2.1 + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -6023,10 +6023,10 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param #function IteratorFunction The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter. -- @return #SET_CARGO self - function SET_CARGO:ForEachCargo( IteratorFunction, ... ) -- R2.1 - --self:F2( arg ) + function SET_CARGO:ForEachCargo(IteratorFunction, ...) -- R2.1 + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -6035,21 +6035,21 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param Core.Point#COORDINATE Coordinate A @{Core.Point#COORDINATE} object from where to evaluate the closest @{Cargo.Cargo#CARGO}. -- @return Cargo.Cargo#CARGO The closest @{Cargo.Cargo#CARGO}. - function SET_CARGO:FindNearestCargoFromPointVec2( Coordinate ) -- R2.1 - --self:F2( Coordinate ) + function SET_CARGO:FindNearestCargoFromPointVec2(Coordinate) -- R2.1 + --self:F2(Coordinate) - local NearestCargo = self:FindNearestObjectFromPointVec2( Coordinate ) + local NearestCargo = self:FindNearestObjectFromPointVec2(Coordinate) return NearestCargo end --- -- @param #SET_CARGO self - function SET_CARGO:FirstCargoWithState( State ) + function SET_CARGO:FirstCargoWithState(State) local FirstCargo = nil - for CargoName, Cargo in pairs( self.Set ) do - if Cargo:Is( State ) then + for CargoName, Cargo in pairs(self.Set) do + if Cargo:Is(State) then FirstCargo = Cargo break end @@ -6060,12 +6060,12 @@ do -- SET_CARGO --- -- @param #SET_CARGO self - function SET_CARGO:FirstCargoWithStateAndNotDeployed( State ) + function SET_CARGO:FirstCargoWithStateAndNotDeployed(State) local FirstCargo = nil - for CargoName, Cargo in pairs( self.Set ) do - if Cargo:Is( State ) and not Cargo:IsDeployed() then + for CargoName, Cargo in pairs(self.Set) do + if Cargo:Is(State) and not Cargo:IsDeployed() then FirstCargo = Cargo break end @@ -6078,7 +6078,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoUnLoaded() - local FirstCargo = self:FirstCargoWithState( "UnLoaded" ) + local FirstCargo = self:FirstCargoWithState("UnLoaded") return FirstCargo end @@ -6086,7 +6086,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoUnLoadedAndNotDeployed() - local FirstCargo = self:FirstCargoWithStateAndNotDeployed( "UnLoaded" ) + local FirstCargo = self:FirstCargoWithStateAndNotDeployed("UnLoaded") return FirstCargo end @@ -6094,7 +6094,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoLoaded() - local FirstCargo = self:FirstCargoWithState( "Loaded" ) + local FirstCargo = self:FirstCargoWithState("Loaded") return FirstCargo end @@ -6102,7 +6102,7 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoDeployed() - local FirstCargo = self:FirstCargoWithState( "Deployed" ) + local FirstCargo = self:FirstCargoWithState("Deployed") return FirstCargo end @@ -6110,8 +6110,8 @@ do -- SET_CARGO -- @param #SET_CARGO self -- @param AI.AI_Cargo#AI_CARGO MCargo -- @return #SET_CARGO self - function SET_CARGO:IsIncludeObject( MCargo ) -- R2.1 - --self:F2( MCargo ) + function SET_CARGO:IsIncludeObject(MCargo) -- R2.1 + --self:F2(MCargo) local MCargoInclude = true @@ -6120,38 +6120,38 @@ do -- SET_CARGO if self.Filter.Coalitions then local MCargoCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do local CargoCoalitionID = MCargo:GetCoalition() - --self:T(3( { "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + --self:T(3({ "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == CargoCoalitionID then MCargoCoalition = true end end - --self:F( { "Evaluated Coalition", MCargoCoalition } ) + --self:F({ "Evaluated Coalition", MCargoCoalition }) MCargoInclude = MCargoInclude and MCargoCoalition end if self.Filter.Types then local MCargoType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T(3( { "Type:", MCargo:GetType(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T(3({ "Type:", MCargo:GetType(), TypeName }) if TypeName == MCargo:GetType() then MCargoType = true end end - --self:F( { "Evaluated Type", MCargoType } ) + --self:F({ "Evaluated Type", MCargoType }) MCargoInclude = MCargoInclude and MCargoType end if self.Filter.CargoPrefixes then local MCargoPrefix = false - for CargoPrefixId, CargoPrefix in pairs( self.Filter.CargoPrefixes ) do - --self:T(3( { "Prefix:", string.find( MCargo.Name, CargoPrefix, 1 ), CargoPrefix } ) - if string.find( MCargo.Name, CargoPrefix, 1 ) then + for CargoPrefixId, CargoPrefix in pairs(self.Filter.CargoPrefixes) do + --self:T(3({ "Prefix:", string.find(MCargo.Name, CargoPrefix, 1), CargoPrefix }) + if string.find(MCargo.Name, CargoPrefix, 1) then MCargoPrefix = true end end - --self:F( { "Evaluated Prefix", MCargoPrefix } ) + --self:F({ "Evaluated Prefix", MCargoPrefix }) MCargoInclude = MCargoInclude and MCargoPrefix end end @@ -6161,20 +6161,20 @@ do -- SET_CARGO MCargoInclude = MCargoInclude and MClientFunc end - --self:T(2( MCargoInclude ) + --self:T(2(MCargoInclude) return MCargoInclude end --- Handles the OnEventNewCargo event for the Set. -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA EventData - function SET_CARGO:OnEventNewCargo( EventData ) -- R2.1 + function SET_CARGO:OnEventNewCargo(EventData) -- R2.1 - --self:F( { "New Cargo", EventData } ) + --self:F({ "New Cargo", EventData }) if EventData.Cargo then - if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then - self:Add( EventData.Cargo.Name, EventData.Cargo ) + if EventData.Cargo and self:IsIncludeObject(EventData.Cargo) then + self:Add(EventData.Cargo.Name, EventData.Cargo) end end end @@ -6182,11 +6182,11 @@ do -- SET_CARGO --- Handles the OnDead or OnCrash event for alive units set. -- @param #SET_CARGO self -- @param Core.Event#EVENTDATA EventData - function SET_CARGO:OnEventDeleteCargo( EventData ) -- R2.1 - --self:F3( { EventData } ) + function SET_CARGO:OnEventDeleteCargo(EventData) -- R2.1 + --self:F3({ EventData }) if EventData.Cargo then - local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name ) + local Cargo = _DATABASE:FindCargo(EventData.Cargo.Name) if Cargo and Cargo.Name then -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. @@ -6195,10 +6195,10 @@ do -- SET_CARGO -- To prevent this from happening, the Cargo object has a flag NoDestroy. -- When true, the SET_CARGO won't Remove the Cargo object from the set. -- This flag is switched off after the event handlers have been called in the EVENT class. - --self:F( { CargoNoDestroy = Cargo.NoDestroy } ) + --self:F({ CargoNoDestroy = Cargo.NoDestroy }) if Cargo.NoDestroy then else - self:Remove( Cargo.Name ) + self:Remove(Cargo.Name) end end end @@ -6266,7 +6266,7 @@ do -- SET_ZONE -- DatabaseSet = SET_ZONE:New() function SET_ZONE:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES ) ) + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.ZONES)) return self end @@ -6275,12 +6275,12 @@ do -- SET_ZONE -- @param Core.Set#SET_ZONE self -- @param #string AddZoneNames A single name or an array of ZONE_BASE names. -- @return self - function SET_ZONE:AddZonesByName( AddZoneNames ) + function SET_ZONE:AddZonesByName(AddZoneNames) - local AddZoneNamesArray = (type( AddZoneNames ) == "table") and AddZoneNames or { AddZoneNames } + local AddZoneNamesArray = (type(AddZoneNames) == "table") and AddZoneNames or { AddZoneNames } - for AddAirbaseID, AddZoneName in pairs( AddZoneNamesArray ) do - self:Add( AddZoneName, ZONE:FindByName( AddZoneName ) ) + for AddAirbaseID, AddZoneName in pairs(AddZoneNamesArray) do + self:Add(AddZoneName, ZONE:FindByName(AddZoneName)) end return self @@ -6290,9 +6290,9 @@ do -- SET_ZONE -- @param Core.Set#SET_ZONE self -- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object. -- @return self - function SET_ZONE:AddZone( Zone ) + function SET_ZONE:AddZone(Zone) - self:Add( Zone:GetName(), Zone ) + self:Add(Zone:GetName(), Zone) return self end @@ -6301,12 +6301,12 @@ do -- SET_ZONE -- @param Core.Set#SET_ZONE self -- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names. -- @return self - function SET_ZONE:RemoveZonesByName( RemoveZoneNames ) + function SET_ZONE:RemoveZonesByName(RemoveZoneNames) - local RemoveZoneNamesArray = (type( RemoveZoneNames ) == "table") and RemoveZoneNames or { RemoveZoneNames } + local RemoveZoneNamesArray = (type(RemoveZoneNames) == "table") and RemoveZoneNames or { RemoveZoneNames } - for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do - self:Remove( RemoveZoneName ) + for RemoveZoneID, RemoveZoneName in pairs(RemoveZoneNamesArray) do + self:Remove(RemoveZoneName) end return self @@ -6316,7 +6316,7 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param #string ZoneName -- @return Core.Zone#ZONE_BASE The found Zone. - function SET_ZONE:FindZone( ZoneName ) + function SET_ZONE:FindZone(ZoneName) local ZoneFound = self.Set[ZoneName] return ZoneFound @@ -6327,7 +6327,7 @@ do -- SET_ZONE -- @param #number margin Number of tries to find a zone -- @return Core.Zone#ZONE_BASE The random Zone. -- @return #nil if no zone in the collection. - function SET_ZONE:GetRandomZone( margin ) + function SET_ZONE:GetRandomZone(margin) local margin = margin or 100 if self:Count() ~= 0 then @@ -6340,7 +6340,7 @@ do -- SET_ZONE -- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues! local counter = 0 while (not ZoneFound) or (counter < margin) do - local ZoneRandom = math.random( 1, #Index ) + local ZoneRandom = math.random(1, #Index) ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe() counter = counter + 1 end @@ -6354,9 +6354,9 @@ do -- SET_ZONE --- Set a zone probability. -- @param #SET_ZONE self -- @param #string ZoneName The name of the zone. - function SET_ZONE:SetZoneProbability( ZoneName, ZoneProbability ) - local Zone = self:FindZone( ZoneName ) - Zone:SetZoneProbability( ZoneProbability ) + function SET_ZONE:SetZoneProbability(ZoneName, ZoneProbability) + local Zone = self:FindZone(ZoneName) + Zone:SetZoneProbability(ZoneProbability) end --- Builds a set of ZONEs that contain the given string in their name. @@ -6364,14 +6364,14 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param #string Prefixes The string pattern(s) that need to be contained in the zone name. Can also be passed as a `#table` of strings. -- @return #SET_ZONE self - function SET_ZONE:FilterPrefixes( Prefixes ) + function SET_ZONE:FilterPrefixes(Prefixes) if not self.Filter.Prefixes then self.Filter.Prefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.Prefixes[Prefix] = Prefix end return self @@ -6385,17 +6385,17 @@ do -- SET_ZONE if _DATABASE then -- We initialize the first set. - for ObjectName, Object in pairs( self.Database ) do - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + for ObjectName, Object in pairs(self.Database) do + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) else - self:RemoveZonesByName( ObjectName ) + self:RemoveZonesByName(ObjectName) end end end - self:HandleEvent( EVENTS.NewZone ) - self:HandleEvent( EVENTS.DeleteZone ) + self:HandleEvent(EVENTS.NewZone) + self:HandleEvent(EVENTS.DeleteZone) return self end @@ -6405,8 +6405,8 @@ do -- SET_ZONE -- @return #SET_ZONE self function SET_ZONE:FilterStop() - self:UnHandleEvent( EVENTS.NewZone ) - self:UnHandleEvent( EVENTS.DeleteZone ) + self:UnHandleEvent(EVENTS.NewZone) + self:UnHandleEvent(EVENTS.DeleteZone) return self end @@ -6417,8 +6417,8 @@ do -- SET_ZONE -- @param Core.Event#EVENTDATA Event -- @return #string The name of the AIRBASE -- @return #table The AIRBASE - function SET_ZONE:AddInDatabase( Event ) - --self:F3( { Event } ) + function SET_ZONE:AddInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -6429,8 +6429,8 @@ do -- SET_ZONE -- @param Core.Event#EVENTDATA Event -- @return #string The name of the AIRBASE -- @return #table The AIRBASE - function SET_ZONE:FindInDatabase( Event ) - --self:F3( { Event } ) + function SET_ZONE:FindInDatabase(Event) + --self:F3({ Event }) return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end @@ -6439,10 +6439,10 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE. The function needs to accept a AIRBASE parameter. -- @return #SET_ZONE self - function SET_ZONE:ForEachZone( IteratorFunction, ... ) - --self:F2( arg ) + function SET_ZONE:ForEachZone(IteratorFunction, ...) + --self:F2(arg) - self:ForEach( IteratorFunction, arg, self:GetSet() ) + self:ForEach(IteratorFunction, arg, self:GetSet()) return self end @@ -6494,8 +6494,8 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param Core.Zone#ZONE_BASE MZone -- @return #SET_ZONE self - function SET_ZONE:IsIncludeObject( MZone ) - --self:F2( MZone ) + function SET_ZONE:IsIncludeObject(MZone) + --self:F2(MZone) local MZoneInclude = true @@ -6504,13 +6504,13 @@ do -- SET_ZONE if self.Filter.Prefixes then local MZonePrefix = false - for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do - --self:T(2( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } ) - if string.find( MZoneName, ZonePrefix, 1 ) then + for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do + --self:T(2({ "Prefix:", string.find(MZoneName, ZonePrefix, 1), ZonePrefix }) + if string.find(MZoneName, ZonePrefix, 1) then MZonePrefix = true end end - --self:T(( { "Evaluated Prefix", MZonePrefix } ) + --self:T(({ "Evaluated Prefix", MZonePrefix }) MZoneInclude = MZoneInclude and MZonePrefix end end @@ -6520,20 +6520,20 @@ do -- SET_ZONE MZoneInclude = MZoneInclude and MClientFunc end - --self:T(2( MZoneInclude ) + --self:T(2(MZoneInclude) return MZoneInclude end --- Handles the OnEventNewZone event for the Set. -- @param #SET_ZONE self -- @param Core.Event#EVENTDATA EventData - function SET_ZONE:OnEventNewZone( EventData ) -- R2.1 + function SET_ZONE:OnEventNewZone(EventData) -- R2.1 - --self:F( { "New Zone", EventData } ) + --self:F({ "New Zone", EventData }) if EventData.Zone then - if EventData.Zone and self:IsIncludeObject( EventData.Zone ) then - self:Add( EventData.Zone.ZoneName, EventData.Zone ) + if EventData.Zone and self:IsIncludeObject(EventData.Zone) then + self:Add(EventData.Zone.ZoneName, EventData.Zone) end end end @@ -6541,11 +6541,11 @@ do -- SET_ZONE --- Handles the OnDead or OnCrash event for alive units set. -- @param #SET_ZONE self -- @param Core.Event#EVENTDATA EventData - function SET_ZONE:OnEventDeleteZone( EventData ) -- R2.1 - --self:F3( { EventData } ) + function SET_ZONE:OnEventDeleteZone(EventData) -- R2.1 + --self:F3({ EventData }) if EventData.Zone then - local Zone = _DATABASE:FindZone( EventData.Zone.ZoneName ) + local Zone = _DATABASE:FindZone(EventData.Zone.ZoneName) if Zone and Zone.ZoneName then -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. @@ -6554,10 +6554,10 @@ do -- SET_ZONE -- To prevent this from happening, the Zone object has a flag NoDestroy. -- When true, the SET_ZONE won't Remove the Zone object from the set. -- This flag is switched off after the event handlers have been called in the EVENT class. - --self:F( { ZoneNoDestroy = Zone.NoDestroy } ) + --self:F({ ZoneNoDestroy = Zone.NoDestroy }) if Zone.NoDestroy then else - self:Remove( Zone.ZoneName ) + self:Remove(Zone.ZoneName) end end end @@ -6569,11 +6569,11 @@ do -- SET_ZONE -- @param #SET_ZONE self -- @param Core.Point#COORDINATE Coordinate The coordinate to be searched. -- @return Core.Zone#ZONE_BASE The zone (if any) that validates the coordinate location. - function SET_ZONE:IsCoordinateInZone( Coordinate ) + function SET_ZONE:IsCoordinateInZone(Coordinate) - for _, Zone in pairs( self:GetSet() ) do + for _, Zone in pairs(self:GetSet()) do local Zone = Zone -- Core.Zone#ZONE_BASE - if Zone:IsCoordinateInZone( Coordinate ) then + if Zone:IsCoordinateInZone(Coordinate) then return Zone end end @@ -6586,11 +6586,11 @@ do -- SET_ZONE -- @param Core.Point#COORDINATE Coordinate The reference coordinate from which the closest zone is determined. -- @return Core.Zone#ZONE_BASE The closest zone (if any). -- @return #number Distance to ref coordinate in meters. - function SET_ZONE:GetClosestZone( Coordinate ) + function SET_ZONE:GetClosestZone(Coordinate) local dmin=math.huge local zmin=nil - for _, Zone in pairs( self:GetSet() ) do + for _, Zone in pairs(self:GetSet()) do local Zone = Zone -- Core.Zone#ZONE_BASE local d=Zone:Get2DDistance(Coordinate) if d x2 ) and Coordinate.x or x2 - y1 = ( Coordinate.y < y1 ) and Coordinate.y or y1 - y2 = ( Coordinate.y > y2 ) and Coordinate.y or y2 - z1 = ( Coordinate.y < z1 ) and Coordinate.z or z1 - z2 = ( Coordinate.y > z2 ) and Coordinate.z or z2 + x1 = (Coordinate.x < x1) and Coordinate.x or x1 + x2 = (Coordinate.x > x2) and Coordinate.x or x2 + y1 = (Coordinate.y < y1) and Coordinate.y or y1 + y2 = (Coordinate.y > y2) and Coordinate.y or y2 + z1 = (Coordinate.y < z1) and Coordinate.z or z1 + z2 = (Coordinate.y > z2) and Coordinate.z or z2 end - Coordinate.x = ( x2 - x1 ) / 2 + x1 - Coordinate.y = ( y2 - y1 ) / 2 + y1 - Coordinate.z = ( z2 - z1 ) / 2 + z1 + Coordinate.x = (x2 - x1) / 2 + x1 + Coordinate.y = (y2 - y1) / 2 + y1 + Coordinate.z = (z2 - z1) / 2 + z1 - --self:F( { Coordinate = Coordinate } ) + --self:F({ Coordinate = Coordinate }) return Coordinate end @@ -8523,8 +8523,8 @@ do -- SET_SCENERY -- @param #SET_SCENERY self -- @param Wrapper.Scenery#SCENERY MScenery -- @return #SET_SCENERY self - function SET_SCENERY:IsIncludeObject( MScenery ) - --self:T(( MScenery.SceneryName ) + function SET_SCENERY:IsIncludeObject(MScenery) + --self:T((MScenery.SceneryName) local MSceneryInclude = true @@ -8534,25 +8534,25 @@ do -- SET_SCENERY -- Filter Prefixes if self.Filter.Prefixes then local MSceneryPrefix = false - for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do - --self:T(( { "Prefix:", string.find( MSceneryName, ZonePrefix, 1 ), ZonePrefix } ) - if string.find( MSceneryName, ZonePrefix, 1 ) then + for ZonePrefixId, ZonePrefix in pairs(self.Filter.Prefixes) do + --self:T(({ "Prefix:", string.find(MSceneryName, ZonePrefix, 1), ZonePrefix }) + if string.find(MSceneryName, ZonePrefix, 1) then MSceneryPrefix = true end end - --self:T(( { "Evaluated Prefix", MSceneryPrefix } ) + --self:T(({ "Evaluated Prefix", MSceneryPrefix }) MSceneryInclude = MSceneryInclude and MSceneryPrefix end if self.Filter.Zones then local MSceneryZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T(( "Zone:", ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T(("Zone:", ZoneName) local coord = MScenery:GetCoordinate() if coord and Zone:IsCoordinateInZone(coord) then MSceneryZone = true end - --self:T(( { "Evaluated Zone", MSceneryZone } ) + --self:T(({ "Evaluated Zone", MSceneryZone }) end MSceneryInclude = MSceneryInclude and MSceneryZone end @@ -8561,13 +8561,13 @@ do -- SET_SCENERY if self.Filter.SceneryRoles then local MSceneryRole = false local Role = MScenery:GetProperty("ROLE") or "none" - for ZoneRoleId, ZoneRole in pairs( self.Filter.SceneryRoles ) do - --self:T(( { "Role:", ZoneRole, Role } ) + for ZoneRoleId, ZoneRole in pairs(self.Filter.SceneryRoles) do + --self:T(({ "Role:", ZoneRole, Role }) if ZoneRole == Role then MSceneryRole = true end end - --self:T(( { "Evaluated Role ", MSceneryRole } ) + --self:T(({ "Evaluated Role ", MSceneryRole }) MSceneryInclude = MSceneryInclude and MSceneryRole end end @@ -8577,7 +8577,7 @@ do -- SET_SCENERY MSceneryInclude = MSceneryInclude and MClientFunc end - --self:T(2( MSceneryInclude ) + --self:T(2(MSceneryInclude) return MSceneryInclude end @@ -8586,10 +8586,10 @@ do -- SET_SCENERY -- @return #SET_SCENERY self function SET_SCENERY:FilterOnce() - for ObjectName, Object in pairs( self:GetSet() ) do + for ObjectName, Object in pairs(self:GetSet()) do --self:T((ObjectName) - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + if self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) else self:Remove(ObjectName, true) end @@ -8609,7 +8609,7 @@ do -- SET_SCENERY local Obj = obj -- Wrapper.Scenery#SCENERY life0 = life0 + Obj:GetLife0() end - ) + ) return life0 end @@ -8623,7 +8623,7 @@ do -- SET_SCENERY local Obj = obj -- Wrapper.Scenery#SCENERY life = life + Obj:GetLife() end - ) + ) return life end @@ -8759,7 +8759,7 @@ do -- SET_DYNAMICCARGO function SET_DYNAMICCARGO:New() --- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.DYNAMICCARGO ) ) -- Core.Set#SET_DYNAMICCARGO + local self = BASE:Inherit(self, SET_BASE:New(_DATABASE.DYNAMICCARGO)) -- Core.Set#SET_DYNAMICCARGO return self end @@ -8768,14 +8768,14 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param Wrapper.DynamicCargo#DYNAMICCARGO DCargo -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:IsIncludeObject( DCargo ) - --self:F2( DCargo ) + function SET_DYNAMICCARGO:IsIncludeObject(DCargo) + --self:F2(DCargo) local DCargoInclude = true if self.Filter.Coalitions then local DCargoCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - --self:T2( { "Coalition:", DCargo:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + for CoalitionID, CoalitionName in pairs(self.Filter.Coalitions) do + --self:T2({ "Coalition:", DCargo:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName }) if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == DCargo:GetCoalition() then DCargoCoalition = true end @@ -8785,8 +8785,8 @@ do -- SET_DYNAMICCARGO if self.Filter.Types then local DCargoType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - --self:T2( { "Type:", DCargo:GetTypeName(), TypeName } ) + for TypeID, TypeName in pairs(self.Filter.Types) do + --self:T2({ "Type:", DCargo:GetTypeName(), TypeName }) if TypeName == DCargo:GetTypeName() then DCargoType = true end @@ -8796,8 +8796,8 @@ do -- SET_DYNAMICCARGO if self.Filter.Countries then local DCargoCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - --self:T2( { "Country:", DCargo:GetCountry(), CountryName } ) + for CountryID, CountryName in pairs(self.Filter.Countries) do + --self:T2({ "Country:", DCargo:GetCountry(), CountryName }) if country.id[CountryName] == DCargo:GetCountry() then DCargoCountry = true end @@ -8807,9 +8807,9 @@ do -- SET_DYNAMICCARGO if self.Filter.StaticPrefixes then local DCargoPrefix = false - for StaticPrefixId, StaticPrefix in pairs( self.Filter.StaticPrefixes ) do - --self:T2( { "Prefix:", string.find( DCargo:GetName(), StaticPrefix, 1 ), StaticPrefix } ) - if string.find( DCargo:GetName(), StaticPrefix, 1 ) then + for StaticPrefixId, StaticPrefix in pairs(self.Filter.StaticPrefixes) do + --self:T2({ "Prefix:", string.find(DCargo:GetName(), StaticPrefix, 1), StaticPrefix }) + if string.find(DCargo:GetName(), StaticPrefix, 1) then DCargoPrefix = true end end @@ -8818,8 +8818,8 @@ do -- SET_DYNAMICCARGO if self.Filter.Zones then local DCargoZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:T2( "In zone: "..ZoneName ) + for ZoneName, Zone in pairs(self.Filter.Zones) do + --self:T2("In zone: "..ZoneName) if DCargo and DCargo:IsInZone(Zone) then DCargoZone = true end @@ -8832,7 +8832,7 @@ do -- SET_DYNAMICCARGO DCargoInclude = DCargoInclude and MClientFunc end - --self:T2( DCargoInclude ) + --self:T2(DCargoInclude) return DCargoInclude end @@ -8846,14 +8846,14 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param #string Types Can take those type name strings known within DCS world. -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterTypes( Types ) + function SET_DYNAMICCARGO:FilterTypes(Types) if not self.Filter.Types then self.Filter.Types = {} end - if type( Types ) ~= "table" then + if type(Types) ~= "table" then Types = { Types } end - for TypeID, Type in pairs( Types ) do + for TypeID, Type in pairs(Types) do self.Filter.Types[Type] = Type end return self @@ -8874,7 +8874,7 @@ do -- SET_DYNAMICCARGO -- if dynamiccargo:GetName() == "Exclude Me" then isinclude = false end -- return isinclude -- end - -- ):FilterOnce() + -- ):FilterOnce() -- BASE:I(cargoset:Flush()) --- Builds a set of dynamic cargo of defined countries. @@ -8882,14 +8882,14 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param #string Countries Can take those country strings known within DCS world. -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterCountries( Countries ) + function SET_DYNAMICCARGO:FilterCountries(Countries) if not self.Filter.Countries then self.Filter.Countries = {} end - if type( Countries ) ~= "table" then + if type(Countries) ~= "table" then Countries = { Countries } end - for CountryID, Country in pairs( Countries ) do + for CountryID, Country in pairs(Countries) do self.Filter.Countries[Country] = Country end return self @@ -8900,14 +8900,14 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param #string Prefixes The string pattern(s) that need to be contained in the dynamic cargo name. Can also be passed as a `#table` of strings. -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterPrefixes( Prefixes ) + function SET_DYNAMICCARGO:FilterPrefixes(Prefixes) if not self.Filter.StaticPrefixes then self.Filter.StaticPrefixes = {} end - if type( Prefixes ) ~= "table" then + if type(Prefixes) ~= "table" then Prefixes = { Prefixes } end - for PrefixID, Prefix in pairs( Prefixes ) do + for PrefixID, Prefix in pairs(Prefixes) do self.Filter.StaticPrefixes[Prefix] = Prefix end return self @@ -8918,7 +8918,7 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param #string Patterns The string pattern(s) that need to be contained in the dynamic cargo name. Can also be passed as a `#table` of strings. -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterNamePattern( Patterns ) + function SET_DYNAMICCARGO:FilterNamePattern(Patterns) return self:FilterPrefixes(Patterns) end @@ -8934,7 +8934,7 @@ do -- SET_DYNAMICCARGO return false end end - ) + ) return self end @@ -8950,7 +8950,7 @@ do -- SET_DYNAMICCARGO return false end end - ) + ) return self end @@ -8966,7 +8966,7 @@ do -- SET_DYNAMICCARGO return false end end - ) + ) return self end @@ -8983,7 +8983,7 @@ do -- SET_DYNAMICCARGO return false end end - ) + ) return self end @@ -8991,20 +8991,20 @@ do -- SET_DYNAMICCARGO -- @param #SET_DYNAMICCARGO self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE -- @return #SET_DYNAMICCARGO self - function SET_DYNAMICCARGO:FilterZones( Zones ) + function SET_DYNAMICCARGO:FilterZones(Zones) if not self.Filter.Zones then self.Filter.Zones = {} end local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then + elseif type(Zones) ~= "table" or (type(Zones) == "table" and Zones.ClassName) then self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") return self else zones = Zones end - for _,Zone in pairs( zones ) do + for _,Zone in pairs(zones) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end @@ -9016,8 +9016,8 @@ do -- SET_DYNAMICCARGO -- @return #SET_DYNAMICCARGO self function SET_DYNAMICCARGO:FilterStart() if _DATABASE then - self:HandleEvent( EVENTS.NewDynamicCargo, self._EventHandlerDCAdd ) - self:HandleEvent( EVENTS.DynamicCargoRemoved, self._EventHandlerDCRemove ) + self:HandleEvent(EVENTS.NewDynamicCargo, self._EventHandlerDCAdd) + self:HandleEvent(EVENTS.DynamicCargoRemoved, self._EventHandlerDCRemove) if self.Filter.Zones then self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self) local timing = self.ZoneTimerInterval or 30 @@ -9034,8 +9034,8 @@ do -- SET_DYNAMICCARGO -- @return #SET_DYNAMICCARGO self function SET_DYNAMICCARGO:FilterStop() if _DATABASE then - self:UnHandleEvent( EVENTS.NewDynamicCargo) - self:UnHandleEvent( EVENTS.DynamicCargoRemoved ) + self:UnHandleEvent(EVENTS.NewDynamicCargo) + self:UnHandleEvent(EVENTS.DynamicCargoRemoved) if self.ZoneTimer and self.ZoneTimer:IsRunning() then self.ZoneTimer:Stop() end @@ -9049,10 +9049,10 @@ do -- SET_DYNAMICCARGO -- @return #SET_DYNAMICCARGO self function SET_DYNAMICCARGO:_ContinousZoneFilter() local Database = _DATABASE.DYNAMICCARGO - for ObjectName, Object in pairs( Database ) do - if self:IsIncludeObject( Object ) and self:IsNotInSet(Object) then - self:Add( ObjectName, Object ) - elseif (not self:IsIncludeObject( Object )) and self:IsInSet(Object) then + for ObjectName, Object in pairs(Database) do + if self:IsIncludeObject(Object) and self:IsNotInSet(Object) then + self:Add(ObjectName, Object) + elseif (not self:IsIncludeObject(Object)) and self:IsInSet(Object) then self:Remove(ObjectName) end end @@ -9063,14 +9063,14 @@ do -- SET_DYNAMICCARGO --- Handles the events for the Set. -- @param #SET_DYNAMICCARGO self -- @param Core.Event#EVENTDATA Event - function SET_DYNAMICCARGO:_EventHandlerDCAdd( Event ) + function SET_DYNAMICCARGO:_EventHandlerDCAdd(Event) if Event.IniDynamicCargo and Event.IniDynamicCargoName then if not _DATABASE.DYNAMICCARGO[Event.IniDynamicCargoName] then - _DATABASE:AddDynamicCargo( Event.IniDynamicCargoName ) + _DATABASE:AddDynamicCargo(Event.IniDynamicCargoName) end - local ObjectName, Object = self:FindInDatabase( Event ) - if Object and self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + local ObjectName, Object = self:FindInDatabase(Event) + if Object and self:IsIncludeObject(Object) then + self:Add(ObjectName, Object) end end @@ -9080,11 +9080,11 @@ do -- SET_DYNAMICCARGO --- Handles the remove event for dynamic cargo set. -- @param #SET_DYNAMICCARGO self -- @param Core.Event#EVENTDATA Event - function SET_DYNAMICCARGO:_EventHandlerDCRemove( Event ) + function SET_DYNAMICCARGO:_EventHandlerDCRemove(Event) if Event.IniDCSUnitName then - local ObjectName, Object = self:FindInDatabase( Event ) + local ObjectName, Object = self:FindInDatabase(Event) if ObjectName then - self:Remove( ObjectName ) + self:Remove(ObjectName) end end @@ -9097,7 +9097,7 @@ do -- SET_DYNAMICCARGO -- @param Core.Event#EVENTDATA Event -- @return #string The name of the DYNAMICCARGO -- @return Wrapper.DynamicCargo#DYNAMICCARGO The DYNAMICCARGO object - function SET_DYNAMICCARGO:FindInDatabase( Event ) + function SET_DYNAMICCARGO:FindInDatabase(Event) return Event.IniDCSUnitName, self.Set[Event.IniDCSUnitName] end @@ -9136,7 +9136,7 @@ do -- SET_DYNAMICCARGO table.insert(owners, cargo.Owner, cargo.Owner) end end - ) + ) return owners end @@ -9151,7 +9151,7 @@ do -- SET_DYNAMICCARGO table.insert(owners, cargo.StaticName, cargo.warehouse) end end - ) + ) return owners end @@ -9169,7 +9169,7 @@ do -- SET_DYNAMICCARGO end end end - ) + ) return owners end From bedd1056491926061612e6e7a06b8331bf0c50ea Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 9 Nov 2025 18:22:39 +0100 Subject: [PATCH 30/31] Update Auftrag.lua - added NAVALENGAGEMENT to :NewFromTarget --- Moose Development/Moose/Ops/Auftrag.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 6e58b56d3..cdad223f1 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -2774,6 +2774,8 @@ function AUFTRAG:NewFromTarget(Target, MissionType) mission=self:NewARMORATTACK(Target) elseif MissionType==AUFTRAG.Type.GROUNDATTACK then mission=self:NewGROUNDATTACK(Target) + elseif MissionType==AUFTRAG.Type.NAVALENGAGEMENT then + mission=self:NewNAVALENGAGEMENT(Target) else return nil end From 65ddff13f7f77452011d00bd787eedd90e338188 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 9 Nov 2025 21:46:23 +0100 Subject: [PATCH 31/31] OPS Ammo - Added guns/cannons differentiation to ammo count --- Moose Development/Moose/Ops/ArmyGroup.lua | 4 +-- Moose Development/Moose/Ops/NavyGroup.lua | 2 +- Moose Development/Moose/Ops/OpsGroup.lua | 42 +++++++++++++++++------ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 608a1c004..375cbe145 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -839,8 +839,8 @@ function ARMYGROUP:Status() local ammo=self:GetAmmoElement(element) -- Output text for element. - text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, rockets=%d, bombs=%d, missiles=%d, cargo=%d/%d kg", - i, name, status, life, life0, ammo.Guns, ammo.Rockets, ammo.Bombs, ammo.Missiles, element.weightCargo, element.weightMaxCargo) + text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, cannons=%d, rockets=%d, missiles=%d, cargo=%d/%d kg", + i, name, status, life, life0, ammo.Guns, ammo.Cannons, ammo.Rockets, ammo.Missiles, element.weightCargo, element.weightMaxCargo) end if #self.elements==0 then text=text.." none!" diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index 24860af03..5f2869d03 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -1083,7 +1083,7 @@ function NAVYGROUP:onafterSpawned(From, Event, To) text=text..string.format("Elements = %d\n", #self.elements) text=text..string.format("Waypoints = %d\n", #self.waypoints) text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On)) - text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos) + text=text..string.format("Ammo = %d (G=%d/C=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Cannons, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos) text=text..string.format("FSM state = %s\n", self:GetState()) text=text..string.format("Is alive = %s\n", tostring(self:IsAlive())) text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated())) diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 168359dd2..08539aa5c 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -405,7 +405,9 @@ OPSGROUP.TaskType={ --- Ammo data. -- @type OPSGROUP.Ammo -- @field #number Total Total amount of ammo. --- @field #number Guns Amount of gun shells. +-- @field #number Shells Amount of shells (guns + cannons). +-- @field #number Guns Amount of gun shells (caliber < 25). +-- @field #number Cannons Amount of cannon shells (caliber >= 25). -- @field #number Bombs Amount of bombs. -- @field #number Rockets Amount of rockets. -- @field #number Torpedos Amount of torpedos. @@ -4758,7 +4760,7 @@ function OPSGROUP:_UpdateTask(Task, Mission) elseif weaponType==ENUMS.WeaponFlag.AnyRocket then nAmmo=ammo.Rockets elseif weaponType==ENUMS.WeaponFlag.Cannons then - nAmmo=ammo.Guns + nAmmo=ammo.Cannons end --TODO: Update target location while we're at it anyway. @@ -11210,11 +11212,11 @@ function OPSGROUP:_CheckAmmoStatus() self:OutOfAmmo() end - -- Guns. - if self.outofGuns and ammo.Guns>0 then + -- Guns (changed to shells) + if self.outofGuns and ammo.Shells>0 then self.outofGuns=false end - if ammo.Guns==0 and self.ammo.Guns>0 and not self.outofGuns then + if ammo.Shells==0 and self.ammo.Shells>0 and not self.outofGuns then self.outofGuns=true self:OutOfGuns() end @@ -13390,7 +13392,9 @@ function OPSGROUP:GetAmmoTot() local Ammo={} --#OPSGROUP.Ammo Ammo.Total=0 + Ammo.Shells=0 Ammo.Guns=0 + Ammo.Cannons=0 Ammo.Rockets=0 Ammo.Bombs=0 Ammo.Torpedos=0 @@ -13411,7 +13415,9 @@ function OPSGROUP:GetAmmoTot() -- Add up total. Ammo.Total=Ammo.Total+ammo.Total + Ammo.Shells=Ammo.Shells+ammo.Shells Ammo.Guns=Ammo.Guns+ammo.Guns + Ammo.Cannons=Ammo.Cannons+ammo.Cannons Ammo.Rockets=Ammo.Rockets+ammo.Rockets Ammo.Bombs=Ammo.Bombs+ammo.Bombs Ammo.Torpedos=Ammo.Torpedos+ammo.Torpedos @@ -13444,6 +13450,8 @@ function OPSGROUP:GetAmmoUnit(unit, display) -- Init counter. local nammo=0 local nshells=0 + local nguns=0 + local ncannons=0 local nrockets=0 local nmissiles=0 local nmissilesAA=0 @@ -13468,8 +13476,8 @@ function OPSGROUP:GetAmmoUnit(unit, display) if ammotable then local weapons=#ammotable - --self:I(ammotable) - --UTILS.PrintTableToLog(ammotable) + --self:I(ammotable) + --UTILS.PrintTableToLog(ammotable) -- Loop over all weapons. for w=1,weapons do @@ -13477,9 +13485,9 @@ function OPSGROUP:GetAmmoUnit(unit, display) -- Number of current weapon. local Nammo=ammotable[w]["count"] - -- Range in meters. Seems only to exist for missiles (not shells). - local rmin=ammotable[w]["desc"]["rangeMin"] or 0 - local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0 + -- Range in meters. Seems only to exist for missiles (not shells). + local rmin=ammotable[w]["desc"]["rangeMin"] or 0 + local rmax=ammotable[w]["desc"]["rangeMaxAltMin"] or 0 -- Type name of current weapon. local Tammo=ammotable[w]["desc"]["typeName"] @@ -13501,6 +13509,16 @@ function OPSGROUP:GetAmmoUnit(unit, display) -- Add up all shells. nshells=nshells+Nammo + + -- Add small and large caliber shells for guns and cannons + if ammotable[w]["desc"]["warhead"] and ammotable[w]["desc"]["warhead"]["caliber"] then + local caliber=ammotable[w]["desc"]["warhead"]["caliber"] + if caliber<25 then + nguns=nguns+Nammo + else + ncannons=ncannons+Nammo + end + end -- Debug info. text=text..string.format("- %d shells of type %s, range=%d - %d meters\n", Nammo, _weaponName, rmin, rmax) @@ -13579,7 +13597,9 @@ function OPSGROUP:GetAmmoUnit(unit, display) local ammo={} --#OPSGROUP.Ammo ammo.Total=nammo - ammo.Guns=nshells + ammo.Shells=nshells + ammo.Guns=nguns + ammo.Cannons=ncannons ammo.Rockets=nrockets ammo.Bombs=nbombs ammo.Torpedos=ntorps