diff --git a/Moose Development/Documentation/FAC_Detection logic.pdf b/Moose Development/Documentation/FAC_Detection logic.pdf new file mode 100644 index 000000000..e2e1cac38 Binary files /dev/null and b/Moose Development/Documentation/FAC_Detection logic.pdf differ diff --git a/Moose Development/Moose/AIBalancer.lua b/Moose Development/Moose/AIBalancer.lua index 2c4fbbb2a..8b9e32c20 100644 --- a/Moose Development/Moose/AIBalancer.lua +++ b/Moose Development/Moose/AIBalancer.lua @@ -153,7 +153,7 @@ function AIBALANCER:_ClientAliveMonitorScheduler() -- If there is no CLIENT within the self.ReturnTresholdRange, then the unit will return to the Airbase return method selected. local PlayerInRange = { Value = false } - local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetPointVec2(), self.ReturnTresholdRange ) + local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnTresholdRange ) self:E( RangeZone ) @@ -183,7 +183,7 @@ function AIBALANCER:_ClientAliveMonitorScheduler() else -- Okay, we need to send this Group back to the nearest base of the Coalition of the AI. --TODO: i need to rework the POINT_VEC2 thing. - local PointVec2 = POINT_VEC2:New( AIGroup:GetPointVec2().x, AIGroup:GetPointVec2().y ) + local PointVec2 = POINT_VEC2:New( AIGroup:GetVec2().x, AIGroup:GetVec2().y ) local ClosestAirbase = self.ReturnAirbaseSet:FindNearestAirbaseFromPointVec2( PointVec2 ) self:T( ClosestAirbase.AirbaseName ) AIGroup:MessageToRed( "Returning to " .. ClosestAirbase:GetName().. " ...", 30 ) diff --git a/Moose Development/Moose/Controllable.lua b/Moose Development/Moose/Controllable.lua index c1c899bd5..5cf7b8c1d 100644 --- a/Moose Development/Moose/Controllable.lua +++ b/Moose Development/Moose/Controllable.lua @@ -588,7 +588,7 @@ function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed ) local DCSControllable = self:GetDCSObject() if DCSControllable then - local ControllablePoint = self:GetPointVec2() + local ControllablePoint = self:GetVec2() return self:TaskOrbitCircleAtVec2( ControllablePoint, Altitude, Speed ) end @@ -765,7 +765,7 @@ function CONTROLLABLE:TaskLandAtZone( Zone, Duration, RandomPoint ) if RandomPoint then Point = Zone:GetRandomVec2() else - Point = Zone:GetPointVec2() + Point = Zone:GetVec2() end local DCSTask = self:TaskLandAtVec2( Point, Duration ) @@ -1365,7 +1365,7 @@ end function CONTROLLABLE:TaskRouteToVec2( Point, Speed ) self:F2( { Point, Speed } ) - local ControllablePoint = self:GetUnit( 1 ):GetPointVec2() + local ControllablePoint = self:GetUnit( 1 ):GetVec2() local PointFrom = {} PointFrom.x = ControllablePoint.x @@ -1504,7 +1504,7 @@ function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) if DCSControllable then - local ControllablePoint = self:GetPointVec2() + local ControllablePoint = self:GetVec2() local PointFrom = {} PointFrom.x = ControllablePoint.x @@ -1520,7 +1520,7 @@ function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) if Randomize then ZonePoint = Zone:GetRandomVec2() else - ZonePoint = Zone:GetPointVec2() + ZonePoint = Zone:GetVec2() end PointTo.x = ZonePoint.x @@ -1602,7 +1602,7 @@ function CONTROLLABLE:RouteReturnToAirbase( ReturnAirbase, Speed ) if DCSControllable then - local ControllablePoint = self:GetPointVec2() + local ControllablePoint = self:GetVec2() local ControllableVelocity = self:GetMaxVelocity() local PointFrom = {} @@ -1614,7 +1614,7 @@ function CONTROLLABLE:RouteReturnToAirbase( ReturnAirbase, Speed ) local PointTo = {} - local AirbasePoint = ReturnAirbase:GetPointVec2() + local AirbasePoint = ReturnAirbase:GetVec2() PointTo.x = AirbasePoint.x PointTo.y = AirbasePoint.y diff --git a/Moose Development/Moose/Escort.lua b/Moose Development/Moose/Escort.lua index e03c871c9..4143efd82 100644 --- a/Moose Development/Moose/Escort.lua +++ b/Moose Development/Moose/Escort.lua @@ -674,7 +674,7 @@ function ESCORT._HoldPosition( MenuParam ) PointFrom.alt = GroupPoint.y PointFrom.alt_type = AI.Task.AltitudeType.BARO - local OrbitPoint = OrbitUnit:GetPointVec2() + local OrbitPoint = OrbitUnit:GetVec2() local PointTo = {} PointTo.x = OrbitPoint.x PointTo.y = OrbitPoint.y @@ -867,7 +867,7 @@ function ESCORT._AttackTarget( MenuParam ) SCHEDULER:New( EscortGroup, EscortGroup.PushTask, { EscortGroup:TaskCombo( - { EscortGroup:TaskFireAtPoint( AttackUnit:GetPointVec2(), 50 ) + { EscortGroup:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) } ) }, 10 @@ -907,7 +907,7 @@ function ESCORT._AssistTarget( MenuParam ) SCHEDULER:New( EscortGroupAttack, EscortGroupAttack.PushTask, { EscortGroupAttack:TaskCombo( - { EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetPointVec2(), 50 ) + { EscortGroupAttack:TaskFireAtPoint( AttackUnit:GetVec2(), 50 ) } ) }, 10 diff --git a/Moose Development/Moose/Group.lua b/Moose Development/Moose/Group.lua index 5792cad84..91ddb61b2 100644 --- a/Moose Development/Moose/Group.lua +++ b/Moose Development/Moose/Group.lua @@ -475,12 +475,12 @@ end --- Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group. -- @param #GROUP self -- @return DCSTypes#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group. -function GROUP:GetPointVec2() +function GROUP:GetVec2() self:F2( self.GroupName ) local UnitPoint = self:GetUnit(1) - UnitPoint:GetPointVec2() - local GroupPointVec2 = UnitPoint:GetPointVec2() + UnitPoint:GetVec2() + local GroupPointVec2 = UnitPoint:GetVec2() self:T3( GroupPointVec2 ) return GroupPointVec2 end diff --git a/Moose Development/Moose/PatrolZone.lua b/Moose Development/Moose/PatrolZone.lua index f4bc61b6f..ffa4a3a6d 100644 --- a/Moose Development/Moose/PatrolZone.lua +++ b/Moose Development/Moose/PatrolZone.lua @@ -138,7 +138,7 @@ function PATROLZONE:NewPatrolRoute() -- If not, make a waypoint within the to that the PatrolGroup will fly at maximum speed to that point. -- --- Calculate the current route point. --- local CurrentVec2 = self.PatrolGroup:GetPointVec2() +-- local CurrentVec2 = self.PatrolGroup:GetVec2() -- local CurrentAltitude = self.PatrolGroup:GetUnit(1):GetAltitude() -- local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y ) -- local CurrentRoutePoint = CurrentPointVec3:RoutePointAir( diff --git a/Moose Development/Moose/Point.lua b/Moose Development/Moose/Point.lua index 7fdd395a7..638de5178 100644 --- a/Moose Development/Moose/Point.lua +++ b/Moose Development/Moose/Point.lua @@ -4,6 +4,9 @@ -- =============================================== -- The @{Point#POINT_VEC3} class defines a 3D point in the simulator. -- +-- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. +-- In order to keep the credibility of the the author, I want to emphasize that the of the MIST framework was created by Grimes, who you can find on the Eagle Dynamics Forums. +-- -- 1.1) POINT_VEC3 constructor -- --------------------------- -- @@ -28,6 +31,7 @@ --- The POINT_VEC3 class -- @type POINT_VEC3 -- @extends Base#BASE +-- @field DCSTypes#Vec3 PointVec3 -- @field #POINT_VEC3.SmokeColor SmokeColor -- @field #POINT_VEC3.FlareColor FlareColor -- @field #POINT_VEC3.RoutePointAltType RoutePointAltType @@ -48,6 +52,7 @@ POINT_VEC3 = { White = trigger.flareColor.White, Yellow = trigger.flareColor.Yellow }, + Metric = true, RoutePointAltType = { BARO = "BARO", }, @@ -114,6 +119,148 @@ function POINT_VEC3:New( x, y, z ) end +--- Return the coordinates of the POINT_VEC3 in Vec3 format. +-- @param #POINT_VEC3 self +-- @return DCSTypes#Vec3 The Vec3 coodinate. +function POINT_VEC3:GetVec3() + return self.PointVec3 +end + + +--- Return the x coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @return #number The x coodinate. +function POINT_VEC3:GetX() + return self.PointVec3.x +end + +--- Return the y coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @return #number The y coodinate. +function POINT_VEC3:GetY() + return self.PointVec3.y +end + +--- Return the z coordinate of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @return #number The z coodinate. +function POINT_VEC3:GetZ() + return self.PointVec3.z +end + + +--- Return a direction vector Vec3 from POINT_VEC3 to the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #POINT_VEC3 TargetPointVec3 The target PointVec3. +-- @return DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. +function POINT_VEC3:GetDirectionVec3( TargetPointVec3 ) + return { x = TargetPointVec3:GetX() - self:GetX(), y = TargetPointVec3:GetY() - self:GetY(), z = TargetPointVec3:GetZ() - self:GetZ() } +end + +--- Get a correction in radians of the real magnetic north of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @return #number CorrectionRadians The correction in radians. +function POINT_VEC3:GetNorthCorrectionRadians() + local TargetVec3 = self:GetVec3() + local lat, lon = coord.LOtoLL(TargetVec3) + local north_posit = coord.LLtoLO(lat + 1, lon) + return math.atan2( north_posit.z - TargetVec3.z, north_posit.x - TargetVec3.x ) +end + + +--- Return a direction in radians from the POINT_VEC3 using a direction vector in Vec3 format. +-- @param #POINT_VEC3 self +-- @param DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. +-- @return #number DirectionRadians The direction in radians. +function POINT_VEC3:GetDirectionRadians( DirectionVec3 ) + local DirectionRadians = math.atan2( DirectionVec3.z, DirectionVec3.x ) + DirectionRadians = DirectionRadians + self:GetNorthCorrectionRadians() + if DirectionRadians < 0 then + DirectionRadians = DirectionRadians + 2 * math.pi -- put dir in range of 0 to 2*pi ( the full circle ) + end + return DirectionRadians +end + +--- Return the 2D distance in meters between the target POINT_VEC3 and the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #POINT_VEC3 TargetPointVec3 The target PointVec3. +-- @return DCSTypes#Distance Distance The distance in meters. +function POINT_VEC3:Get2DDistance( TargetPointVec3 ) + local TargetVec3 = TargetPointVec3:GetVec3() + local SourceVec3 = self:GetVec3() + return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 +end + +--- Return the 3D distance in meters between the target POINT_VEC3 and the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #POINT_VEC3 TargetPointVec3 The target PointVec3. +-- @return DCSTypes#Distance Distance The distance in meters. +function POINT_VEC3:Get3DDistance( TargetPointVec3 ) + local TargetVec3 = TargetPointVec3:GetVec3() + local SourceVec3 = self:GetVec3() + return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 +end + +--- Provides a Bearing / Range string +-- @param #POINT_VEC3 self +-- @param #number AngleRadians The angle in randians +-- @param #number Distance The distance +-- @return #string The BR Text +function POINT_VEC3:ToStringBR( AngleRadians, Distance ) + + AngleRadians = UTILS.Round( UTILS.ToDegree( AngleRadians ), 0 ) + if self:IsMetric() then + Distance = UTILS.Round( Distance / 1000, 2 ) + else + Distance = UTILS.Round( UTILS.MetersToNM( Distance ), 2 ) + end + + local s = string.format( '%03d', AngleRadians ) .. ' for ' .. Distance + + s = s .. self:GetAltitudeText() -- When the POINT is a VEC2, there will be no altitude shown. + + return s +end + +--- Return the altitude text of the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @return #string Altitude text. +function POINT_VEC3:GetAltitudeText() + if self:IsMetric() then + return ' at ' .. UTILS.Round( self:GetY(), 0 ) + else + return ' at ' .. UTILS.Round( UTILS.MetersToFeet( self:GetY() ), 0 ) + end +end + +--- Return a BR string from a POINT_VEC3 to the POINT_VEC3. +-- @param #POINT_VEC3 self +-- @param #POINT_VEC3 TargetPointVec3 The target PointVec3. +-- @return #string The BR text. +function POINT_VEC3:GetBRText( TargetPointVec3 ) + local DirectionVec3 = self:GetDirectionVec3( TargetPointVec3 ) + local AngleRadians = self:GetDirectionRadians( DirectionVec3 ) + local Distance = self:Get2DDistance( TargetPointVec3 ) + return self:ToStringBR( AngleRadians, Distance ) +end + +--- Sets the POINT_VEC3 metric or NM. +-- @param #POINT_VEC3 self +-- @param #boolean Metric true means metric, false means NM. +function POINT_VEC3:SetMetric( Metric ) + self.Metric = Metric +end + +--- Gets if the POINT_VEC3 is metric or NM. +-- @param #POINT_VEC3 self +-- @return #boolean Metric true means metric, false means NM. +function POINT_VEC3:IsMetric() + return self.Metric +end + + + + --- Build an air type route point. -- @param #POINT_VEC3 self -- @param #POINT_VEC3.RoutePointAltType AltType The altitude type. @@ -299,3 +446,10 @@ function POINT_VEC2:DistanceFromVec2( Vec2Reference ) end +--- Return no text for the altitude of the POINT_VEC2. +-- @param #POINT_VEC2 self +-- @return #string Empty string. +function POINT_VEC2:GetAltitudeText() + return '' +end + diff --git a/Moose Development/Moose/Positionable.lua b/Moose Development/Moose/Positionable.lua index 82c824957..be6737e5f 100644 --- a/Moose Development/Moose/Positionable.lua +++ b/Moose Development/Moose/Positionable.lua @@ -71,7 +71,7 @@ end -- @param Positionable#POSITIONABLE self -- @return DCSTypes#Vec2 The 2D point vector of the DCS Positionable. -- @return #nil The DCS Positionable is not existing or alive. -function POSITIONABLE:GetPointVec2() +function POSITIONABLE:GetVec2() self:F2( self.PositionableName ) local DCSPositionable = self:GetDCSObject() @@ -137,7 +137,7 @@ function POSITIONABLE:IsAboveRunway() if DCSPositionable then - local PointVec2 = self:GetPointVec2() + local PointVec2 = self:GetVec2() local SurfaceType = land.getSurfaceType( PointVec2 ) local IsAboveRunway = SurfaceType == land.SurfaceType.RUNWAY diff --git a/Moose Development/Moose/Routines.lua b/Moose Development/Moose/Routines.lua index f5f42d146..3743752b3 100644 --- a/Moose Development/Moose/Routines.lua +++ b/Moose Development/Moose/Routines.lua @@ -245,22 +245,6 @@ end --- From http://lua-users.org/wiki/SimpleRound --- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place -routines.utils.round = function(num, idp) - local mult = 10^(idp or 0) - return math.floor(num * mult + 0.5) / mult -end - --- porting in Slmod's dostring -routines.utils.dostring = function(s) - local f, err = loadstring(s) - if f then - return true, f() - else - return false, err - end -end --3D Vector manipulation diff --git a/Moose Development/Moose/Set.lua b/Moose Development/Moose/Set.lua index bd8a9c97d..d9e8fb7ba 100644 --- a/Moose Development/Moose/Set.lua +++ b/Moose Development/Moose/Set.lua @@ -399,9 +399,9 @@ function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ) for ObjectID, ObjectData in pairs( self.Set ) do if NearestObject == nil then NearestObject = ObjectData - ClosestDistance = PointVec2:DistanceFromVec2( ObjectData:GetPointVec2() ) + ClosestDistance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) else - local Distance = PointVec2:DistanceFromVec2( ObjectData:GetPointVec2() ) + local Distance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) if Distance < ClosestDistance then NearestObject = ObjectData ClosestDistance = Distance diff --git a/Moose Development/Moose/Spawn.lua b/Moose Development/Moose/Spawn.lua index 8fc80052f..046d824a5 100644 --- a/Moose Development/Moose/Spawn.lua +++ b/Moose Development/Moose/Spawn.lua @@ -563,7 +563,7 @@ function SPAWN:SpawnFromUnit( HostUnit, OuterRadius, InnerRadius, SpawnIndex ) if SpawnTemplate then - local UnitPoint = HostUnit:GetPointVec2() + local UnitPoint = HostUnit:GetVec2() self:T( { "Current point of ", self.SpawnTemplatePrefix, UnitPoint } ) @@ -646,7 +646,7 @@ function SPAWN:SpawnInZone( Zone, ZoneRandomize, SpawnIndex ) if ZoneRandomize == true then ZonePoint = Zone:GetRandomVec2() else - ZonePoint = Zone:GetPointVec2() + ZonePoint = Zone:GetVec2() end SpawnTemplate.route.points[1].x = ZonePoint.x diff --git a/Moose Development/Moose/TaskClientRoute.lua b/Moose Development/Moose/TaskClientRoute.lua new file mode 100644 index 000000000..7f05cc53b --- /dev/null +++ b/Moose Development/Moose/TaskClientRoute.lua @@ -0,0 +1,83 @@ +--- @module Task_Route + +--- TASK2_ROUTE_CLIENT class +-- @type TASK2_ROUTE_CLIENT +-- @field Mission#MISSION Mission +-- @field Client#CLIENT Client +-- @field Zone#ZONE_BASE TargetZone +-- @extends Task2#TASK2 +TASK2_ROUTE_CLIENT = { + ClassName = "TASK2_ROUTE_CLIENT", +} + + +--- Creates a new routing state machine. The task will route a CLIENT to a ZONE until the CLIENT is within that ZONE. +-- @param #TASK2_ROUTE_CLIENT self +-- @param Mission#MISSION Mission +-- @param Client#CLIENT Client +-- @return #TASK2_ROUTE_CLIENT self +function TASK2_ROUTE_CLIENT:New( Mission, Client, TargetZone ) + + -- Inherits from BASE + local self = BASE:Inherit( self, TASK2:New( Mission, Client ) ) -- #TASK2_ROUTE_CLIENT + + self.TargetZone = TargetZone + self.DisplayInterval = 30 + self.DisplayCount = 1 + self.DisplayMessage = true + self.DisplayTime = 10 -- 10 seconds is the default + self.DisplayCategory = "Route" -- Route is the default display category + + self.Fsm = STATEMACHINE_TASK:New( self, { + initial = 'Unarrived', + events = { + { name = 'Route', from = 'UnArrived', to = 'Arrived' }, + { name = 'Fail', from = 'UnArrived', to = 'Failed' }, + }, + callbacks = { + onleaveUnarrived = self.OnBeforeRoute, + onFail = self.OnFail, + }, + endstates = { + 'Arrived', 'Failed' + }, + } ) + + return self +end + +--- Task Events + +--- StateMachine callback function for a TASK2 +-- @param #TASK2_ROUTE_CLIENT self +-- @param StateMachine#STATEMACHINE_TASK Fsm +-- @param #string Event +-- @param #string From +-- @param #string To +function TASK2_ROUTE_CLIENT:OnBeforeRoute( Fsm, Event, From, To ) + self:E( { Event, From, To, self.Client.ClientName } ) + + local IsInZone = self.Client:IsInZone( self.TargetZone ) + + if self.DisplayCount >= self.DisplayInterval then + if not IsInZone then + local ZoneVec2 = self.TargetZone:GetVec2() + local ZonePointVec2 = POINT_VEC2:New( ZoneVec2.x, ZoneVec2.y ) + local ClientVec2 = self.Client:GetVec2() + local ClientPointVec2 = POINT_VEC2:New( ClientVec2.x, ClientVec2.y ) + local RouteText = ClientPointVec2:GetBRText( ZonePointVec2 ) + self.Client:Message( RouteText, self.DisplayTime, self.DisplayCategory ) + end + self.DisplayCount = 1 + else + self.DisplayCount = self.DisplayCount + 1 + end + + if not IsInZone then + self:NextEvent( Fsm.Route ) + end + + return IsInZone -- if false, then the event will not be executed... + +end + diff --git a/Moose Development/Moose/Utils.lua b/Moose Development/Moose/Utils.lua new file mode 100644 index 000000000..49fa615a0 --- /dev/null +++ b/Moose Development/Moose/Utils.lua @@ -0,0 +1,176 @@ + +--- Utilities static class. +-- @type UTILS +UTILS = {} + + +--from http://lua-users.org/wiki/CopyTable +UTILS.DeepCopy = function(object) + local lookup_table = {} + local function _copy(object) + if type(object) ~= "table" then + return object + elseif lookup_table[object] then + return lookup_table[object] + end + local new_table = {} + lookup_table[object] = new_table + for index, value in pairs(object) do + new_table[_copy(index)] = _copy(value) + end + return setmetatable(new_table, getmetatable(object)) + end + local objectreturn = _copy(object) + return objectreturn +end + + +-- porting in Slmod's serialize_slmod2 +UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function + + lookup_table = {} + + local function _Serialize( tbl ) + + if type(tbl) == 'table' then --function only works for tables! + + if lookup_table[tbl] then + return lookup_table[object] + end + + local tbl_str = {} + + lookup_table[tbl] = tbl_str + + tbl_str[#tbl_str + 1] = '{' + + for ind,val in pairs(tbl) do -- serialize its fields + local ind_str = {} + if type(ind) == "number" then + ind_str[#ind_str + 1] = '[' + ind_str[#ind_str + 1] = tostring(ind) + ind_str[#ind_str + 1] = ']=' + else --must be a string + ind_str[#ind_str + 1] = '[' + ind_str[#ind_str + 1] = routines.utils.basicSerialize(ind) + ind_str[#ind_str + 1] = ']=' + end + + local val_str = {} + if ((type(val) == 'number') or (type(val) == 'boolean')) then + val_str[#val_str + 1] = tostring(val) + val_str[#val_str + 1] = ',' + tbl_str[#tbl_str + 1] = table.concat(ind_str) + tbl_str[#tbl_str + 1] = table.concat(val_str) + elseif type(val) == 'string' then + val_str[#val_str + 1] = routines.utils.basicSerialize(val) + val_str[#val_str + 1] = ',' + tbl_str[#tbl_str + 1] = table.concat(ind_str) + tbl_str[#tbl_str + 1] = table.concat(val_str) + elseif type(val) == 'nil' then -- won't ever happen, right? + val_str[#val_str + 1] = 'nil,' + tbl_str[#tbl_str + 1] = table.concat(ind_str) + tbl_str[#tbl_str + 1] = table.concat(val_str) + elseif type(val) == 'table' then + if ind == "__index" then + -- tbl_str[#tbl_str + 1] = "__index" + -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + else + + val_str[#val_str + 1] = _Serialize(val) + val_str[#val_str + 1] = ',' --I think this is right, I just added it + tbl_str[#tbl_str + 1] = table.concat(ind_str) + tbl_str[#tbl_str + 1] = table.concat(val_str) + end + elseif type(val) == 'function' then + -- tbl_str[#tbl_str + 1] = "function " .. tostring(ind) + -- tbl_str[#tbl_str + 1] = ',' --I think this is right, I just added it + else +-- env.info('unable to serialize value type ' .. routines.utils.basicSerialize(type(val)) .. ' at index ' .. tostring(ind)) +-- env.info( debug.traceback() ) + end + + end + tbl_str[#tbl_str + 1] = '}' + return table.concat(tbl_str) + else + return tostring(tbl) + end + end + + local objectreturn = _Serialize(tbl) + return objectreturn +end + +--porting in Slmod's "safestring" basic serialize +UTILS.BasicSerialize = function(s) + if s == nil then + return "\"\"" + else + if ((type(s) == 'number') or (type(s) == 'boolean') or (type(s) == 'function') or (type(s) == 'table') or (type(s) == 'userdata') ) then + return tostring(s) + elseif type(s) == 'string' then + s = string.format('%q', s) + return s + end + end +end + + +UTILS.ToDegree = function(angle) + return angle*180/math.pi +end + +UTILS.ToRadian = function(angle) + return angle*math.pi/180 +end + +UTILS.MetersToNM = function(meters) + return meters/1852 +end + +UTILS.MetersToFeet = function(meters) + return meters/0.3048 +end + +UTILS.NMToMeters = function(NM) + return NM*1852 +end + +UTILS.FeetToMeters = function(feet) + return feet*0.3048 +end + +UTILS.MpsToKnots = function(mps) + return mps*3600/1852 +end + +UTILS.MpsToKmph = function(mps) + return mps*3.6 +end + +UTILS.KnotsToMps = function(knots) + return knots*1852/3600 +end + +UTILS.KmphToMps = function(kmph) + return kmph/3.6 +end + + +--- From http://lua-users.org/wiki/SimpleRound +-- use negative idp for rounding ahead of decimal place, positive for rounding after decimal place +function UTILS.Round( num, idp ) + local mult = 10 ^ ( idp or 0 ) + return math.floor( num * mult + 0.5 ) / mult +end + +-- porting in Slmod's dostring +function UTILS.DoString( s ) + local f, err = loadstring( s ) + if f then + return true, f() + else + return false, err + end +end diff --git a/Moose Development/Moose/Zone.lua b/Moose Development/Moose/Zone.lua index 9bb906a90..786259d4f 100644 --- a/Moose Development/Moose/Zone.lua +++ b/Moose Development/Moose/Zone.lua @@ -101,10 +101,10 @@ end --- Returns if a location is within the zone. -- @param #ZONE_BASE self --- @param DCSTypes#Vec2 PointVec2 The location to test. +-- @param DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_BASE:IsPointVec2InZone( PointVec2 ) - self:F2( PointVec2 ) +function ZONE_BASE:IsPointVec2InZone( Vec2 ) + self:F2( Vec2 ) return false end @@ -121,18 +121,27 @@ function ZONE_BASE:IsPointVec3InZone( PointVec3 ) return InZone end +--- Returns the Vec2 coordinate of the zone. +-- @param #ZONE_BASE self +-- @return #nil. +function ZONE_BASE:GetVec2() + self:F2( self.ZoneName ) + + return nil +end --- Define a random @{DCSTypes#Vec2} within the zone. -- @param #ZONE_BASE self --- @return DCSTypes#Vec2 The Vec2 coordinates. +-- @return #nil The Vec2 coordinates. function ZONE_BASE:GetRandomVec2() - return { x = 0, y = 0 } + return nil end --- Get the bounding square the zone. -- @param #ZONE_BASE self --- @return #ZONE_BASE.BoundingSquare The bounding square. +-- @return #nil The bounding square. function ZONE_BASE:GetBoundingSquare() - return { x1 = 0, y1 = 0, x2 = 0, y2 = 0 } + --return { x1 = 0, y1 = 0, x2 = 0, y2 = 0 } + return nil end @@ -147,7 +156,7 @@ end --- The ZONE_RADIUS class, defined by a zone name, a location and a radius. -- @type ZONE_RADIUS --- @field DCSTypes#Vec2 PointVec2 The current location of the zone. +-- @field DCSTypes#Vec2 Vec2 The current location of the zone. -- @field DCSTypes#Distance Radius The radius of the zone. -- @extends Zone#ZONE_BASE ZONE_RADIUS = { @@ -157,15 +166,15 @@ ZONE_RADIUS = { --- Constructor of ZONE_RADIUS, taking the zone name, the zone location and a radius. -- @param #ZONE_RADIUS self -- @param #string ZoneName Name of the zone. --- @param DCSTypes#Vec2 PointVec2 The location of the zone. +-- @param DCSTypes#Vec2 Vec2 The location of the zone. -- @param DCSTypes#Distance Radius The radius of the zone. -- @return #ZONE_RADIUS self -function ZONE_RADIUS:New( ZoneName, PointVec2, Radius ) +function ZONE_RADIUS:New( ZoneName, Vec2, Radius ) local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) - self:F( { ZoneName, PointVec2, Radius } ) + self:F( { ZoneName, Vec2, Radius } ) self.Radius = Radius - self.PointVec2 = PointVec2 + self.Vec2 = Vec2 return self end @@ -179,7 +188,7 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points ) self:F2( SmokeColor ) local Point = {} - local PointVec2 = self:GetPointVec2() + local Vec2 = self:GetVec2() Points = Points and Points or 360 @@ -188,8 +197,8 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points ) for Angle = 0, 360, 360 / Points do local Radial = Angle * RadialBase / 360 - Point.x = PointVec2.x + math.cos( Radial ) * self:GetRadius() - Point.y = PointVec2.y + math.sin( Radial ) * self:GetRadius() + Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() + Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() POINT_VEC2:New( Point.x, Point.y ):Smoke( SmokeColor ) end @@ -207,7 +216,7 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth ) self:F2( { FlareColor, Azimuth } ) local Point = {} - local PointVec2 = self:GetPointVec2() + local Vec2 = self:GetVec2() Points = Points and Points or 360 @@ -216,8 +225,8 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth ) for Angle = 0, 360, 360 / Points do local Radial = Angle * RadialBase / 360 - Point.x = PointVec2.x + math.cos( Radial ) * self:GetRadius() - Point.y = PointVec2.y + math.sin( Radial ) * self:GetRadius() + Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() + Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() POINT_VEC2:New( Point.x, Point.y ):Flare( FlareColor, Azimuth ) end @@ -251,26 +260,26 @@ end --- Returns the location of the zone. -- @param #ZONE_RADIUS self -- @return DCSTypes#Vec2 The location of the zone. -function ZONE_RADIUS:GetPointVec2() +function ZONE_RADIUS:GetVec2() self:F2( self.ZoneName ) - self:T2( { self.PointVec2 } ) + self:T2( { self.Vec2 } ) - return self.PointVec2 + return self.Vec2 end --- Sets the location of the zone. -- @param #ZONE_RADIUS self --- @param DCSTypes#Vec2 PointVec2 The new location of the zone. +-- @param DCSTypes#Vec2 Vec2 The new location of the zone. -- @return DCSTypes#Vec2 The new location of the zone. -function ZONE_RADIUS:SetPointVec2( PointVec2 ) +function ZONE_RADIUS:SetPointVec2( Vec2 ) self:F2( self.ZoneName ) - self.PointVec2 = PointVec2 + self.Vec2 = Vec2 - self:T2( { self.PointVec2 } ) + self:T2( { self.Vec2 } ) - return self.PointVec2 + return self.Vec2 end --- Returns the point of the zone. @@ -280,9 +289,9 @@ end function ZONE_RADIUS:GetPointVec3( Height ) self:F2( self.ZoneName ) - local PointVec2 = self:GetPointVec2() + local Vec2 = self:GetVec2() - local PointVec3 = { x = PointVec2.x, y = land.getHeight( self:GetPointVec2() ) + Height, z = PointVec2.y } + local PointVec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } self:T2( { PointVec3 } ) @@ -292,14 +301,14 @@ end --- Returns if a location is within the zone. -- @param #ZONE_RADIUS self --- @param DCSTypes#Vec2 PointVec2 The location to test. +-- @param DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_RADIUS:IsPointVec2InZone( PointVec2 ) - self:F2( PointVec2 ) +function ZONE_RADIUS:IsPointVec2InZone( Vec2 ) + self:F2( Vec2 ) - local ZonePointVec2 = self:GetPointVec2() + local ZoneVec2 = self:GetVec2() - if (( PointVec2.x - ZonePointVec2.x )^2 + ( PointVec2.y - ZonePointVec2.y ) ^2 ) ^ 0.5 <= self:GetRadius() then + if (( Vec2.x - ZoneVec2.x )^2 + ( Vec2.y - ZoneVec2.y ) ^2 ) ^ 0.5 <= self:GetRadius() then return true end @@ -310,10 +319,10 @@ end -- @param #ZONE_RADIUS self -- @param DCSTypes#Vec3 PointVec3 The point to test. -- @return #boolean true if the point is within the zone. -function ZONE_RADIUS:IsPointVec3InZone( PointVec3 ) - self:F2( PointVec3 ) +function ZONE_RADIUS:IsPointVec3InZone( Vec3 ) + self:F2( Vec3 ) - local InZone = self:IsPointVec2InZone( { x = PointVec3.x, y = PointVec3.z } ) + local InZone = self:IsPointVec2InZone( { x = Vec3.x, y = Vec3.z } ) return InZone end @@ -325,11 +334,11 @@ function ZONE_RADIUS:GetRandomVec2() self:F( self.ZoneName ) local Point = {} - local PointVec2 = self:GetPointVec2() + local Vec2 = self:GetVec2() local angle = math.random() * math.pi*2; - Point.x = PointVec2.x + math.cos( angle ) * math.random() * self:GetRadius(); - Point.y = PointVec2.y + math.sin( angle ) * math.random() * self:GetRadius(); + Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); + Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); self:T( { Point } ) @@ -383,8 +392,8 @@ ZONE_UNIT = { -- @param DCSTypes#Distance Radius The radius of the zone. -- @return #ZONE_UNIT self function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius ) - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetPointVec2(), Radius ) ) - self:F( { ZoneName, ZoneUNIT:GetPointVec2(), Radius } ) + local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) + self:F( { ZoneName, ZoneUNIT:GetVec2(), Radius } ) self.ZoneUNIT = ZoneUNIT @@ -395,14 +404,14 @@ end --- Returns the current location of the @{Unit#UNIT}. -- @param #ZONE_UNIT self -- @return DCSTypes#Vec2 The location of the zone based on the @{Unit#UNIT}location. -function ZONE_UNIT:GetPointVec2() +function ZONE_UNIT:GetVec2() self:F( self.ZoneName ) - local ZonePointVec2 = self.ZoneUNIT:GetPointVec2() + local ZoneVec2 = self.ZoneUNIT:GetVec2() - self:T( { ZonePointVec2 } ) + self:T( { ZoneVec2 } ) - return ZonePointVec2 + return ZoneVec2 end -- Polygons @@ -492,10 +501,10 @@ end --- Returns if a location is within the zone. -- Source learned and taken from: https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html -- @param #ZONE_POLYGON_BASE self --- @param DCSTypes#Vec2 PointVec2 The location to test. +-- @param DCSTypes#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. -function ZONE_POLYGON_BASE:IsPointVec2InZone( PointVec2 ) - self:F2( PointVec2 ) +function ZONE_POLYGON_BASE:IsPointVec2InZone( Vec2 ) + self:F2( Vec2 ) local Next local Prev @@ -506,8 +515,8 @@ function ZONE_POLYGON_BASE:IsPointVec2InZone( PointVec2 ) while Next <= #self.Polygon do self:T( { Next, Prev, self.Polygon[Next], self.Polygon[Prev] } ) - if ( ( ( self.Polygon[Next].y > PointVec2.y ) ~= ( self.Polygon[Prev].y > PointVec2.y ) ) and - ( PointVec2.x < ( self.Polygon[Prev].x - self.Polygon[Next].x ) * ( PointVec2.y - self.Polygon[Next].y ) / ( self.Polygon[Prev].y - self.Polygon[Next].y ) + self.Polygon[Next].x ) + if ( ( ( self.Polygon[Next].y > Vec2.y ) ~= ( self.Polygon[Prev].y > Vec2.y ) ) and + ( Vec2.x < ( self.Polygon[Prev].x - self.Polygon[Next].x ) * ( Vec2.y - self.Polygon[Next].y ) / ( self.Polygon[Prev].y - self.Polygon[Next].y ) + self.Polygon[Next].x ) ) then InPolygon = not InPolygon end diff --git a/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.lua b/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.lua index 1e7fb9e92..bc63bac96 100644 --- a/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.lua +++ b/Moose Test Missions/Moose_Test_TASK_SEAD/Moose_Test_TASK_SEAD.lua @@ -8,7 +8,7 @@ local Client = CLIENT:FindByName( "Test SEAD" ) local TargetSet = SET_UNIT:New():FilterPrefixes( "US Hawk SR" ):FilterStart() local Task_Menu = TASK2_CLIENT_MENU:New( Client, Mission, "SEAD" ) ---local Task_Route = TASK2_ROUTE:New( Client, Mission ) -- Zone is dynamically defined in state machine +local Task_Route = TASK2_ROUTE:New( Client, Mission ) -- The target location is dynamically defined in state machine local Task_Client_Sead = TASK2_SEAD:New( Client, Mission, TargetSet ) Task_Client_Sead:AddScore( "Destroy", "Destroyed RADAR", 25 ) @@ -17,17 +17,20 @@ Task_Client_Sead:AddScore( "Success", "Destroyed all radars!!!", 100 ) local Task_Sead = STATEMACHINE:New( { initial = 'None', events = { - { name = 'Start', from = 'None', to = 'Unassigned' }, - { name = 'Next', from = 'Unassigned', to = 'Assigned' }, --- { name = 'Route', from = 'Assigned', to = 'Arrived' }, - { name = 'Next', from = 'Assigned', to = 'Success' }, - { name = 'Failed', from = 'Assigned', to = 'Failed' }, + { name = 'Start', from = 'None', to = 'Unassigned' }, + { name = 'Next', from = 'Unassigned', to = 'Assigned' }, + { name = 'Next', from = 'Assigned', to = 'Arrived' }, + { name = 'Next', from = 'Arrived', to = 'Success' }, + { name = 'Fail', from = 'Assigned', to = 'Failed' }, + { name = 'Fail', from = 'Arrived', to = 'Failed' } }, subs = { - Menu = { onstateparent = 'Unassigned', oneventparent = 'Start', fsm = Task_Menu.Fsm, event = 'Menu', returnevents = { 'Next' } }, - --Assigned = { onstateparent = 'Assigned', oneventparent = 'Assign', fsm = Task_Route.Fsm, event = 'Route' }, - Sead = { onstateparent = 'Assigned', oneventparent = 'Next', fsm = Task_Client_Sead.Fsm, event = 'Await', returnevents = { 'Next' } } + Menu = { onstateparent = 'Unassigned', oneventparent = 'Start', fsm = Task_Menu.Fsm, event = 'Menu', returnevents = { 'Next' } }, + Route = { onstateparent = 'Assigned', oneventparent = 'Next', fsm = Task_Route.Fsm, event = 'Route', returnevents = { 'Next' } }, + Sead = { onstateparent = 'Arrived', oneventparent = 'Next', fsm = Task_Client_Sead.Fsm, event = 'Await', returnevents = { 'Next' } } } } ) Task_Sead:Start() + + diff --git a/Moose Training/Presentations/DCS World - MOOSE - Tasking - SEAD.pptx b/Moose Training/Presentations/DCS World - MOOSE - Tasking - SEAD.pptx index 8079649e9..e76f27590 100644 Binary files a/Moose Training/Presentations/DCS World - MOOSE - Tasking - SEAD.pptx and b/Moose Training/Presentations/DCS World - MOOSE - Tasking - SEAD.pptx differ