diff --git a/.appveyor/appveyor.yml b/.appveyor/appveyor.yml index 7fcd96a10..2af7fd529 100644 --- a/.appveyor/appveyor.yml +++ b/.appveyor/appveyor.yml @@ -26,24 +26,25 @@ init: # - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) install: + - cmd: # Outcomment if lua environment invalidates and needs to be reinstalled, otherwise all will run from the cache. -# - call choco install 7zip.commandline -# - call choco install lua51 -# - call choco install luarocks -# - call refreshenv -# - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" -# - cmd: PATH = %PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\bin -# - cmd: set LUA_PATH = %LUA_PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?.lua;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?\init.lua -# - cmd: set LUA_CPATH = %LUA_CPATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\lib\lua\5.1\?.dll -# - call luarocks install luasrcdiet -# - call luarocks install checks -# - call luarocks install luadocumentor -# - call luarocks install luacheck + call choco install 7zip.commandline + call choco install lua51 + call choco install luarocks + call refreshenv + call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" + cmd: PATH = %PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\bin + cmd: set LUA_PATH = %LUA_PATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?.lua;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\share\lua\5.1\?\init.lua + cmd: set LUA_CPATH = %LUA_CPATH%;C:\ProgramData\chocolatey\lib\luarocks\luarocks-2.4.3-win32\systree\lib\lua\5.1\?.dll + call luarocks install luasrcdiet + call luarocks install checks + call luarocks install luadocumentor + call luarocks install luacheck -#cache: -# - C:\ProgramData\chocolatey\lib -# - C:\ProgramData\chocolatey\bin +cache: +C:\ProgramData\chocolatey\lib +C:\ProgramData\chocolatey\bin @@ -51,8 +52,9 @@ build_script: - ps: | if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' ) { + echo "Hello World!" $apiUrl = 'https://ci.appveyor.com/api' - $token = 'qts80b5kpq0ooj4x6vvw' + $token = 'v2.6hcv3ige78kg3yvg4ge8' $headers = @{ "Authorization" = "Bearer $token" "Content-type" = "application/json" @@ -65,7 +67,7 @@ build_script: if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' ) { $apiUrl = 'https://ci.appveyor.com/api' - $token = 'qts80b5kpq0ooj4x6vvw' + $token = 'v2.6hcv3ige78kg3yvg4ge8' $headers = @{ "Authorization" = "Bearer $token" "Content-type" = "application/json" diff --git a/.gitignore b/.gitignore index 96ee72d9a..c84f22bdf 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ local.properties # External tool builders .externalToolBuilders/ +# AppVeyor +.appveyor/ # CDT-specific .cproject diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 000000000..b29cbbc22 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,54 @@ +ignore = { + "011", -- A syntax error. + "021", -- An invalid inline option. + "022", -- An unpaired inline push directive. + "023", -- An unpaired inline pop directive. + "111", -- Setting an undefined global variable. + "112", -- Mutating an undefined global variable. + "113", -- Accessing an undefined global variable. + "121", -- Setting a read-only global variable. + "122", -- Setting a read-only field of a global variable. + "131", -- Unused implicitly defined global variable. + "142", -- Setting an undefined field of a global variable. + "143", -- Accessing an undefined field of a global variable. + "211", -- Unused local variable. + "212", -- Unused argument. + "213", -- Unused loop variable. + "221", -- Local variable is accessed but never set. + "231", -- Local variable is set but never accessed. + "232", -- An argument is set but never accessed. + "233", -- Loop variable is set but never accessed. + "241", -- Local variable is mutated but never accessed. + "311", -- Value assigned to a local variable is unused. + "312", -- Value of an argument is unused. + "313", -- Value of a loop variable is unused. + "314", -- Value of a field in a table literal is unused. + "321", -- Accessing uninitialized local variable. + "331", -- Value assigned to a local variable is mutated but never accessed. + "341", -- Mutating uninitialized local variable. + "411", -- Redefining a local variable. + "412", -- Redefining an argument. + "413", -- Redefining a loop variable. + "421", -- Shadowing a local variable. + "422", -- Shadowing an argument. + "423", -- Shadowing a loop variable. + "431", -- Shadowing an upvalue. + "432", -- Shadowing an upvalue argument. + "433", -- Shadowing an upvalue loop variable. + "511", -- Unreachable code. + "512", -- Loop can be executed at most once. + "521", -- Unused label. + "531", -- Left-hand side of an assignment is too short. + "532", -- Left-hand side of an assignment is too long. + "541", -- An empty do end block. + "542", -- An empty if branch. + "551", -- An empty statement. + "561", -- Cyclomatic complexity of a function is too high. + "571", -- A numeric for loop goes from #(expr) down to 1 or less without negative step. + "611", -- A line consists of nothing but whitespace. + "612", -- A line contains trailing whitespace. + "613", -- Trailing whitespace in a string. + "614", -- Trailing whitespace in a comment. + "621", -- Inconsistent indentation (SPACE followed by TAB). + "631", -- Line is too long. +} diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index f2c5ebcda..7f075b420 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -190,7 +190,7 @@ do -- ACT_ROUTE self:F( { ZoneName = ZoneName } ) local Zone = Zone -- Core.Zone#ZONE local ZoneCoord = Zone:GetCoordinate() - local ZoneDistance = ZoneCoord:Get2DDistance( self.Coordinate ) + local ZoneDistance = ZoneCoord:Get2DDistance( Coordinate ) self:F( { ShortestDistance, ShortestReferenceName } ) if ShortestDistance == 0 or ZoneDistance < ShortestDistance then ShortestDistance = ZoneDistance diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 7bb39a1b7..07ddcfdf1 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -254,7 +254,7 @@ EVENTS = { -- Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event: -- -- * A (Object.Category.)UNIT : A UNIT object type is involved in the Event. --- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event.µ +-- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event. -- -- @type EVENTDATA -- @field #number id The identifier of the event. diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 011ea21d8..54dbc86fd 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -1242,7 +1242,7 @@ do -- Group -- @param #Group self -- @return #number - --- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed. Initial size limits the unitNumber parameter for Group.getUnit() function. + --- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed; Initial size limits the unitNumber parameter for Group.getUnit() function. -- @function [parent=#Group] getInitialSize -- @param #Group self -- @return #number diff --git a/Moose Development/Moose/Ops/ATIS.lua b/Moose Development/Moose/Ops/ATIS.lua index 001b43f1b..91333138d 100644 --- a/Moose Development/Moose/Ops/ATIS.lua +++ b/Moose Development/Moose/Ops/ATIS.lua @@ -580,6 +580,8 @@ ATIS.version="0.9.1" -- DONE: Metric units. -- DONE: Set UTC correction. -- DONE: Set magnetic variation. +-- DONE: New DCS 2.7 weather presets. +-- DONE: whatever ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 2109475a7..c99cdf3ba 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -17930,7 +17930,7 @@ function AIRBOSS:onbeforeSave(From, Event, To, path, filename) -- Check default path. if path==nil and not lfs then - self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\DCS\" folder.") + self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.") end return true @@ -18037,7 +18037,7 @@ function AIRBOSS:onbeforeLoad(From, Event, To, path, filename) -- Check default path. if path==nil and not lfs then - self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\DCS\" folder.") + self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.") end -- Set path or default. diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 6ad1cf5d0..113bfff23 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -1,11 +1,11 @@ --- **Ops** - Generic group enhancement. --- +-- -- This class is **not** meant to be used itself by the end user. It contains common functionalities of derived classes for air, ground and sea. --- +-- -- === -- -- ### Author: **funkyfranky** --- +-- -- === -- @module Ops.OpsGroup -- @image OPS_OpsGroup.png @@ -26,7 +26,7 @@ -- @field #boolean isArmygroup Is an ARMYGROUP. -- @field #boolean isNavygroup Is a NAVYGROUP. -- @field #boolean isHelo If true, the is a helicopter group. --- @field #boolean isVTOL If true, the is capable of Vertical TakeOff and Landing (VTOL). +-- @field #boolean isVTOL If true, the is capable of Vertical TakeOff and Landing (VTOL). -- @field #table elements Table of elements, i.e. units of the group. -- @field #boolean isAI If true, group is purely AI. -- @field #boolean isAircraft If true, group is airplane or helicopter. @@ -70,9 +70,9 @@ -- @field Ops.Auftrag#AUFTRAG missionpaused Paused mission. -- @field #number Ndestroyed Number of destroyed units. -- @field #number Nkills Number kills of this groups. --- +-- -- @field Core.Point#COORDINATE coordinate Current coordinate. --- +-- -- @field DCS#Vec3 position Position of the group at last status check. -- @field DCS#Vec3 positionLast Backup of last position vec to monitor changes. -- @field #number heading Heading of the group at last status check. @@ -81,31 +81,31 @@ -- @field DCS#Vec3 orientXLast Backup of last orientation to monitor changes. -- @field #number traveldist Distance traveled in meters. This is a lower bound. -- @field #number traveltime Time. --- +-- -- @field Core.Astar#ASTAR Astar path finding. -- @field #boolean ispathfinding If true, group is on pathfinding route. --- +-- -- @field #OPSGROUP.Radio radio Current radio settings. -- @field #OPSGROUP.Radio radioDefault Default radio settings. -- @field Core.RadioQueue#RADIOQUEUE radioQueue Radio queue. --- +-- -- @field #OPSGROUP.Beacon tacan Current TACAN settings. -- @field #OPSGROUP.Beacon tacanDefault Default TACAN settings. --- +-- -- @field #OPSGROUP.Beacon icls Current ICLS settings. -- @field #OPSGROUP.Beacon iclsDefault Default ICLS settings. --- +-- -- @field #OPSGROUP.Option option Current optional settings. -- @field #OPSGROUP.Option optionDefault Default option settings. --- +-- -- @field #OPSGROUP.Callsign callsign Current callsign settings. -- @field #OPSGROUP.Callsign callsignDefault Default callsign settings. --- +-- -- @field #OPSGROUP.Spot spot Laser and IR spot. --- +-- -- @field #OPSGROUP.Ammo ammo Initial ammount of ammo. -- @field #OPSGROUP.WeaponData weaponData Weapon data table with key=BitType. --- +-- -- @field #OPSGROUP.Element carrier Carrier the group is loaded into as cargo. -- @field #OPSGROUP carrierGroup Carrier group transporting this group as cargo. -- @field #OPSGROUP.MyCarrier mycarrier Carrier group for this group. @@ -117,7 +117,7 @@ -- @field #number cargocounter Running number of cargo UIDs. -- @field #OPSGROUP.CarrierLoader carrierLoader Carrier loader parameters. -- @field #OPSGROUP.CarrierLoader carrierUnloader Carrier unloader parameters. --- +-- -- @extends Core.Fsm#FSM --- *A small group of determined and like-minded people can change the course of history.* --- Mahatma Gandhi @@ -127,13 +127,13 @@ -- ![Banner Image](..\Presentations\OPS\OpsGroup\_Main.png) -- -- # The OPSGROUP Concept --- +-- -- The OPSGROUP class contains common functions used by other classes such as FLIGHGROUP, NAVYGROUP and ARMYGROUP. --- Those classes inherit everything of this class and extend it with features specific to their unit category. --- +-- Those classes inherit everything of this class and extend it with features specific to their unit category. +-- -- This class is **NOT** meant to be used by the end user itself. --- --- +-- +-- -- @field #OPSGROUP OPSGROUP = { ClassName = "OPSGROUP", @@ -154,7 +154,7 @@ OPSGROUP = { taskenroute = nil, taskpaused = {}, missionqueue = {}, - currentmission = nil, + currentmission = nil, detectedunits = {}, detectedgroups = {}, attribute = nil, @@ -190,7 +190,7 @@ OPSGROUP = { -- @field DCS#Unit DCSunit The DCS unit object. -- @field #boolean ai If true, element is AI. -- @field #string skill Skill level. --- +-- -- @field Core.Zone#ZONE_POLYGON_BASE zoneBoundingbox Bounding box zone of the element unit. -- @field Core.Zone#ZONE_POLYGON_BASE zoneLoad Loading zone. -- @field Core.Zone#ZONE_POLYGON_BASE zoneUnload Unloading zone. @@ -203,7 +203,7 @@ OPSGROUP = { -- @field #number length Length of element in meters. -- @field #number width Width of element in meters. -- @field #number height Height of element in meters. --- +-- -- @field DCS#Vec3 vec3 Last known 3D position vector. -- @field DCS#Vec3 orientX Last known ordientation vector in the direction of the nose X. -- @field #number heading Last known heading in degrees. @@ -211,7 +211,7 @@ OPSGROUP = { -- @field #number life0 Initial life points. -- @field #number life Life points when last updated. -- @field #number damage Damage of element in percent. --- +-- -- @field DCS#Object.Desc descriptors Descriptors table. -- @field #number weightEmpty Empty weight in kg. -- @field #number weightMaxTotal Max. total weight in kg. @@ -440,7 +440,7 @@ OPSGROUP.version="0.7.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - + -- TODO: AI on/off. -- TODO: Invisible/immortal. -- TODO: F10 menu. @@ -464,7 +464,7 @@ function OPSGROUP:New(group) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #OPSGROUP - + -- Get group and group name. if type(group)=="string" then self.groupname=group @@ -473,26 +473,26 @@ function OPSGROUP:New(group) self.group=group self.groupname=group:GetName() end - + -- Set some string id for output to DCS.log file. self.lid=string.format("OPSGROUP %s | ", tostring(self.groupname)) - + if self.group then if not self:IsExist() then self:E(self.lid.."ERROR: GROUP does not exist! Returning nil") return nil end end - + -- Init set of detected units. self.detectedunits=SET_UNIT:New() - + -- Init set of detected groups. self.detectedgroups=SET_GROUP:New() - + -- Init inzone set. self.inzones=SET_ZONE:New() - + -- Laser. self.spot={} self.spot.On=false @@ -504,33 +504,33 @@ function OPSGROUP:New(group) self.cargocounter=1 self:SetCarrierLoaderAllAspect() self:SetCarrierUnloaderAllAspect() - - + + -- Init task counter. self.taskcurrent=0 self.taskcounter=0 - + -- Start state. self:SetStartState("InUtero") -- Add FSM transitions. -- From State --> Event --> To State self:AddTransition("InUtero", "Spawned", "Spawned") -- The whole group was spawned. - self:AddTransition("*", "Respawn", "*") -- Respawn group. + self:AddTransition("*", "Respawn", "*") -- Respawn group. self:AddTransition("*", "Dead", "Dead") -- The whole group is dead. self:AddTransition("*", "InUtero", "InUtero") -- Deactivated group goes back to mummy. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:AddTransition("*", "Status", "*") -- Status update. - - self:AddTransition("*", "Destroyed", "*") -- The whole group is dead. + + self:AddTransition("*", "Destroyed", "*") -- The whole group is dead. self:AddTransition("*", "Damaged", "*") -- Someone in the group took damage. self:AddTransition("*", "UpdateRoute", "*") -- Update route of group. Only if airborne. self:AddTransition("*", "PassingWaypoint", "*") -- Passing waypoint. - + self:AddTransition("*", "Wait", "*") -- Group will wait for further orders. - + self:AddTransition("*", "DetectedUnit", "*") -- Unit was detected (again) in this detection cycle. self:AddTransition("*", "DetectedUnitNew", "*") -- Add a newly detected unit to the detected units set. self:AddTransition("*", "DetectedUnitKnown", "*") -- A known unit is still detected. @@ -539,8 +539,8 @@ function OPSGROUP:New(group) self:AddTransition("*", "DetectedGroup", "*") -- Unit was detected (again) in this detection cycle. self:AddTransition("*", "DetectedGroupNew", "*") -- Add a newly detected unit to the detected units set. self:AddTransition("*", "DetectedGroupKnown", "*") -- A known unit is still detected. - self:AddTransition("*", "DetectedGroupLost", "*") -- Group lost a detected target group. - + self:AddTransition("*", "DetectedGroupLost", "*") -- Group lost a detected target group. + self:AddTransition("*", "PassingWaypoint", "*") -- Group passed a waypoint. self:AddTransition("*", "GotoWaypoint", "*") -- Group switches to a specific waypoint. @@ -565,7 +565,7 @@ function OPSGROUP:New(group) self:AddTransition("*", "TaskPause", "*") -- Pause current task. Not implemented yet! self:AddTransition("*", "TaskCancel", "*") -- Cancel current task. self:AddTransition("*", "TaskDone", "*") -- Task is over. - + self:AddTransition("*", "MissionStart", "*") -- Mission is started. self:AddTransition("*", "MissionExecute", "*") -- Mission execution began. self:AddTransition("*", "MissionCancel", "*") -- Cancel current mission. @@ -582,7 +582,7 @@ function OPSGROUP:New(group) self:AddTransition("*", "Board", "*") -- Group is ordered to board the carrier. self:AddTransition("*", "Embarked", "*") -- Group was loaded into a cargo carrier. self:AddTransition("*", "Disembarked", "*") -- Group was unloaded from a cargo carrier. - + self:AddTransition("*", "Pickup", "*") -- Carrier and is on route to pick up cargo. self:AddTransition("*", "Loading", "*") -- Carrier is loading cargo. self:AddTransition("*", "Load", "*") -- Carrier loads cargo into carrier. @@ -592,7 +592,7 @@ function OPSGROUP:New(group) self:AddTransition("*", "Unload", "*") -- Carrier unload a cargo group. self:AddTransition("*", "Unloaded", "*") -- Carrier unloaded all its current cargo. self:AddTransition("*", "Delivered", "*") -- Carrier delivered ALL cargo of the transport assignment. - + ------------------------ --- Pseudo Functions --- ------------------------ @@ -616,7 +616,7 @@ function OPSGROUP:New(group) -- TODO: Add pseudo functions. - return self + return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -641,9 +641,9 @@ function OPSGROUP:GetLifePoints(Element) local life0=0 if Element then - + local unit=Element.unit - + if unit then life=unit:GetLife() life0=unit:GetLife0() @@ -655,10 +655,10 @@ function OPSGROUP:GetLifePoints(Element) local l,l0=self:GetLifePoints(element) life=life+l life0=life+l0 - end - + end + end - + return life, life0 end @@ -691,7 +691,7 @@ function OPSGROUP:GetSpeedCruise() end --- Set detection on or off. --- If detection is on, detected targets of the group will be evaluated and FSM events triggered. +-- If detection is on, detected targets of the group will be evaluated and FSM events triggered. -- @param #OPSGROUP self -- @param #boolean Switch If `true`, detection is on. If `false` or `nil`, detection is off. Default is off. -- @return #OPSGROUP self @@ -700,7 +700,7 @@ function OPSGROUP:SetDetection(Switch) return self end ---- Set LASER parameters. +--- Set LASER parameters. -- @param #OPSGROUP self -- @param #number Code Laser code. Default 1688. -- @param #boolean CheckLOS Check if lasing unit has line of sight to target coordinate. Default is `true`. @@ -719,7 +719,7 @@ function OPSGROUP:SetLaser(Code, CheckLOS, IROff, UpdateTime) return self end ---- Get LASER code. +--- Get LASER code. -- @param #OPSGROUP self -- @return #number Current Laser code. function OPSGROUP:GetLaserCode() @@ -762,7 +762,7 @@ function OPSGROUP:AddCheckZone(CheckZone) end ---- Add a weapon range for ARTY auftrag. +--- Add a weapon range for ARTY auftrag. -- @param #OPSGROUP self -- @param #number RangeMin Minimum range in nautical miles. Default 0 NM. -- @param #number RangeMax Maximum range in nautical miles. Default 10 NM. @@ -779,9 +779,9 @@ function OPSGROUP:AddWeaponRange(RangeMin, RangeMax, BitType) weapon.RangeMax=RangeMax weapon.RangeMin=RangeMin - self.weaponData=self.weaponData or {} + self.weaponData=self.weaponData or {} self.weaponData[weapon.BitType]=weapon - + return self end @@ -793,7 +793,7 @@ function OPSGROUP:GetWeaponData(BitType) BitType=BitType or ENUMS.WeaponFlag.Auto - if self.weaponData[BitType] then + if self.weaponData[BitType] then return self.weaponData[BitType] else return self.weaponData[ENUMS.WeaponFlag.Auto] @@ -837,22 +837,22 @@ function OPSGROUP:GetThreat(ThreatLevelMin, ThreatLevelMax) local level=0 for _,_unit in pairs(self.detectedunits:GetSet()) do local unit=_unit --Wrapper.Unit#UNIT - + -- Get threatlevel of unit. local threatlevel=unit:GetThreatLevel() - + -- Check if withing threasholds. if threatlevel>=ThreatLevelMin and threatlevel<=ThreatLevelMax then - + if threatlevellevelmax then threat=unit levelmax=threatlevel end - + end return threat, levelmax @@ -897,8 +897,8 @@ function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate) end --- Function to check LoS for an element of the group. - local function checklos(element) - local vec3=element.unit:GetVec3() + local function checklos(element) + local vec3=element.unit:GetVec3() if OffsetElement then vec3=UTILS.VecAdd(vec3, OffsetElement) end @@ -907,19 +907,19 @@ function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate) return _los end - if Element then + if Element then local los=checklos(Element) return los else - + for _,element in pairs(self.elements) do -- Get LoS of this element. - local los=checklos(element) + local los=checklos(element) if los then return true end end - + return false end @@ -955,12 +955,12 @@ end function OPSGROUP:GetUnit(UnitNumber) local DCSUnit=self:GetDCSUnit(UnitNumber) - + if DCSUnit then local unit=UNIT:Find(DCSUnit) return unit end - + return nil end @@ -971,12 +971,12 @@ end function OPSGROUP:GetDCSUnit(UnitNumber) local DCSGroup=self:GetDCSGroup() - + if DCSGroup then local unit=DCSGroup:getUnit(UnitNumber or 1) return unit end - + return nil end @@ -986,12 +986,12 @@ end function OPSGROUP:GetDCSUnits() local DCSGroup=self:GetDCSGroup() - + if DCSGroup then local units=DCSGroup:getUnits() return units end - + return nil end @@ -1003,7 +1003,7 @@ end function OPSGROUP:GetVec2(UnitName) local vec3=self:GetVec3(UnitName) - + if vec3 then local vec2={x=vec3.x, y=vec3.z} return vec2 @@ -1020,7 +1020,7 @@ end function OPSGROUP:GetVec3(UnitName) local vec3=nil --DCS#Vec3 - + -- First check if this group is loaded into a carrier local carrier=self:_GetMyCarrierElement() if carrier and carrier.status~=OPSGROUP.ElementStatus.DEAD and self:IsLoaded() then @@ -1033,21 +1033,21 @@ function OPSGROUP:GetVec3(UnitName) if self:IsExist() then - local unit=nil --DCS#Unit + local unit=nil --DCS#Unit if UnitName then unit=Unit.getByName(UnitName) else unit=self:GetDCSUnit() end - + if unit then - local vec3=unit:getPoint() + local vec3=unit:getPoint() return vec3 end - + end - + return nil end @@ -1058,11 +1058,11 @@ end function OPSGROUP:GetCoordinate(NewObject) local vec3=self:GetVec3() or self.position --DCS#Vec3 - + if vec3 then - + self.coordinate=self.coordinate or COORDINATE:New(0,0,0) - + self.coordinate.x=vec3.x self.coordinate.y=vec3.y self.coordinate.z=vec3.z @@ -1072,11 +1072,11 @@ function OPSGROUP:GetCoordinate(NewObject) return coord else return self.coordinate - end + end else - self:E(self.lid.."WARNING: Cannot get coordinate!") + self:E(self.lid.."WARNING: Cannot get coordinate!") end - + return nil end @@ -1087,7 +1087,7 @@ end function OPSGROUP:GetVelocity(UnitName) if self:IsExist() then - + local unit=nil --DCS#Unit if UnitName then @@ -1095,20 +1095,20 @@ function OPSGROUP:GetVelocity(UnitName) else unit=self:GetDCSUnit() end - + if unit then - + local velvec3=unit:getVelocity() - + local vel=UTILS.VecNorm(velvec3) - + return vel end - + else self:E(self.lid.."WARNING: Group does not exist. Cannot get velocity!") end - + return nil end @@ -1119,33 +1119,33 @@ end function OPSGROUP:GetHeading(UnitName) if self:IsExist() then - - local unit=nil --DCS#Unit + + local unit=nil --DCS#Unit if UnitName then unit=Unit.getByName(UnitName) else unit=self:GetDCSUnit() end - + if unit then - + local pos=unit:getPosition() - + local heading=math.atan2(pos.x.z, pos.x.x) - + if heading<0 then heading=heading+ 2*math.pi end - + heading=math.deg(heading) - + return heading end - + else self:E(self.lid.."WARNING: Group does not exist. Cannot get heading!") end - + return nil end @@ -1158,26 +1158,26 @@ end function OPSGROUP:GetOrientation(UnitName) if self:IsExist() then - + local unit=nil --DCS#Unit - + if UnitName then unit=Unit.getByName(UnitName) else unit=self:GetDCSUnit() end - + if unit then - + local pos=unit:getPosition() - + return pos.x, pos.y, pos.z end - + else self:E(self.lid.."WARNING: Group does not exist. Cannot get orientation!") end - + return nil end @@ -1188,7 +1188,7 @@ end function OPSGROUP:GetOrientationX(UnitName) local X,Y,Z=self:GetOrientation(UnitName) - + return X end @@ -1225,26 +1225,26 @@ function OPSGROUP:DespawnUnit(UnitName, Delay, NoEventRemoveUnit) -- Get element. local element=self:GetElementByName(UnitName) - + if element then - + -- Get DCS unit object. local DCSunit=Unit.getByName(UnitName) - + if DCSunit then - + -- Despawn unit. DCSunit:destroy() - + -- Element goes back in utero. self:ElementInUtero(element) - + if not NoEventRemoveUnit then self:CreateEventRemoveUnit(timer.getTime(), DCSunit) end - + end - + end end @@ -1262,24 +1262,24 @@ function OPSGROUP:DespawnElement(Element, Delay, NoEventRemoveUnit) else if Element then - + -- Get DCS unit object. local DCSunit=Unit.getByName(Element.name) - + if DCSunit then - + -- Destroy object. DCSunit:destroy() - + -- Create a remove unit event. if not NoEventRemoveUnit then self:CreateEventRemoveUnit(timer.getTime(), DCSunit) end - + end - + end - + end return self @@ -1297,26 +1297,26 @@ function OPSGROUP:Despawn(Delay, NoEventRemoveUnit) else local DCSGroup=self:GetDCSGroup() - + if DCSGroup then - + -- Clear any task ==> makes DCS crash! --self.group:ClearTasks() -- Get all units. local units=self:GetDCSUnits() - + for i=1,#units do local unit=units[i] if unit then local name=unit:getName() if name then - -- Despawn the unit. + -- Despawn the unit. self:DespawnUnit(name, 0, NoEventRemoveUnit) end end end - + end end @@ -1333,24 +1333,24 @@ function OPSGROUP:DestroyUnit(UnitName, Delay) if Delay and Delay>0 then self:ScheduleOnce(Delay, OPSGROUP.DestroyUnit, self, UnitName, 0) else - + local unit=Unit.getByName(UnitName) - + if unit then -- Create a "Unit Lost" event. - local EventTime=timer.getTime() - + local EventTime=timer.getTime() + if self.isAircraft then self:CreateEventUnitLost(EventTime, unit) else self:CreateEventDead(EventTime, unit) end - + end - + end - + end --- Destroy group. The whole group is despawned and a *Unit Lost* for aircraft or *Dead* event for ground/naval units is generated for all current units. @@ -1365,18 +1365,18 @@ function OPSGROUP:Destroy(Delay) -- Get all units. local units=self:GetDCSUnits() - + if units then - - -- Create a "Unit Lost" event. + + -- Create a "Unit Lost" event. for _,unit in pairs(units) do if unit then self:DestroyUnit(unit:getName()) end end - + end - + end return self @@ -1390,21 +1390,21 @@ function OPSGROUP:Activate(delay) if delay and delay>0 then self:T2(self.lid..string.format("Activating late activated group in %d seconds", delay)) - self:ScheduleOnce(delay, OPSGROUP.Activate, self) + self:ScheduleOnce(delay, OPSGROUP.Activate, self) else - + if self:IsAlive()==false then - + self:T(self.lid.."Activating late activated group") self.group:Activate() self.isLateActivated=false - + elseif self:IsAlive()==true then self:E(self.lid.."WARNING: Activating group that is already activated") else self:E(self.lid.."ERROR: Activating group that is does not exist!") end - + end return self @@ -1417,19 +1417,19 @@ end function OPSGROUP:Deactivate(delay) if delay and delay>0 then - self:ScheduleOnce(delay, OPSGROUP.Deactivate, self) + self:ScheduleOnce(delay, OPSGROUP.Deactivate, self) else - + if self:IsAlive()==true then - + self.template.lateActivation=true - + local template=UTILS.DeepCopy(self.template) - + self:_Respawn(0, template) - + end - + end return self @@ -1445,13 +1445,13 @@ function OPSGROUP:SelfDestruction(Delay, ExplosionPower) if Delay and Delay>0 then self:ScheduleOnce(Delay, OPSGROUP.SelfDestruction, self, 0, ExplosionPower) else - + -- Loop over all elements. for i,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element - + local unit=element.unit - + if unit and unit:IsAlive() then unit:Explode(ExplosionPower or 100) end @@ -1611,7 +1611,7 @@ end function OPSGROUP:IsExist() local DCSGroup=self:GetDCSGroup() - + if DCSGroup then local exists=DCSGroup:isExist() return exists @@ -1827,21 +1827,21 @@ function OPSGROUP:MarkWaypoints(Duration) for i,_waypoint in pairs(self.waypoints or {}) do local waypoint=_waypoint --#OPSGROUP.Waypoint - + local text=string.format("Waypoint ID=%d of %s", waypoint.uid, self.groupname) text=text..string.format("\nSpeed=%.1f kts, Alt=%d ft (%s)", UTILS.MpsToKnots(waypoint.speed), UTILS.MetersToFeet(waypoint.alt), "BARO") - + if waypoint.marker then if waypoint.marker.text~=text then waypoint.marker.text=text end - + else waypoint.marker=MARKER:New(waypoint.coordinate, text):ToCoalition(self:GetCoalition()) end end - - + + if Duration then self:RemoveWaypointMarkers(Duration) end @@ -1861,14 +1861,14 @@ function OPSGROUP:RemoveWaypointMarkers(Delay) for i,_waypoint in pairs(self.waypoints or {}) do local waypoint=_waypoint --#OPSGROUP.Waypoint - + if waypoint.marker then waypoint.marker:Remove() end end - + end - + return self end @@ -1949,24 +1949,24 @@ function OPSGROUP:GetWaypointIndexNext(cyclic, i) if cyclic==nil then cyclic=self.adinfinitum end - + local N=#self.waypoints - + i=i or self.currentwp local n=math.min(i+1, N) - + if cyclic and i==N then n=1 end - + return n end --- Get current waypoint index. This is the index of the last passed waypoint. -- @param #OPSGROUP self -- @return #number Current waypoint index. -function OPSGROUP:GetWaypointIndexCurrent() +function OPSGROUP:GetWaypointIndexCurrent() return self.currentwp or 1 end @@ -1981,8 +1981,8 @@ function OPSGROUP:GetWaypointIndexAfterID(uid) return index+1 else return #self.waypoints+1 - end - + end + end --- Get waypoint. @@ -2007,7 +2007,7 @@ end function OPSGROUP:GetWaypointNext(cyclic) local n=self:GetWaypointIndexNext(cyclic) - + return self.waypoints[n] end @@ -2024,7 +2024,7 @@ end -- @return Core.Point#COORDINATE Coordinate of the next waypoint. function OPSGROUP:GetNextWaypointCoordinate(cyclic) - -- Get next waypoint + -- Get next waypoint local waypoint=self:GetWaypointNext(cyclic) return waypoint.coordinate @@ -2049,7 +2049,7 @@ end function OPSGROUP:GetWaypointSpeed(indx) local waypoint=self:GetWaypoint(indx) - + if waypoint then return UTILS.MpsToKnots(waypoint.speed) end @@ -2072,7 +2072,7 @@ end function OPSGROUP:GetWaypointID(indx) local waypoint=self:GetWaypoint(indx) - + if waypoint then return waypoint.uid end @@ -2088,7 +2088,7 @@ end function OPSGROUP:GetSpeedToWaypoint(indx) local speed=self:GetWaypointSpeed(indx) - + if speed<=0.1 then speed=self:GetSpeedCruise() end @@ -2102,22 +2102,22 @@ end -- @return #number Distance in meters. function OPSGROUP:GetDistanceToWaypoint(indx) local dist=0 - + if #self.waypoints>0 then indx=indx or self:GetWaypointIndexNext() - + local wp=self:GetWaypoint(indx) - + if wp then - + local coord=self:GetCoordinate() - + dist=coord:Get2DDistance(wp.coordinate) end - + end - + return dist end @@ -2126,21 +2126,21 @@ end -- @param #number indx Waypoint index. Default is the next waypoint. -- @return #number Time in seconds. If velocity is 0 function OPSGROUP:GetTimeToWaypoint(indx) - + local s=self:GetDistanceToWaypoint(indx) - + local v=self:GetVelocity() - + local t=s/v - + if t==math.inf then return 365*24*60*60 elseif t==math.nan then return 0 - else + else return t end - + end --- Returns the currently expected speed. @@ -2153,7 +2153,7 @@ function OPSGROUP:GetExpectedSpeed() else return self.speedWp or 0 end - + end --- Remove a waypoint with a ceratin UID. @@ -2163,9 +2163,9 @@ end function OPSGROUP:RemoveWaypointByID(uid) local index=self:GetWaypointIndex(uid) - + if index then - self:RemoveWaypoint(index) + self:RemoveWaypoint(index) end return self @@ -2178,10 +2178,10 @@ end function OPSGROUP:RemoveWaypoint(wpindex) if self.waypoints then - + -- Number of waypoints before delete. local N=#self.waypoints - + -- Remove waypoint marker. local wp=self:GetWaypoint(wpindex) if wp and wp.marker then @@ -2190,22 +2190,22 @@ function OPSGROUP:RemoveWaypoint(wpindex) -- Remove waypoint. table.remove(self.waypoints, wpindex) - + -- Number of waypoints after delete. local n=#self.waypoints - + -- Debug info. self:T(self.lid..string.format("Removing waypoint index %d, current wp index %d. N %d-->%d", wpindex, self.currentwp, N, n)) - + -- Waypoint was not reached yet. if wpindex > self.currentwp then - + --- -- Removed a FUTURE waypoint --- - + -- TODO: patrol adinfinitum. - + if self.currentwp>=n then self.passedfinalwp=true end @@ -2213,29 +2213,29 @@ function OPSGROUP:RemoveWaypoint(wpindex) self:_CheckGroupDone(1) else - + --- -- Removed a waypoint ALREADY PASSED --- - + -- If an already passed waypoint was deleted, we do not need to update the route. - + -- If current wp = 1 it stays 1. Otherwise decrease current wp. - + if self.currentwp==1 then - + if self.adinfinitum then self.currentwp=#self.waypoints else self.currentwp=1 end - + else self.currentwp=self.currentwp-1 end - + end - + end return self @@ -2255,49 +2255,49 @@ function OPSGROUP:OnEventBirth(EventData) local unit=EventData.IniUnit local group=EventData.IniGroup local unitname=EventData.IniUnitName - + if self.respawning then - + self:I(self.lid.."Respawning unit "..tostring(unitname)) - + local function reset() self.respawning=nil self:_CheckGroupDone() end - + -- Reset switch in 1 sec. This should allow all birth events of n>1 groups to have passed. -- TODO: Can I do this more rigorously? self:ScheduleOnce(1, reset) - + else -- Set homebase if not already set. if self.isFlightgroup then - + if EventData.Place then self.homebase=self.homebase or EventData.Place self.currbase=EventData.Place else self.currbase=nil end - + if self.homebase and not self.destbase then self.destbase=self.homebase end - + self:T(self.lid..string.format("EVENT: Element %s born at airbase %s==> spawned", unitname, self.homebase and self.homebase:GetName() or "unknown")) else - self:T3(self.lid..string.format("EVENT: Element %s born ==> spawned", unitname)) + self:T3(self.lid..string.format("EVENT: Element %s born ==> spawned", unitname)) end - + -- Get element. local element=self:GetElementByName(unitname) - -- Set element to spawned state. + -- Set element to spawned state. self:ElementSpawned(element) - - end - + + end + end end @@ -2310,7 +2310,7 @@ function OPSGROUP:OnEventDead(EventData) -- Check that this is the right group. if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then self:T(self.lid..string.format("EVENT: Unit %s dead!", EventData.IniUnitName)) - + local unit=EventData.IniUnit local group=EventData.IniGroup local unitname=EventData.IniUnitName @@ -2322,7 +2322,7 @@ function OPSGROUP:OnEventDead(EventData) self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed", element.name)) self:ElementDestroyed(element) end - + end end @@ -2357,15 +2357,15 @@ function OPSGROUP:OnEventKill(EventData) -- Check that this is the right group. if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then - + -- Target name local targetname=tostring(EventData.TgtUnitName) - + -- Debug info. self:T2(self.lid..string.format("EVENT: Unit %s killed object %s!", tostring(EventData.IniUnitName), targetname)) - + -- Check if this was a UNIT or STATIC object. - local target=UNIT:FindByName(targetname) + local target=UNIT:FindByName(targetname) if not target then target=STATIC:FindByName(targetname, false) end @@ -2378,15 +2378,15 @@ function OPSGROUP:OnEventKill(EventData) -- Kill counter. self.Nkills=self.Nkills+1 - + -- Check if on a mission. local mission=self:GetMissionCurrent() if mission then mission.Nkills=mission.Nkills+1 -- Increase mission kill counter. end - + end - + end end @@ -2402,16 +2402,16 @@ end function OPSGROUP:SetTask(DCSTask) if self:IsAlive() then - + if self.taskcurrent>0 then - + -- TODO: Why the hell did I do this? It breaks scheduled tasks. I comment it out for now to see where it fails. --local task=self:GetTaskCurrent() --self:RemoveTask(task) --self.taskcurrent=0 - + end - + -- Inject enroute tasks. if self.taskenroute and #self.taskenroute>0 then if tostring(DCSTask.id)=="ComboTask" then @@ -2421,14 +2421,14 @@ function OPSGROUP:SetTask(DCSTask) else local tasks=UTILS.DeepCopy(self.taskenroute) table.insert(tasks, DCSTask) - + DCSTask=self.group.TaskCombo(self, tasks) end end - + -- Set task. self.group:SetTask(DCSTask) - + -- Debug info. local text=string.format("SETTING Task %s", tostring(DCSTask.id)) if tostring(DCSTask.id)=="ComboTask" then @@ -2436,9 +2436,9 @@ function OPSGROUP:SetTask(DCSTask) text=text..string.format("\n[%d] %s", i, tostring(task.id)) end end - self:T(self.lid..text) + self:T(self.lid..text) end - + return self end @@ -2449,10 +2449,10 @@ end function OPSGROUP:PushTask(DCSTask) if self:IsAlive() then - + -- Push task. self.group:PushTask(DCSTask) - + -- Debug info. local text=string.format("PUSHING Task %s", tostring(DCSTask.id)) if tostring(DCSTask.id)=="ComboTask" then @@ -2460,9 +2460,9 @@ function OPSGROUP:PushTask(DCSTask) text=text..string.format("\n[%d] %s", i, tostring(task.id)) end end - self:T(self.lid..text) + self:T(self.lid..text) end - + return self end @@ -2492,7 +2492,7 @@ function OPSGROUP:AddTask(task, clock, description, prio, duration) -- Add to table. table.insert(self.taskqueue, newtask) - + -- Info. self:T(self.lid..string.format("Adding SCHEDULED task %s starting at %s", newtask.description, UTILS.SecondsToClock(newtask.time, true))) self:T3({newtask=newtask}) @@ -2527,14 +2527,14 @@ function OPSGROUP:NewTaskScheduled(task, clock, description, prio, duration) local newtask={} --#OPSGROUP.Task newtask.status=OPSGROUP.TaskStatus.SCHEDULED newtask.dcstask=task - newtask.description=description or task.id + newtask.description=description or task.id newtask.prio=prio or 50 newtask.time=time newtask.id=self.taskcounter newtask.duration=duration newtask.waypoint=-1 newtask.type=OPSGROUP.TaskType.SCHEDULED - newtask.stopflag=USERFLAG:New(string.format("%s StopTaskFlag %d", self.groupname, newtask.id)) + newtask.stopflag=USERFLAG:New(string.format("%s StopTaskFlag %d", self.groupname, newtask.id)) newtask.stopflag:Set(0) return newtask @@ -2544,15 +2544,15 @@ end -- @param #OPSGROUP self -- @param #table task DCS task table structure. -- @param #OPSGROUP.Waypoint Waypoint where the task is executed. Default is the at *next* waypoint. --- @param #string description Brief text describing the task, e.g. "Attack SAM". +-- @param #string description Brief text describing the task, e.g. "Attack SAM". -- @param #number prio Priority of the task. Number between 1 and 100. Default is 50. -- @param #number duration Duration before task is cancelled in seconds counted after task started. Default never. -- @return #OPSGROUP.Task The task structure. function OPSGROUP:AddTaskWaypoint(task, Waypoint, description, prio, duration) - + -- Waypoint of task. Waypoint=Waypoint or self:GetWaypointNext() - + if Waypoint then -- Increase counter. @@ -2569,22 +2569,22 @@ function OPSGROUP:AddTaskWaypoint(task, Waypoint, description, prio, duration) newtask.time=0 newtask.waypoint=Waypoint.uid newtask.type=OPSGROUP.TaskType.WAYPOINT - newtask.stopflag=USERFLAG:New(string.format("%s StopTaskFlag %d", self.groupname, newtask.id)) + newtask.stopflag=USERFLAG:New(string.format("%s StopTaskFlag %d", self.groupname, newtask.id)) newtask.stopflag:Set(0) - + -- Add to table. table.insert(self.taskqueue, newtask) - + -- Info. self:T(self.lid..string.format("Adding WAYPOINT task %s at WP ID=%d", newtask.description, newtask.waypoint)) self:T3({newtask=newtask}) - + -- Update route. self:__UpdateRoute(-1) - - return newtask + + return newtask end - + return nil end @@ -2596,7 +2596,7 @@ function OPSGROUP:AddTaskEnroute(task) if not self.taskenroute then self.taskenroute={} end - + -- Check not to add the same task twice! local gotit=false for _,Task in pairs(self.taskenroute) do @@ -2605,11 +2605,11 @@ function OPSGROUP:AddTaskEnroute(task) break end end - + if not gotit then table.insert(self.taskenroute, task) end - + end --- Get the unfinished waypoint tasks @@ -2618,7 +2618,7 @@ end -- @return #table Table of tasks. Table could also be empty {}. function OPSGROUP:GetTasksWaypoint(id) - -- Tasks table. + -- Tasks table. local tasks={} -- Sort queue. @@ -2631,7 +2631,7 @@ function OPSGROUP:GetTasksWaypoint(id) table.insert(tasks, task) end end - + return tasks end @@ -2641,7 +2641,7 @@ end -- @return #number Number of waypoint tasks. function OPSGROUP:CountTasksWaypoint(id) - -- Tasks table. + -- Tasks table. local n=0 -- Look for first task that SCHEDULED. @@ -2651,7 +2651,7 @@ function OPSGROUP:CountTasksWaypoint(id) n=n+1 end end - + return n end @@ -2665,7 +2665,7 @@ function OPSGROUP:_SortTaskQueue() local taskB=b --#OPSGROUP.Task return (taskA.prio0 then self:TaskCancel() end - + -- Set current task. self.taskcurrent=Task.id - -- + -- if self:GetTaskCurrent()==nil then table.insert(self.taskqueue, Task) end - + -- Set time stamp. Task.timestamp=timer.getAbsTime() -- Task status executing. Task.status=OPSGROUP.TaskStatus.EXECUTING - + if Task.dcstask.id=="Formation" then -- Set of group(s) to follow Mother. local followSet=SET_GROUP:New():AddGroup(self.group) - + local param=Task.dcstask.params - + local followUnit=UNIT:FindByName(param.unitname) - + -- Define AI Formation object. Task.formation=AI_FORMATION:New(followUnit, followSet, "Formation", "Follow X at given parameters.") - + -- Formation parameters. Task.formation:FormationCenterWing(-param.offsetX, 50, math.abs(param.altitude), 50, param.offsetZ, 50) - + -- Set follow time interval. Task.formation:SetFollowTimeInterval(param.dtFollow) - + -- Formation mode. Task.formation:SetFlightModeFormation(self.group) - + -- Start formation FSM. - Task.formation:Start() + Task.formation:Start() elseif Task.dcstask.id=="PatrolZone" then - + --- -- Task patrol zone. --- - + -- Parameters. - local zone=Task.dcstask.params.zone --Core.Zone#ZONE - local Coordinate=zone:GetRandomCoordinate() - local Speed=UTILS.KmphToKnots(Task.dcstask.params.speed or self.speedCruise) + local zone=Task.dcstask.params.zone --Core.Zone#ZONE + local Coordinate=zone:GetRandomCoordinate() + local Speed=UTILS.KmphToKnots(Task.dcstask.params.speed or self.speedCruise) local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude) or nil - -- New waypoint. + -- New waypoint. if self.isFlightgroup then FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) elseif self.isNavygroup then @@ -2871,37 +2871,37 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- If task is scheduled (not waypoint) set task. if Task.type==OPSGROUP.TaskType.SCHEDULED then - + local DCStasks={} if Task.dcstask.id=='ComboTask' then -- Loop over all combo tasks. for TaskID, Task in ipairs(Task.dcstask.params.tasks) do table.insert(DCStasks, Task) - end + end else table.insert(DCStasks, Task.dcstask) end - + -- Combo task. local TaskCombo=self.group:TaskCombo(DCStasks) - - -- Stop condition! + + -- Stop condition! local TaskCondition=self.group:TaskCondition(nil, Task.stopflag:GetName(), 1, nil, Task.duration) - - -- Controlled task. + + -- Controlled task. local TaskControlled=self.group:TaskControlled(TaskCombo, TaskCondition) - + -- Task done. local TaskDone=self.group:TaskFunction("OPSGROUP._TaskDone", self, Task) - + -- Final task. local TaskFinal=self.group:TaskCombo({TaskControlled, TaskDone}) - + -- Set task for group. self:SetTask(TaskFinal) - + end - + end -- Get mission of this task (if any). @@ -2910,7 +2910,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- Set AUFTRAG status. self:MissionExecute(Mission) end - + end --- On after "TaskCancel" event. Cancels the current task or simply sets the status to DONE if the task is not the current one. @@ -2920,28 +2920,28 @@ end -- @param #string To To state. -- @param #OPSGROUP.Task Task The task to cancel. Default is the current task (if any). function OPSGROUP:onafterTaskCancel(From, Event, To, Task) - + -- Get current task. local currenttask=self:GetTaskCurrent() - + -- If no task, we take the current task. But this could also be *nil*! Task=Task or currenttask - + if Task then - + -- Check if the task is the current task? if currenttask and Task.id==currenttask.id then - + -- Current stop flag value. I noticed cases, where setting the flag to 1 would not cancel the task, e.g. when firing HARMS on a dead ship. local stopflag=Task.stopflag:Get() - + -- Debug info. local text=string.format("Current task %s ID=%d cancelled (flag %s=%d)", Task.description, Task.id, Task.stopflag:GetName(), stopflag) self:T(self.lid..text) - + -- Set stop flag. When the flag is true, the _TaskDone function is executed and calls :TaskDone() Task.stopflag:Set(1) - + local done=false if Task.dcstask.id=="Formation" then Task.formation:Stop() @@ -2952,28 +2952,28 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task) -- Manual call TaskDone if setting flag to one was not successful. done=true end - + if done then self:TaskDone(Task) end - + else - + -- Debug info. self:T(self.lid..string.format("TaskCancel: Setting task %s ID=%d to DONE", Task.description, Task.id)) - - -- Call task done function. + + -- Call task done function. self:TaskDone(Task) end - + else - + local text=string.format("WARNING: No (current) task to cancel!") self:E(self.lid..text) - + end - + end --- On before "TaskDone" event. Deny transition if task status is PAUSED. @@ -3009,22 +3009,22 @@ function OPSGROUP:onafterTaskDone(From, Event, To, Task) if Task.id==self.taskcurrent then self.taskcurrent=0 end - + -- Task status done. Task.status=OPSGROUP.TaskStatus.DONE - + -- Restore old ROE. if Task.backupROE then self:SwitchROE(Task.backupROE) end - + -- Check if this task was the task of the current mission ==> Mission Done! local Mission=self:GetMissionByTaskID(Task.id) - + if Mission and Mission:IsNotOver() then - - local status=Mission:GetGroupStatus(self) - + + local status=Mission:GetGroupStatus(self) + if status~=AUFTRAG.GroupStatus.PAUSED then self:T(self.lid.."Task Done ==> Mission Done!") self:MissionDone(Mission) @@ -3032,15 +3032,15 @@ function OPSGROUP:onafterTaskDone(From, Event, To, Task) --Mission paused. Do nothing! end else - + if Task.description=="Engage_Target" then self:Disengage() - end - + end + self:T(self.lid.."Task Done but NO mission found ==> _CheckGroupDone in 1 sec") self:_CheckGroupDone(1) end - + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -3052,27 +3052,27 @@ end -- @param Ops.Auftrag#AUFTRAG Mission Mission for this group. -- @return #OPSGROUP self function OPSGROUP:AddMission(Mission) - + -- Add group to mission. Mission:AddOpsGroup(self) - + -- Set group status to SCHEDULED.. Mission:SetGroupStatus(self, AUFTRAG.GroupStatus.SCHEDULED) - + -- Set mission status to SCHEDULED. Mission:Scheduled() - + -- Add elements. Mission.Nelements=Mission.Nelements+#self.elements -- Add mission to queue. table.insert(self.missionqueue, Mission) - + -- Info text. - local text=string.format("Added %s mission %s starting at %s, stopping at %s", + local text=string.format("Added %s mission %s starting at %s, stopping at %s", tostring(Mission.type), tostring(Mission.name), UTILS.SecondsToClock(Mission.Tstart, true), Mission.Tstop and UTILS.SecondsToClock(Mission.Tstop, true) or "INF") self:T(self.lid..text) - + return self end @@ -3084,22 +3084,22 @@ function OPSGROUP:RemoveMission(Mission) for i,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG - + if mission.auftragsnummer==Mission.auftragsnummer then - + -- Remove mission waypoint task. local Task=Mission:GetGroupWaypointTask(self) - + if Task then self:RemoveTask(Task) end - + -- Remove mission from queue. table.remove(self.missionqueue, i) - + return self end - + end return self @@ -3115,19 +3115,19 @@ function OPSGROUP:CountRemainingMissison() -- Loop over mission queue. for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG - + if mission and mission:IsNotOver() then - + -- Get group status. local status=mission:GetGroupStatus(self) - + if status~=AUFTRAG.GroupStatus.DONE and status~=AUFTRAG.GroupStatus.CANCELLED then N=N+1 end - + end end - + return N end @@ -3141,16 +3141,16 @@ function OPSGROUP:CountRemainingTransports() -- Loop over mission queue. for _,_transport in pairs(self.cargoqueue) do local transport=_transport --Ops.OpsTransport#OPSTRANSPORT - + -- Debug info. self:T(self.lid..string.format("Transport status=%s [%s]", transport:GetCarrierTransportStatus(self), transport:GetState())) - + -- Count not delivered (executing or scheduled) assignments. if transport and transport:GetCarrierTransportStatus(self)==OPSTRANSPORT.Status.SCHEDULED and transport:GetState()~=OPSTRANSPORT.Status.DELIVERED then - N=N+1 + N=N+1 end end - + return N end @@ -3179,14 +3179,14 @@ function OPSGROUP:_GetNextMission() return (taskA.prioweapondata.RangeMax then - + local d=(dist-weapondata.RangeMax)*1.1 - + -- New waypoint coord. waypointcoord=self:GetCoordinate():Translate(d, heading) - + self:T(self.lid..string.format("Out of max range = %.1f km for weapon %d", weapondata.RangeMax/1000, mission.engageWeaponType)) elseif dist Trigger "DetectedUnitKnown" event. self:DetectedUnitKnown(Unit) @@ -3924,10 +3924,10 @@ end -- @param #string To To state. -- @param Wrapper.Unit#UNIT Unit The detected unit. function OPSGROUP:onafterDetectedUnitNew(From, Event, To, Unit) - + -- Debug info. self:T(self.lid..string.format("Detected New unit %s", Unit:GetName())) - + -- Add unit to detected unit set. self.detectedunits:AddUnit(Unit) end @@ -3945,7 +3945,7 @@ function OPSGROUP:onafterDetectedGroup(From, Event, To, Group) -- Debug info. self:T(self.lid..string.format("Detected group %s", groupname)) - + if self.detectedgroups:FindGroup(groupname) then -- Group is already in the detected set ==> Trigger "DetectedGroupKnown" event. self:DetectedGroupKnown(Group) @@ -3953,7 +3953,7 @@ function OPSGROUP:onafterDetectedGroup(From, Event, To, Group) -- Group is was not detected ==> Trigger "DetectedGroupNew" event. self:DetectedGroupNew(Group) end - + end --- On after "DetectedGroupNew" event. Add newly detected group to detected group set. @@ -3966,7 +3966,7 @@ function OPSGROUP:onafterDetectedGroupNew(From, Event, To, Group) -- Debug info. self:T(self.lid..string.format("Detected New group %s", Group:GetName())) - + -- Add unit to detected unit set. self.detectedgroups:AddGroup(Group) end @@ -4010,38 +4010,38 @@ function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) if Target then - -- Target specified ==> set target. + -- Target specified ==> set target. self:SetLaserTarget(Target) - + else -- No target specified. self:E(self.lid.."ERROR: No target provided for LASER!") return false end - + -- Get the first element alive. local element=self:GetElementAlive() - + if element then - + -- Set element. - self.spot.element=element - + self.spot.element=element + -- Height offset. No offset for aircraft. We take the height for ground or naval. local offsetY=0 if self.isGround or self.isNaval then offsetY=element.height end - + -- Local offset of the LASER source. self.spot.offset={x=0, y=offsetY, z=0} - + -- Check LOS. if self.spot.CheckLOS then - + -- Check LOS. local los=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) - + --self:I({los=los, coord=self.spot.Coordinate, offset=self.spot.offset}) if los then @@ -4052,9 +4052,9 @@ function OPSGROUP:onbeforeLaserOn(From, Event, To, Target) self:__LaserOn(-10, Target) return false end - + end - + else self:E(self.lid.."ERROR: No element alive for lasing") return false @@ -4084,16 +4084,16 @@ function OPSGROUP:onafterLaserOn(From, Event, To, Target) if self.spot.IRon then self.spot.IR=Spot.createInfraRed(DCSunit, self.spot.offset, self.spot.vec3) end - + -- Laser is on. self.spot.On=true - + -- No paused in case it was. self.spot.Paused=false -- Debug message. self:T(self.lid.."Switching LASER on") - + end --- On before "LaserOff" event. Check if LASER is on. @@ -4119,7 +4119,7 @@ function OPSGROUP:onafterLaserOff(From, Event, To) if self.spot.On then self.spot.Laser:destroy() self.spot.IR:destroy() - + -- Set to nil. self.spot.Laser=nil self.spot.IR=nil @@ -4127,13 +4127,13 @@ function OPSGROUP:onafterLaserOff(From, Event, To) -- Stop update timer. self.spot.timer:Stop() - + -- No target unit. self.spot.TargetUnit=nil -- Laser is off. self.spot.On=false - + -- Not paused if it was. self.spot.Paused=false end @@ -4151,17 +4151,17 @@ function OPSGROUP:onafterLaserPause(From, Event, To) -- "Destroy" the laser beam. self.spot.Laser:destroy() self.spot.IR:destroy() - + -- Set to nil. self.spot.Laser=nil self.spot.IR=nil -- Laser is off. self.spot.On=false - + -- Laser is paused. self.spot.Paused=true - + end --- On before "LaserResume" event. @@ -4182,12 +4182,12 @@ function OPSGROUP:onafterLaserResume(From, Event, To) -- Debug info. self:T(self.lid.."Resuming LASER") - + -- Unset paused. self.spot.Paused=false -- Set target. - local target=nil + local target=nil if self.spot.TargetType==0 then target=self.spot.Coordinate elseif self.spot.TargetType==1 or self.spot.TargetType==2 then @@ -4201,7 +4201,7 @@ function OPSGROUP:onafterLaserResume(From, Event, To) -- Debug message. self:T(self.lid.."Switching LASER on again") - + self:LaserOn(target) end @@ -4220,16 +4220,16 @@ function OPSGROUP:onafterLaserCode(From, Event, To, Code) -- Debug message. self:T2(self.lid..string.format("Setting LASER Code to %d", self.spot.Code)) - + if self.spot.On then - + -- Debug info. self:T(self.lid..string.format("New LASER Code is %d", self.spot.Code)) - + -- Set LASER code. self.spot.Laser:setCode(self.spot.Code) end - + end --- On after "LaserLostLOS" event. @@ -4241,7 +4241,7 @@ function OPSGROUP:onafterLaserLostLOS(From, Event, To) -- No of sight. self.spot.LOS=false - + -- Lost line of sight. self.spot.lostLOS=true @@ -4249,9 +4249,9 @@ function OPSGROUP:onafterLaserLostLOS(From, Event, To) -- Switch laser off. self:LaserPause() - + end - + end --- On after "LaserGotLOS" event. @@ -4265,7 +4265,7 @@ function OPSGROUP:onafterLaserGotLOS(From, Event, To) self.spot.LOS=true if self.spot.lostLOS then - + -- Did not loose LOS anymore. self.spot.lostLOS=false @@ -4275,7 +4275,7 @@ function OPSGROUP:onafterLaserGotLOS(From, Event, To) end end - + end --- Set LASER target. @@ -4287,17 +4287,17 @@ function OPSGROUP:SetLaserTarget(Target) -- Check object type. if Target:IsInstanceOf("SCENERY") then - + -- Scenery as target. Treat it like a coordinate. Set offset to 1 meter above ground. self.spot.TargetType=0 self.spot.offsetTarget={x=0, y=1, z=0} - + elseif Target:IsInstanceOf("POSITIONABLE") then - + local target=Target --Wrapper.Positionable#POSITIONABLE - + if target:IsAlive() then - + if target:IsInstanceOf("GROUP") then -- We got a GROUP as target. self.spot.TargetGroup=target @@ -4312,37 +4312,37 @@ function OPSGROUP:SetLaserTarget(Target) self.spot.TargetType=2 end end - + -- Get object size. local size,x,y,z=self.spot.TargetUnit:GetObjectSize() - + if y then self.spot.offsetTarget={x=0, y=y*0.75, z=0} else self.spot.offsetTarget={x=0, 2, z=0} end - + else self:E("WARNING: LASER target is not alive!") return end - + elseif Target:IsInstanceOf("COORDINATE") then - + -- Coordinate as target. self.spot.TargetType=0 self.spot.offsetTarget={x=0, y=0, z=0} - + else self:E(self.lid.."ERROR: LASER target should be a POSITIONABLE (GROUP, UNIT or STATIC) or a COORDINATE object!") return end - + -- Set vec3 and account for target offset. self.spot.vec3=UTILS.VecAdd(Target:GetVec3(), self.spot.offsetTarget) - + -- Set coordinate. - self.spot.Coordinate:UpdateFromVec3(self.spot.vec3) + self.spot.Coordinate:UpdateFromVec3(self.spot.vec3) end end @@ -4353,31 +4353,31 @@ function OPSGROUP:_UpdateLaser() -- Check if we have a POSITIONABLE to lase. if self.spot.TargetUnit then - + --- -- Lasing a possibly moving target --- - + if self.spot.TargetUnit:IsAlive() then - -- Get current target position. + -- Get current target position. local vec3=self.spot.TargetUnit:GetVec3() - + -- Add target offset. vec3=UTILS.VecAdd(vec3, self.spot.offsetTarget) - - -- Calculate distance + + -- Calculate distance local dist=UTILS.VecDist3D(vec3, self.spot.vec3) -- Store current position. self.spot.vec3=vec3 - + -- Update beam coordinate. self.spot.Coordinate:UpdateFromVec3(vec3) - + -- Update laser if target moved more than one meter. if dist>1 then - + -- If the laser is ON, set the new laser target point. if self.spot.On then self.spot.Laser:setPoint(vec3) @@ -4385,16 +4385,16 @@ function OPSGROUP:_UpdateLaser() self.spot.IR:setPoint(vec3) end end - + end - + else - + if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive() then - + -- Get first alive unit in the group. local unit=self.spot.TargetGroup:GetHighestThreat() - + if unit then self:T(self.lid..string.format("Switching to target unit %s in the group", unit:GetName())) self.spot.TargetUnit=unit @@ -4404,43 +4404,43 @@ function OPSGROUP:_UpdateLaser() -- Switch laser off. self:T(self.lid.."Target is not alive any more ==> switching LASER off") self:LaserOff() - return + return end - + else - + -- Switch laser off. self:T(self.lid.."Target is not alive any more ==> switching LASER off") self:LaserOff() return end - - end + + end end - + -- Check LOS. if self.spot.CheckLOS then - + -- Check current LOS. local los=self:HasLoS(self.spot.Coordinate, self.spot.element, self.spot.offset) - - if los then - -- Got LOS + + if los then + -- Got LOS if self.spot.lostLOS then --self:I({los=self.spot.LOS, coord=self.spot.Coordinate, offset=self.spot.offset}) self:LaserGotLOS() end - - else - -- No LOS currently + + else + -- No LOS currently if not self.spot.lostLOS then self:LaserLostLOS() - end - + end + end - + end - + end --- On after "ElementInUtero" event. @@ -4451,10 +4451,10 @@ end -- @param #OPSGROUP.Element Element The flight group element. function OPSGROUP:onafterElementInUtero(From, Event, To, Element) self:I(self.lid..string.format("Element in utero %s", Element.name)) - + -- Set element status. self:_UpdateStatus(Element, OPSGROUP.ElementStatus.INUTERO) - + end --- On after "ElementDestroyed" event. @@ -4465,7 +4465,7 @@ end -- @param #OPSGROUP.Element Element The flight group element. function OPSGROUP:onafterElementDestroyed(From, Event, To, Element) self:T(self.lid..string.format("Element destroyed %s", Element.name)) - + -- Cancel all missions. for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG @@ -4473,16 +4473,16 @@ function OPSGROUP:onafterElementDestroyed(From, Event, To, Element) mission:ElementDestroyed(self, Element) end - + -- Increase counter. self.Ndestroyed=self.Ndestroyed+1 - + -- Element is dead. self:ElementDead(Element) -- Set element status. --self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD) - + end --- On after "ElementDead" event. @@ -4493,22 +4493,22 @@ end -- @param #OPSGROUP.Element Element The flight group element. function OPSGROUP:onafterElementDead(From, Event, To, Element) self:I(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime())) - + -- Set element status. self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD) - + -- Check if element was lasing and if so, switch to another unit alive to lase. if self.spot.On and self.spot.element.name==Element.name then - + -- Switch laser off. self:LaserOff() - - -- If there is another element alive, switch laser on again. + + -- If there is another element alive, switch laser on again. if self:GetNelements()>0 then - + -- New target if any. local target=nil - + if self.spot.TargetType==0 then -- Coordinate target=self.spot.Coordinate @@ -4516,39 +4516,39 @@ function OPSGROUP:onafterElementDead(From, Event, To, Element) -- Static or unit if self.spot.TargetUnit and self.spot.TargetUnit:IsAlive() then target=self.spot.TargetUnit - end + end elseif self.spot.TargetType==3 then -- Group if self.spot.TargetGroup and self.spot.TargetGroup:IsAlive() then target=self.spot.TargetGroup - end + end end - + -- Switch laser on again. if target then self:__LaserOn(-1, target) end end end - + -- Check cargo bay and declare cargo groups dead. for groupname, carriername in pairs(self.cargoBay or {}) do if Element.name==carriername then local opsgroup=_DATABASE:GetOpsGroup(groupname) if opsgroup and not (opsgroup:IsDead() or opsgroup:IsStopped()) then for _,element in pairs(opsgroup.elements) do - + -- Debug info. self:T2(self.lid.."Cargo element dead "..element.name) - + -- Trigger dead event. opsgroup:ElementDead(element) - + end end end end - + end --- On after "Respawn" event. @@ -4562,13 +4562,13 @@ function OPSGROUP:onafterRespawn(From, Event, To, Template) self:I(self.lid.."Respawning group!") local template=UTILS.DeepCopy(Template or self.template) - + template.lateActivation=false - + --self.respawning=true - + self:_Respawn(0, template) - + end --- Respawn the group. @@ -4584,25 +4584,25 @@ function OPSGROUP:_Respawn(Delay, Template, Reset) else self:I(self.lid.."FF _Respawn") - + -- Given template or get old. Template=Template or UTILS.DeepCopy(self.template) - + if self:IsAlive() then - + --- -- Group is ALIVE --- - + -- Get units. local units=self.group:GetUnits() - + -- Loop over template units. for UnitID, Unit in pairs(Template.units) do - + for _,_unit in pairs(units) do local unit=_unit --Wrapper.Unit#UNIT - + if unit:GetName()==Unit.name then local vec3=unit:GetVec3() local heading=unit:GetHeading() @@ -4613,12 +4613,12 @@ function OPSGROUP:_Respawn(Delay, Template, Reset) Unit.psi=-Unit.heading end end - + end - + -- Despawn old group. Dont trigger any remove unit event since this is a respawn. self:Despawn(0, true) - + else --- @@ -4630,8 +4630,8 @@ function OPSGROUP:_Respawn(Delay, Template, Reset) -- Loop over template units. for UnitID, Unit in pairs(Template.units) do - local element=self:GetElementByName(Unit.name) - + local element=self:GetElementByName(Unit.name) + if element then local vec3=element.vec3 local heading=element.heading @@ -4641,28 +4641,28 @@ function OPSGROUP:_Respawn(Delay, Template, Reset) Unit.heading=math.rad(heading) Unit.psi=-Unit.heading end - + end - - ]] - + + ]] + end - + -- Debug output. self:I({Template=Template}) - + -- Spawn new group. _DATABASE:Spawn(Template) - + -- Set activation and controlled state. self.isLateActivated=Template.lateActivation self.isUncontrolled=Template.uncontrolled - + -- Reset events. --self:ResetEvents() - + end - + return self end @@ -4694,7 +4694,7 @@ end -- @param #string To To state. function OPSGROUP:onafterDead(From, Event, To) self:T(self.lid..string.format("Group dead at t=%.3f", timer.getTime())) - + -- Is dead now. self.isDead=true @@ -4708,10 +4708,10 @@ function OPSGROUP:onafterDead(From, Event, To) mission:GroupDead(self) end - + -- Delete waypoints so they are re-initialized at the next spawn. self.waypoints=nil - self.groupinitialized=false + self.groupinitialized=false -- Stop in a sec. self:__Stop(-5) @@ -4723,14 +4723,14 @@ end -- @param #string Event Event. -- @param #string To To state. function OPSGROUP:onafterStop(From, Event, To) - + -- Stop check timers. self.timerCheckZone:Stop() self.timerQueueUpdate:Stop() -- Stop FSM scheduler. self.CallScheduler:Clear() - + if self:IsAlive() and not (self:IsDead() or self:IsStopped()) then local life, life0=self:GetLifePoints() local state=self:GetState() @@ -4739,10 +4739,10 @@ function OPSGROUP:onafterStop(From, Event, To) end -- Remove flight from data base. - _DATABASE.FLIGHTGROUPS[self.groupname]=nil + _DATABASE.FLIGHTGROUPS[self.groupname]=nil -- Debug output. - self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from database") + self:I(self.lid.."STOPPED! Unhandled events, cleared scheduler and removed from database") end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -4767,7 +4767,7 @@ function OPSGROUP:_CheckCargoTransport() self:I(self.lid.."Cargo bay:"..text) end end - + -- Cargo queue debug info. if self.verbose>=3 then local text="" @@ -4789,119 +4789,119 @@ function OPSGROUP:_CheckCargoTransport() self:I(self.lid.."Cargo queue:"..text) end end - + -- Check if there is anything in the queue. - if not self.cargoTransport then + if not self.cargoTransport then self.cargoTransport=self:_GetNextCargoTransport() end -- Now handle the transport. - if self.cargoTransport then - + if self.cargoTransport then + -- Debug info. if self.verbose>=2 then - local text=string.format("Carrier [%s]: %s --> %s", self.carrierStatus, self.cargoTransport.pickupzone:GetName(), self.cargoTransport.deployzone:GetName()) - for _,_cargo in pairs(self.cargoTransport.cargos) do - local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup + local text=string.format("Carrier [%s]: %s --> %s", self.carrierStatus, self.cargoTransport.pickupzone:GetName(), self.cargoTransport.deployzone:GetName()) + for _,_cargo in pairs(self.cargoTransport.cargos) do + local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup local name=cargo.opsgroup:GetName() local gstatus=cargo.opsgroup:GetState() local cstatus=cargo.opsgroup.cargoStatus local weight=cargo.opsgroup:GetWeightTotal() local carriergroup, carrierelement, reserved=cargo.opsgroup:_GetMyCarrier() local carrierGroupname=carriergroup and carriergroup.groupname or "none" - local carrierElementname=carrierelement and carrierelement.name or "none" + local carrierElementname=carrierelement and carrierelement.name or "none" text=text..string.format("\n- %s (%.1f kg) [%s]: %s, carrier=%s (%s), delivered=%s", name, weight, gstatus, cstatus, carrierElementname, carrierGroupname, tostring(cargo.delivered)) - end + end self:I(self.lid..text) end - + if self:IsNotCarrier() then - + -- Debug info. self:I(self.lid.."Not carrier ==> pickup") - + -- Initiate the cargo transport process. self:Pickup() - + elseif self:IsPickingup() then - + -- Debug Info. self:T(self.lid.."Picking up...") elseif self:IsLoading() then - + -- Set loading time stamp. --TODO: Check max loading time. If exceeded ==> abort transport. - self.Tloading=self.Tloading or Time - + self.Tloading=self.Tloading or Time + -- Debug info. self:I(self.lid.."Loading...") - + local boarding=false local gotcargo=false - for _,_cargo in pairs(self.cargoTransport.cargos) do + for _,_cargo in pairs(self.cargoTransport.cargos) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - + -- Check if anyone is still boarding. if cargo.opsgroup:IsBoarding(self.groupname) then boarding=true end - + -- Check if we have any cargo to transport. if cargo.opsgroup:IsLoaded(self.groupname) then gotcargo=true - end - + end + end - + -- Boarding finished ==> Transport cargo. if gotcargo and not boarding then self:I(self.lid.."Boarding finished ==> Loaded") self:Loaded() end - + -- No cargo and no one is boarding ==> check again if we can make anyone board. if not gotcargo and not boarding then self:Loading() end - + elseif self:IsTransporting() then - + -- Debug info. self:T(self.lid.."Transporting (nothing to do)") - + elseif self:IsUnloading() then - + -- Debug info. self:I(self.lid.."Unloading ==> Checking if all cargo was delivered") local delivered=true - for _,_cargo in pairs(self.cargoTransport.cargos) do + for _,_cargo in pairs(self.cargoTransport.cargos) do local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup - + local carrierGroup=cargo.opsgroup:_GetMyCarrierGroup() - - -- Check that this group is + + -- Check that this group is if (carrierGroup and carrierGroup:GetName()==self:GetName()) and not cargo.delivered then delivered=false break end - + end - + -- Unloading finished ==> pickup next batch or call it a day. if delivered then self:I(self.lid.."Unloading finished ==> Unloaded") self:Unloaded() else self:Unloading() - end - + end + end - + end - + return self end @@ -4913,12 +4913,12 @@ function OPSGROUP:_AddCargobay(CargoGroup, CarrierElement) --TODO: Check group is not already in cargobay of this carrier or any other carrier. - -- Cargo weight. + -- Cargo weight. local weight=CargoGroup:GetWeightTotal() - - -- Add weight to carrier. + + -- Add weight to carrier. self:AddWeightCargo(CarrierElement.name, weight) - + -- Fill cargo bay. self.cargoBay[CargoGroup.groupname]=CarrierElement.name @@ -4936,17 +4936,17 @@ function OPSGROUP:_DelCargobay(CargoGroup, CarrierElement) -- Not in cargo bay any more. self.cargoBay[CargoGroup.groupname]=nil - + -- Reduce carrier weight. local weight=CargoGroup:GetWeightTotal() - + -- Get carrier of group. local carrier=CargoGroup:_GetMyCarrierElement() - - if carrier then + + if carrier then self:RedWeightCargo(carrier.name, weight) end - + return true end @@ -4971,28 +4971,28 @@ function OPSGROUP:_GetNextCargoTransport() return (transportA.prio=weight then return true end - + end - + end - + return false end ---- Add weight to the internal cargo of an element of the group. +--- Add weight to the internal cargo of an element of the group. -- @param #OPSGROUP self -- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier. -- @return #OPSGROUP.Element Carrier able to transport the cargo. function OPSGROUP:FindCarrierForCargo(CargoGroup) local weight=CargoGroup:GetWeightTotal() - + for _,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element - + local free=self:GetFreeCargobay(element.name) - + if free>=weight then return element else self:T3(self.lid..string.format("%s: Weight %d>%d free cargo bay", element.name, weight, free)) end - + end - + return nil end ---- Reserve cargo space for a cargo group. +--- Reserve cargo space for a cargo group. -- @param #OPSGROUP self -- @param #OPSGROUP CargoGroup Cargo group, which needs a carrier. -- @return #OPSGROUP.Element Carrier able to transport the cargo. function OPSGROUP:ReserveCargoSpace(CargoGroup) local element=self:FindCarrierForCargo(CargoGroup) - + if element then element.reservedCargo=element.reservedCargo or {} table.insert(element.reservedCargo, CargoGroup) end - + return nil end ---- Set my carrier. +--- Set my carrier. -- @param #OPSGROUP self -- @param #OPSGROUP CarrierGroup Carrier group. -- @param #OPSGROUP.Element CarrierElement Carrier element. @@ -5354,7 +5354,7 @@ function OPSGROUP:_GetMyCarrier() end ---- Remove my carrier. +--- Remove my carrier. -- @param #OPSGROUP self -- @return #OPSGROUP self function OPSGROUP:_RemoveMyCarrier() @@ -5373,111 +5373,111 @@ end function OPSGROUP:onafterPickup(From, Event, To) -- Debug info. self:I(self.lid..string.format("New carrier status: %s --> %s", self.carrierStatus, OPSGROUP.CarrierStatus.PICKUP)) - + -- Set carrier status. self.carrierStatus=OPSGROUP.CarrierStatus.PICKUP - + -- Pickup zone. local Zone=self.cargoTransport.pickupzone - + -- Check if already in the pickup zone. local inzone=Zone:IsCoordinateInZone(self:GetCoordinate()) - + local airbasePickup=nil --Wrapper.Airbase#AIRBASE if Zone and Zone:IsInstanceOf("ZONE_AIRBASE") then airbasePickup=Zone:GetAirbase() end - + -- Check if group is already ready for loading. - local ready4loading=false + local ready4loading=false if self:IsArmygroup() or self:IsNavygroup() then ready4loading=inzone else - + -- Aircraft is already parking at the pickup airbase. ready4loading=self.currbase and self.currbase:GetName()==Zone:GetName() and self:IsParking() - + -- If a helo is landed in the zone, we also are ready for loading. if ready4loading==false and self.isHelo and self:IsLandedAt() and inzone then ready4loading=true end - end - + end + if ready4loading then -- We are already in the pickup zone ==> wait and initiate loading. if (self:IsArmygroup() or self:IsNavygroup()) and not self:IsHolding() then self:Wait() end - + -- Start loading. self:__Loading(-5) - + else - + -- Get a random coordinate in the pickup zone and let the carrier go there. local Coordinate=Zone:GetRandomCoordinate() - + -- Add waypoint. if self.isFlightgroup then - + if airbasePickup then - + --- -- Pickup at airbase - --- + --- -- Current airbase. local airbaseCurrent=self.currbase - + if airbaseCurrent then - + -- Activate uncontrolled group. if self:IsParking() then self:StartUncontrolled() end - + else - + -- Order group to land at an airbase. self:LandAtAirbase(airbasePickup) - + end - + elseif self.isHelo then - + --- -- Helo can also land in a zone (NOTE: currently VTOL cannot!) --- - + -- If this is a helo and no ZONE_AIRBASE was given, we make the helo land in the pickup zone. Coordinate:SetAltitude(200) local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate) waypoint.detour=true - + else self:E(self.lid.."ERROR: Carrier aircraft cannot land in Pickup zone! Specify a ZONE_AIRBASE as pickup zone") end elseif self.isNavygroup then - + -- Navy Group - + local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid) ; waypoint.detour=true - + self:__Cruise(-2) - + elseif self.isArmygroup then - + -- Army Group local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid) ; waypoint.detour=true - + self:__Cruise(-2) - - end - + + end + end - + end @@ -5492,83 +5492,83 @@ function OPSGROUP:onafterLoading(From, Event, To) -- Set carrier status. self.carrierStatus=OPSGROUP.CarrierStatus.LOADING - + -- Loading time stamp. self.Tloading=timer.getAbsTime() - + -- Create a temp array and monitor the free cargo space for each element. local cargobay={} for _,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element cargobay[element.name]=element.weightMaxCargo-element.weightCargo - end - + end + --- Find a carrier which can load a given weight. local function _findCarrier(weight) local carrier=nil --#OPSGROUP.Element for _,_element in pairs(self.elements) do - local element=_element --#OPSGROUP.Element + local element=_element --#OPSGROUP.Element if cargobay[element.name]>=weight then return element - end - end + end + end return nil end - + --TODO: sort cargos wrt weight. - + -- Loop over all cargos. for _,_cargo in pairs(self.cargoTransport.cargos) do local cargo=_cargo --#OPSGROUP.CargoGroup - - -- Check that cargo weight is + + -- Check that cargo weight is if self:CanCargo(cargo.opsgroup) and not (cargo.delivered or cargo.opsgroup:IsDead()) then -- Check that group is not cargo already and not busy. - -- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport() + -- TODO: Need a better :IsBusy() function or :IsReadyForMission() :IsReadyForBoarding() :IsReadyForTransport() if cargo.opsgroup:IsNotCargo() and not (cargo.opsgroup:IsPickingup() or cargo.opsgroup:IsLoading() or cargo.opsgroup:IsTransporting() or cargo.opsgroup:IsUnloading() or cargo.opsgroup:IsLoaded()) then - + -- Check if cargo is in embark/pickup zone. local inzone=self.cargoTransport.embarkzone:IsCoordinateInZone(cargo.opsgroup:GetCoordinate()) - + -- Cargo MUST be inside zone or it will not be loaded! if inzone then - + -- Weight of cargo. local weight=cargo.opsgroup:GetWeightTotal() - + -- Find a carrier that has enough free cargo bay for this group. local carrier=_findCarrier(weight) - + if carrier then - + -- Decrease free cargo bay. cargobay[carrier.name]=cargobay[carrier.name]-weight - + -- Set cargo status. cargo.opsgroup.cargoStatus=OPSGROUP.CargoStatus.ASSIGNED - + -- Order cargo group to board the carrier. - cargo.opsgroup:Board(self, carrier) - + cargo.opsgroup:Board(self, carrier) + else -- Debug info. - self:T(self.lid.."Cannot board carrier!") + self:T(self.lid.."Cannot board carrier!") end - + else -- Debug info. self:T(self.lid.."Cargo NOT in embark zone "..self.cargoTransport.embarkzone:GetName()) end end - + else -- Debug info. self:T3(self.lid.."Cargo already delivered, is dead or carrier cannot") end - + end end @@ -5576,7 +5576,7 @@ end --- Clear waypoints. -- @param #OPSGROUP self function OPSGROUP:ClearWaypoints() - -- Clear all waypoints. + -- Clear all waypoints. for i=1,#self.waypoints do table.remove(self.waypoints, i) end @@ -5593,10 +5593,10 @@ end function OPSGROUP:onafterLoad(From, Event, To, CargoGroup, Carrier) -- Debug info. self:I(self.lid..string.format("Loading group %s", tostring(CargoGroup.groupname))) - + -- Carrier element. local carrier=Carrier or CargoGroup:_GetMyCarrierElement() --#OPSGROUP.Element - + -- No carrier provided. if not carrier then -- Try to find a carrier manually. @@ -5605,33 +5605,33 @@ function OPSGROUP:onafterLoad(From, Event, To, CargoGroup, Carrier) if carrier then - -- Add into carrier bay. + -- Add into carrier bay. self:_AddCargobay(CargoGroup, carrier) - + --- -- Embark Cargo --- -- Debug info. CargoGroup:I(CargoGroup.lid..string.format("New cargo status: %s --> %s", CargoGroup.cargoStatus, OPSGROUP.CargoStatus.LOADED)) - + -- Set cargo status. CargoGroup.cargoStatus=OPSGROUP.CargoStatus.LOADED - - -- Clear all waypoints. + + -- Clear all waypoints. CargoGroup:ClearWaypoints() - + -- Set carrier (again). CargoGroup:_SetMyCarrier(self, carrier, false) - + -- Despawn this group. if CargoGroup:IsAlive() then CargoGroup:Despawn(0, true) end - + -- Trigger embarked event. - CargoGroup:Embarked(self, Carrier) - + CargoGroup:Embarked(self, Carrier) + else self:E(self.lid.."ERROR: Cargo has no carrier on Load event!") end @@ -5647,16 +5647,16 @@ function OPSGROUP:onafterLoaded(From, Event, To) -- Debug info. self:T(self.lid.."Carrier Loaded ==> Transport") - + -- Cancel landedAt task. if self:IsFlightgroup() and self:IsLandedAt() then local Task=self:GetTaskCurrent() self:TaskCancel(Task) end - + -- Order group to transport. self:__Transport(1) - + end --- On after "Transport" event. @@ -5669,50 +5669,50 @@ function OPSGROUP:onafterTransport(From, Event, To) self:I(self.lid..string.format("New carrier status: %s --> %s", self.carrierStatus, OPSGROUP.CarrierStatus.TRANSPORTING)) -- Set carrier status. - self.carrierStatus=OPSGROUP.CarrierStatus.TRANSPORTING - + self.carrierStatus=OPSGROUP.CarrierStatus.TRANSPORTING + --TODO: This is all very similar to the onafterPickup() function. Could make it general. - + -- Deploy zone. local Zone=self.cargoTransport.deployzone - + -- Check if already in deploy zone. local inzone=Zone:IsCoordinateInZone(self:GetCoordinate()) - + local airbaseDeploy=nil --Wrapper.Airbase#AIRBASE if Zone and Zone:IsInstanceOf("ZONE_AIRBASE") then airbaseDeploy=Zone:GetAirbase() end - + -- Check if group is already ready for loading. - local ready2deploy=false + local ready2deploy=false if self:IsArmygroup() or self:IsNavygroup() then ready2deploy=inzone else -- Aircraft is already parking at the pickup airbase. ready2deploy=self.currbase and self.currbase:GetName()==Zone:GetName() and self:IsParking() - + -- If a helo is landed in the zone, we also are ready for loading. if ready2deploy==false and (self.isHelo or self.isVTOL) and self:IsLandedAt() and inzone then ready2deploy=true end - end - + end + if inzone then - + -- We are already in the pickup zone ==> wait and initiate unloading. if (self:IsArmygroup() or self:IsNavygroup()) and not self:IsHolding() then self:Wait() end - + -- Start loading. self:__UnLoading(-5) else - + -- Coord where the carrier goes to unload. local Coordinate=nil --Core.Point#COORDINATE - + if self.cargoTransport.carrierGroup and self.cargoTransport.carrierGroup:IsLoading() then -- Coordinate of the new carrier. Coordinate=self.cargoTransport.carrierGroup:GetCoordinate() @@ -5727,65 +5727,65 @@ function OPSGROUP:onafterTransport(From, Event, To) --- -- Deploy at airbase --- - + if airbaseDeploy then local airbaseCurrent=self.currbase - + if airbaseCurrent then - + -- Activate uncontrolled group. if self:IsParking() then self:StartUncontrolled() end - + else -- Order group to land at an airbase. self:LandAtAirbase(airbaseDeploy) end - + elseif self.isHelo or self.isVTOL then - + --- -- Helo or VTOL can also land in a zone --- - + -- If this is a helo and no ZONE_AIRBASE was given, we make the helo land in the pickup zone. Coordinate:SetAltitude(200) local waypoint=FLIGHTGROUP.AddWaypoint(self, Coordinate) waypoint.detour=true - + else self:E(self.lid.."ERROR: Carrier aircraft cannot land in Deploy zone! Specify a ZONE_AIRBASE as deploy zone") end - + elseif self.isArmygroup then - - local path=self.cargoTransport:_GetPathTransport() + + local path=self.cargoTransport:_GetPathTransport() if path then - + for _,_zone in pairs(path.zones:GetSet()) do local zone=_zone --Core.Zone#ZONE local coordinate=zone:GetRandomCoordinate(nil, nil, nil) --TODO: surface type land local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, self:GetWaypointCurrent().uid) ; waypoint.temp=true end - + end - - -- ARMYGROUP + + -- ARMYGROUP local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid) ; waypoint.detour=true - + -- Give cruise command. self:Cruise() - + elseif self.isNavygroup then - - local cwp=self:GetWaypointCurrent() + + local cwp=self:GetWaypointCurrent() local uid=cwp and cwp.uid or nil -- Get a (random) pre-defined transport path. local path=self.cargoTransport:_GetPathTransport() - + if path then -- Loop over zones for i,coordinate in pairs(path) do @@ -5793,17 +5793,17 @@ function OPSGROUP:onafterTransport(From, Event, To) waypoint.temp=true uid=waypoint.uid coordinate:MarkToAll(string.format("Path i=%d, UID=%d", i, uid)) - end + end end -- NAVYGROUP local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate, nil, uid) ; waypoint.detour=true - + -- Give cruise command. self:Cruise() - - end - + + end + end end @@ -5816,117 +5816,117 @@ end function OPSGROUP:onafterUnloading(From, Event, To) -- Debug info. self:I(self.lid..string.format("New carrier status: %s --> %s", self.carrierStatus, OPSGROUP.CarrierStatus.UNLOADING)) - + -- Set carrier status to UNLOADING. self.carrierStatus=OPSGROUP.CarrierStatus.UNLOADING - + -- Deploy zone. local zone=self.cargoTransport.disembarkzone or self.cargoTransport.deployzone --Core.Zone#ZONE - + for _,_cargo in pairs(self.cargoTransport.cargos) do local cargo=_cargo --#OPSGROUP.CargoGroup - + -- Check that cargo is loaded into this group. -- NOTE: Could be that the element carriing this cargo group is DEAD, which would mean that the cargo group is also DEAD. if cargo.opsgroup:IsLoaded(self.groupname) and not cargo.opsgroup:IsDead() then - + -- Disembark to carrier. local needscarrier=false local carrier=nil local carrierGroup=nil - - + + if self.cargoTransport.disembarkCarriers and #self.cargoTransport.disembarkCarriers>0 then - + needscarrier=true - + carrier, carrierGroup=self.cargoTransport:FindTransferCarrierForCargo(cargo.opsgroup, zone) - + --TODO: max unloading time if transfer carrier does not arrive in the zone. - + end - + if needscarrier==false or (needscarrier and carrier and carrierGroup) then - + -- Cargo was delivered (somehow). cargo.delivered=true - + -- Increase number of delivered cargos. self.cargoTransport.Ndelivered=self.cargoTransport.Ndelivered+1 - + if carrier and carrierGroup then - + --- -- Delivered to another carrier group. --- - + -- Unload from this and directly load into the other carrier. self:Unload(cargo.opsgroup) carrierGroup:Load(cargo.opsgroup, carrier) - + elseif zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then - + --- -- Delivered to a ship via helo or VTOL --- - + -- Issue warning. env.info("ERROR: Deploy/disembark zone is a ZONE_AIRBASE of a ship! Where to put the cargo? Dumping into the sea, sorry!") --TODO: Dumb into sea. - + else - + --- -- Delivered to deploy zone - --- - + --- + if self.cargoTransport.disembarkInUtero then - + -- Unload but keep "in utero" (no coordinate provided). self:Unload(cargo.opsgroup) - + else - - local Coordinate=nil - + + local Coordinate=nil + if self.cargoTransport.disembarkzone then - + -- Random coordinate in disembark zone. Coordinate=self.cargoTransport.disembarkzone:GetRandomCoordinate() - + else - + -- Get random point in disembark zone. local zoneCarrier=self:GetElementZoneUnload(cargo.opsgroup:_GetMyCarrierElement().name) - + -- Random coordinate/heading in the zone. Coordinate=zoneCarrier:GetRandomCoordinate() - + end - + -- Random heading of the group. local Heading=math.random(0,359) - + -- Unload to Coordinate. self:Unload(cargo.opsgroup, Coordinate, self.cargoTransport.disembarkActivation, Heading) - + end - + -- Trigger "Unloaded" event for current cargo transport - self.cargoTransport:Unloaded(cargo.opsgroup) - + self.cargoTransport:Unloaded(cargo.opsgroup) + end - + else self:T(self.lid.."Cargo needs carrier but no carrier is avaiable (yet)!") end - - else + + else -- Not loaded or dead end end -- loop over cargos - + end --- On before "Unload" event. @@ -5941,7 +5941,7 @@ function OPSGROUP:onbeforeUnload(From, Event, To, OpsGroup, Coordinate, Heading) -- Remove group from carrier bay. If group is not in cargo bay, function will return false and transition is denied. local removed=self:_DelCargobay(OpsGroup) - + return removed end @@ -5953,63 +5953,63 @@ end -- @param #OPSGROUP OpsGroup The OPSGROUP loaded as cargo. -- @param Core.Point#COORDINATE Coordinate Coordinate were the group is unloaded to. -- @param #boolean Activated If `true`, group is active. If `false`, group is spawned in late activated state. --- @param #number Heading (Optional) Heading of group in degrees. Default is random heading for each unit. +-- @param #number Heading (Optional) Heading of group in degrees. Default is random heading for each unit. function OPSGROUP:onafterUnload(From, Event, To, OpsGroup, Coordinate, Activated, Heading) - + -- Debug info. OpsGroup:I(OpsGroup.lid..string.format("New cargo status: %s --> %s", OpsGroup.cargoStatus, OPSGROUP.CargoStatus.NOTCARGO)) -- Set cargo status. OpsGroup.cargoStatus=OPSGROUP.CargoStatus.NOTCARGO - + --TODO: Unload flightgroup. Find parking spot etc. - + if Coordinate then - + --- -- Respawn at a coordinate. --- -- Template for the respawned group. local Template=UTILS.DeepCopy(OpsGroup.template) --DCS#Template - + -- No late activation. if Activated==false then Template.lateActivation=true else Template.lateActivation=false end - + -- Loop over template units. for _,Unit in pairs(Template.units) do - - local element=OpsGroup:GetElementByName(Unit.name) - + + local element=OpsGroup:GetElementByName(Unit.name) + if element then - + local vec3=element.vec3 - + -- Relative pos vector. local rvec2={x=Unit.x-Template.x, y=Unit.y-Template.y} --DCS#Vec2 - + local cvec2={x=Coordinate.x, y=Coordinate.z} --DCS#Vec2 - + -- Position. Unit.x=cvec2.x+rvec2.x Unit.y=cvec2.y+rvec2.y Unit.alt=land.getHeight({x=Unit.x, y=Unit.y}) - + -- Heading. Unit.heading=Heading and math.rad(Heading) or Unit.heading Unit.psi=-Unit.heading - + end - + end - + -- Respawn group. OpsGroup:_Respawn(0, Template) - + -- Add current waypoint. These have been cleard on loading. if OpsGroup:IsNavygroup() then OpsGroup.currentwp=1 @@ -6017,25 +6017,25 @@ function OPSGROUP:onafterUnload(From, Event, To, OpsGroup, Coordinate, Activated NAVYGROUP.AddWaypoint(OpsGroup, Coordinate, nil, nil, nil, false) elseif OpsGroup:IsArmygroup() then OpsGroup.currentwp=1 - OpsGroup.passedfinalwp=true - ARMYGROUP.AddWaypoint(OpsGroup, Coordinate, nil, nil, nil, false) - end - + OpsGroup.passedfinalwp=true + ARMYGROUP.AddWaypoint(OpsGroup, Coordinate, nil, nil, nil, false) + end + else --- -- Just remove from this carrier. --- - + -- Nothing to do. - + OpsGroup.position=self:GetVec3() - + end - + -- Trigger "Disembarked" event. OpsGroup:Disembarked(OpsGroup:_GetMyCarrierGroup(), OpsGroup:_GetMyCarrierElement()) - + -- Remove my carrier. OpsGroup:_RemoveMyCarrier() @@ -6049,30 +6049,30 @@ end function OPSGROUP:onafterUnloaded(From, Event, To) -- Debug info self:I(self.lid.."Cargo unloaded..") - + -- Cancel landedAt task. if self:IsFlightgroup() and self:IsLandedAt() then local Task=self:GetTaskCurrent() self:TaskCancel(Task) - end - + end + -- Check everything was delivered (or is dead). local delivered=self:_CheckDelivered(self.cargoTransport) - + if not delivered then - + -- Pickup the next batch. self:I(self.lid.."Unloaded: Still cargo left ==> Pickup") self:Pickup(self.cargoTransport.pickupzone) - + else - + -- Everything delivered. self:I(self.lid.."Unloaded: ALL cargo unloaded ==> Delivered (current)") self:Delivered(self.cargoTransport) - + end - + end --- On after "Delivered" event. @@ -6082,13 +6082,13 @@ end -- @param #string To To state. -- @param Ops.OpsTransport#OPSTRANSPORT CargoTransport The cargo transport assignment. function OPSGROUP:onafterDelivered(From, Event, To, CargoTransport) - + -- Check if this was the current transport. if self.cargoTransport and self.cargoTransport.uid==CargoTransport.uid then - + -- Debug info. self:I(self.lid..string.format("New carrier status: %s --> %s", self.carrierStatus, OPSGROUP.CarrierStatus.NOTCARRIER)) - + -- Checks if self:IsPickingup() then -- Delete pickup waypoint? @@ -6103,10 +6103,10 @@ function OPSGROUP:onafterDelivered(From, Event, To, CargoTransport) elseif self:IsUnloading() then -- Nothing to do? end - + -- This is not a carrier anymore. - self.carrierStatus=OPSGROUP.CarrierStatus.NOTCARRIER - + self.carrierStatus=OPSGROUP.CarrierStatus.NOTCARRIER + -- Startup uncontrolled aircraft to allow it to go back. if self:IsFlightgroup() then if self:IsUncontrolled() then @@ -6115,19 +6115,19 @@ function OPSGROUP:onafterDelivered(From, Event, To, CargoTransport) local Task=self:GetTaskCurrent() self:TaskCancel(Task) end - else + else -- Army & Navy: give Cruise command to "wake up" from waiting status. self:__Cruise(0.1) end - + -- Check group done. self:I(self.lid.."All cargo delivered ==> check group done") self:_CheckGroupDone(0.2) - -- No current transport any more. + -- No current transport any more. self.cargoTransport=nil end - + -- Remove cargo transport from cargo queue. self:DelCargoTransport(CargoTransport) @@ -6153,7 +6153,7 @@ function OPSGROUP:onbeforeBoard(From, Event, To, CarrierGroup, Carrier) elseif CarrierGroup:IsDead() then self:I(self.lid.."Carrier Group DEAD ==> Deny Board transition!") self.cargoStatus=OPSGROUP.CargoStatus.NOTCARGO - return false + return false elseif Carrier.status==OPSGROUP.ElementStatus.DEAD then self:I(self.lid.."Carrier Element DEAD ==> Deny Board transition!") self.cargoStatus=OPSGROUP.CargoStatus.NOTCARGO @@ -6173,41 +6173,41 @@ end function OPSGROUP:onafterBoard(From, Event, To, CarrierGroup, Carrier) -- Debug info. self:I(self.lid..string.format("New cargo status: %s --> %s", self.cargoStatus, OPSGROUP.CargoStatus.BOARDING)) - + -- Set cargo status. self.cargoStatus=OPSGROUP.CargoStatus.BOARDING - + -- Set carrier. As long as the group is not loaded, we only reserve the cargo space. self:_SetMyCarrier(CarrierGroup, Carrier, true) - + -- Army or Navy group. local CarrierIsArmyOrNavy=CarrierGroup:IsArmygroup() or CarrierGroup:IsNavygroup() local CargoIsArmyOrNavy=self:IsArmygroup() or self:IsNavygroup() - + -- Check that carrier is standing still. if (CarrierIsArmyOrNavy and CarrierGroup:IsHolding()) or (CarrierGroup:IsParking() or CarrierGroup:IsLandedAt()) then - + -- Board if group is mobile, not late activated and army or navy. Everything else is loaded directly. local board=self.speedMax>0 and CargoIsArmyOrNavy and self:IsAlive() and CarrierGroup:IsAlive() - + -- Armygroup cannot board ship ==> Load directly. if self:IsArmygroup() and CarrierGroup:IsNavygroup() then board=false end - + if board then - + -- Debug info. self:I(self.lid..string.format("Boarding group=%s [%s], carrier=%s", CarrierGroup:GetName(), CarrierGroup:GetState(), Carrier.name)) - + -- TODO: Implement embarkzone. local Coordinate=Carrier.unit:GetCoordinate() - + -- Clear all waypoints. self:ClearWaypoints() - + if self.isArmygroup then - local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate) + local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate) waypoint.detour=true self:Cruise() else @@ -6215,21 +6215,21 @@ function OPSGROUP:onafterBoard(From, Event, To, CarrierGroup, Carrier) waypoint.detour=true self:Cruise() end - + else - + --- -- Direct load into carrier. - --- - + --- + -- Debug info. self:I(self.lid..string.format("Board with direct load to carrier %s", CarrierGroup:GetName())) - + -- Trigger Load event. CarrierGroup:Load(self) - + end - + else self:T(self.lid.."Carrier not ready for boarding yet ==> repeating boarding call in 10 sec") self:__Board(-10, CarrierGroup, Carrier) @@ -6246,37 +6246,37 @@ end function OPSGROUP:_CheckInZones() if self.checkzones and self:IsAlive() then - + local Ncheck=self.checkzones:Count() local Ninside=self.inzones:Count() - + -- Debug info. self:T(self.lid..string.format("Check if group is in %d zones. Currently it is in %d zones.", self.checkzones:Count(), self.inzones:Count())) -- Firstly, check if group is still inside zone it was already in. If not, remove zones and trigger LeaveZone() event. local leftzones={} for inzonename, inzone in pairs(self.inzones:GetSet()) do - + -- Check if group is still inside the zone. local isstillinzone=self.group:IsInZone(inzone) --:IsPartlyOrCompletelyInZone(inzone) - + -- If not, trigger, LeaveZone event. if not isstillinzone then table.insert(leftzones, inzone) - end + end end - + -- Trigger leave zone event. for _,leftzone in pairs(leftzones) do self:LeaveZone(leftzone) end - - + + -- Now, run of all check zones and see if the group entered a zone. local enterzones={} for checkzonename,_checkzone in pairs(self.checkzones:GetSet()) do local checkzone=_checkzone --Core.Zone#ZONE - + -- Is group currtently in this check zone? local isincheckzone=self.group:IsInZone(checkzone) --:IsPartlyOrCompletelyInZone(checkzone) @@ -6284,12 +6284,12 @@ function OPSGROUP:_CheckInZones() table.insert(enterzones, checkzone) end end - + -- Trigger enter zone event. for _,enterzone in pairs(enterzones) do self:EnterZone(enterzone) end - + end end @@ -6309,33 +6309,33 @@ function OPSGROUP:_CheckDetectedUnits() local DetectedObject=Detection.object -- DCS#Object if DetectedObject and DetectedObject:isExist() and DetectedObject.id_<50000000 then - + -- Unit. local unit=UNIT:Find(DetectedObject) - + if unit and unit:IsAlive() then - + -- Name of detected unit local unitname=unit:GetName() - -- Add unit to detected table of this run. + -- Add unit to detected table of this run. table.insert(detected, unit) - + -- Trigger detected unit event ==> This also triggers the DetectedUnitNew and DetectedUnitKnown events. self:DetectedUnit(unit) - + -- Get group of unit. local group=unit:GetGroup() - + -- Add group to table. - if group then - groups[group:GetName()]=group + if group then + groups[group:GetName()]=group end - + end end end - + -- Call detected group event. for groupname, group in pairs(groups) do self:DetectedGroup(group) @@ -6361,7 +6361,7 @@ function OPSGROUP:_CheckDetectedUnits() end end - + -- Remove lost units from detected set. self.detectedunits:RemoveUnitsByName(lost) @@ -6386,7 +6386,7 @@ function OPSGROUP:_CheckDetectedUnits() end end - + -- Remove lost units from detected set. self.detectedgroups:RemoveGroupsByName(lost) @@ -6405,84 +6405,84 @@ function OPSGROUP:_CheckGroupDone(delay) -- Delayed call. self:ScheduleOnce(delay, self._CheckGroupDone, self) else - + -- Group is engaging something. if self:IsEngaging() then self:UpdateRoute() return end - + -- Group is waiting. We deny all updates. if self:IsWaiting() then -- If group is waiting, we assume that is the way it is meant to be. return end - + -- Get current waypoint. local waypoint=self:GetWaypoint(self.currentwp) - + --env.info("FF CheckGroupDone") if waypoint then - + -- Number of tasks remaining for this waypoint. local ntasks=self:CountTasksWaypoint(waypoint.uid) - + -- We only want to update the route if there are no more tasks to be done. if ntasks>0 then self:T(self.lid..string.format("Still got %d tasks for the current waypoint UID=%d ==> RETURN (no action)", ntasks, waypoint.uid)) return end - end - + end + if self.adinfinitum then - + --- -- Parol Ad Infinitum --- if #self.waypoints>0 then - + -- Next waypoint index. local i=self:GetWaypointIndexNext(true) - + -- Get positive speed to first waypoint. local speed=self:GetSpeedToWaypoint(i) - + -- Cruise. self:Cruise(speed) - + -- Debug info. self:T(self.lid..string.format("Adinfinitum=TRUE ==> Goto WP index=%d at speed=%d knots", i, speed)) - + else self:E(self.lid..string.format("WARNING: No waypoints left! Commanding a Full Stop")) - self:__FullStop(-1) + self:__FullStop(-1) end else - + --- -- Finite Patrol --- - + if self.passedfinalwp then - + --- -- Passed FINAL waypoint --- - + -- No further waypoints. Command a full stop. self:__FullStop(-1) - + self:T(self.lid..string.format("Passed final WP, adinfinitum=FALSE ==> Full Stop")) - + else - + --- -- Final waypoint NOT passed yet --- - + if #self.waypoints>0 then self:T(self.lid..string.format("NOT Passed final WP, #WP>0 ==> Update Route")) --self:UpdateRoute() @@ -6491,14 +6491,14 @@ function OPSGROUP:_CheckGroupDone(delay) self:E(self.lid..string.format("WARNING: No waypoints left! Commanding a Full Stop")) self:__FullStop(-1) end - + end - + end - - end + + end end - + end --- Check if group got stuck. @@ -6509,25 +6509,25 @@ function OPSGROUP:_CheckStuck() if self:IsHolding() or self:Is("Rearming") then return end - + -- Current time. local Tnow=timer.getTime() - + -- Expected speed in m/s. local ExpectedSpeed=self:GetExpectedSpeed() - + -- Current speed in m/s. local speed=self:GetVelocity() - + -- Check speed. if speed<0.5 then - + if ExpectedSpeed>0 and not self.stuckTimestamp then self:T2(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected", speed, ExpectedSpeed)) self.stuckTimestamp=Tnow self.stuckVec3=self:GetVec3() end - + else -- Moving (again). self.stuckTimestamp=nil @@ -6535,20 +6535,20 @@ function OPSGROUP:_CheckStuck() -- Somehow we are not moving... if self.stuckTimestamp then - + -- Time we are holding. local holdtime=Tnow-self.stuckTimestamp - + if holdtime>=10*60 then - + self:E(self.lid..string.format("WARNING: Group came to an unexpected standstill. Speed=%.1f<%.1f m/s expected for %d sec", speed, ExpectedSpeed, holdtime)) - + --TODO: Stuck event! - + end - + end - + end @@ -6561,24 +6561,24 @@ function OPSGROUP:_CheckDamage() local damaged=false for _,_element in pairs(self.elements) do local element=_element --Ops.OpsGroup#OPSGROUP.Element - + -- Current life points. local life=element.unit:GetLife() - + self.life=self.life+life - + if life0 then - + -- Get current ammo. local ammo=self:GetAmmoTot() - + -- Check if rearming is completed. if self:IsRearming() then if ammo.Total==self.ammo.Total then self:Rearmed() end - end - + end + -- Total. if self.outofAmmo and ammo.Total>0 then self.outofAmmo=false @@ -6663,14 +6663,14 @@ function OPSGROUP:_CheckAmmoStatus() self.outofMissiles=true self:OutOfMissiles() end - + -- Check if group is engaging. if self:IsEngaging() and ammo.Total==0 then self:Disengage() end end - + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -6684,9 +6684,9 @@ function OPSGROUP:_PrintTaskAndMissionStatus() --- -- Tasks: verbose >= 3 --- - + -- Task queue. - if self.verbose>=3 and #self.taskqueue>0 then + if self.verbose>=3 and #self.taskqueue>0 then local text=string.format("Tasks #%d", #self.taskqueue) for i,_task in pairs(self.taskqueue) do local task=_task --Ops.OpsGroup#OPSGROUP.Task @@ -6716,22 +6716,22 @@ function OPSGROUP:_PrintTaskAndMissionStatus() end self:I(self.lid..text) end - + --- -- Missions: verbose>=2 --- - + -- Current mission name. - if self.verbose>=2 then + if self.verbose>=2 then local Mission=self:GetMissionByID(self.currentmission) - + -- Current status. local text=string.format("Missions %d, Current: %s", self:CountRemainingMissison(), Mission and Mission.name or "none") for i,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG local Cstart= UTILS.SecondsToClock(mission.Tstart, true) local Cstop = mission.Tstop and UTILS.SecondsToClock(mission.Tstop, true) or "INF" - text=text..string.format("\n[%d] %s (%s) status=%s (%s), Time=%s-%s, prio=%d wp=%s targets=%d", + text=text..string.format("\n[%d] %s (%s) status=%s (%s), Time=%s-%s, prio=%d wp=%s targets=%d", i, tostring(mission.name), mission.type, mission:GetGroupStatus(self), tostring(mission.status), Cstart, Cstop, mission.prio, tostring(mission:GetGroupWaypointIndex(self)), mission:CountMissionTargets()) end self:I(self.lid..text) @@ -6748,19 +6748,19 @@ end -- @param #OPSGROUP.Waypoint Waypoint data. -- @return #OPSGROUP.Waypoint Modified waypoint data. function OPSGROUP:_CreateWaypoint(waypoint) - + -- Set uid. waypoint.uid=self.wpcounter - + -- Waypoint has not been passed yet. waypoint.npassed=0 - + -- Coordinate. waypoint.coordinate=COORDINATE:New(waypoint.x, waypoint.alt, waypoint.y) -- Set waypoint name. - waypoint.name=string.format("Waypoint UID=%d", waypoint.uid) - + waypoint.name=string.format("Waypoint UID=%d", waypoint.uid) + -- Set types. waypoint.patrol=false waypoint.detour=false @@ -6768,7 +6768,7 @@ function OPSGROUP:_CreateWaypoint(waypoint) -- Increase UID counter. self.wpcounter=self.wpcounter+1 - + return waypoint end @@ -6786,10 +6786,10 @@ function OPSGROUP:_AddWaypoint(waypoint, wpnumber) -- Debug info. self:T(self.lid..string.format("Adding waypoint at index=%d id=%d", wpnumber, waypoint.uid)) - + -- Now we obviously did not pass the final waypoint. self.passedfinalwp=false - + -- Switch to cruise mode. if self:IsHolding() then -- Disable this for now. Cruise has to be commanded manually now. If group is ordered to hold, it will hold until told to move again. @@ -6805,40 +6805,40 @@ function OPSGROUP:InitWaypoints() -- Template waypoints. self.waypoints0=self.group:GetTemplateRoutePoints() - -- Waypoints + -- Waypoints self.waypoints={} - + for index,wp in pairs(self.waypoints0) do - -- Coordinate of the waypoint. + -- Coordinate of the waypoint. local coordinate=COORDINATE:New(wp.x, wp.alt, wp.y) - + -- Strange! wp.speed=wp.speed or 0 - + -- Speed at the waypoint. local speedknots=UTILS.MpsToKnots(wp.speed) - + if index==1 then self.speedWp=wp.speed end - + -- Add waypoint. self:AddWaypoint(coordinate, speedknots, index-1, nil, false) - + end - + -- Debug info. self:T(self.lid..string.format("Initializing %d waypoints", #self.waypoints)) - + -- Update route. if #self.waypoints>0 then - + -- Check if only 1 wp? if #self.waypoints==1 then self.passedfinalwp=true end - + end return self @@ -6856,29 +6856,29 @@ function OPSGROUP:Route(waypoints, delay) else if self:IsAlive() then - + -- DCS task combo. local Tasks={} - + -- Route (Mission) task. local TaskRoute=self.group:TaskRoute(waypoints) table.insert(Tasks, TaskRoute) - + -- TaskCombo of enroute and mission tasks. local TaskCombo=self.group:TaskCombo(Tasks) - + -- Set tasks. if #Tasks>1 then self:SetTask(TaskCombo) else self:SetTask(TaskRoute) end - + else self:E(self.lid.."ERROR: Group is not alive! Cannot route group.") end end - + return self end @@ -6893,25 +6893,25 @@ function OPSGROUP:_UpdateWaypointTasks(n) local nwaypoints=#waypoints for i,_wp in pairs(waypoints) do - local wp=_wp --Ops.OpsGroup#OPSGROUP.Waypoint - + local wp=_wp --Ops.OpsGroup#OPSGROUP.Waypoint + if i>=n or nwaypoints==1 then - + -- Debug info. self:T2(self.lid..string.format("Updating waypoint task for waypoint %d/%d ID=%d. Last waypoint passed %d", i, nwaypoints, wp.uid, self.currentwp)) - + -- Tasks of this waypoint local taskswp={} - + -- At each waypoint report passing. local TaskPassingWaypoint=self.group:TaskFunction("OPSGROUP._PassingWaypoint", self, wp.uid) - table.insert(taskswp, TaskPassingWaypoint) - + table.insert(taskswp, TaskPassingWaypoint) + -- Waypoint task combo. wp.task=self.group:TaskCombo(taskswp) - + end - + end end @@ -6925,125 +6925,125 @@ end --@param #OPSGROUP opsgroup Ops group object. --@param #number uid Waypoint UID. function OPSGROUP._PassingWaypoint(group, opsgroup, uid) - + -- Get waypoint data. local waypoint=opsgroup:GetWaypointByID(uid) - + if waypoint then - + -- Current wp. local currentwp=opsgroup.currentwp - + -- Get the current waypoint index. opsgroup.currentwp=opsgroup:GetWaypointIndex(uid) - + -- Set expected speed and formation from the next WP. - local wpnext=opsgroup:GetWaypointNext() + local wpnext=opsgroup:GetWaypointNext() if wpnext then - + -- Set formation. if opsgroup.isGround then opsgroup.formation=wpnext.action end - + -- Set speed. opsgroup.speed=wpnext.speed - + end - + -- Debug message. local text=string.format("Group passing waypoint uid=%d", uid) opsgroup:I(opsgroup.lid..text) - + -- Trigger PassingWaypoint event. if waypoint.temp then -- Remove temp waypoint. opsgroup:RemoveWaypointByID(uid) - + if opsgroup:IsNavygroup() or opsgroup:IsArmygroup() then --TODO: not sure if this works with FLIGHTGROUPS opsgroup:Cruise() end - + elseif waypoint.astar then - + -- Remove Astar waypoint. opsgroup:RemoveWaypointByID(uid) - + -- Cruise. opsgroup:Cruise() - + elseif waypoint.detour then - + -- Remove detour waypoint. opsgroup:RemoveWaypointByID(uid) - + if opsgroup:IsRearming() then - + -- Trigger Rearming event. opsgroup:Rearming() - + elseif opsgroup:IsRetreating() then - + -- Trigger Retreated event. opsgroup:Retreated() - + elseif opsgroup:IsPickingup() then - + if opsgroup.isFlightgroup then - + -- Land at current pos and wait for 60 min max. opsgroup:LandAt(opsgroup:GetCoordinate(), 60*60) - + else -- Wait and load cargo. - opsgroup:Wait() + opsgroup:Wait() opsgroup:__Loading(-5) - end - + end + elseif opsgroup:IsTransporting() then if opsgroup.isFlightgroup then - + -- Land at current pos and wait for 60 min max. opsgroup:LandAt(opsgroup:GetCoordinate(), 60*60) - + else -- Stop and unload. opsgroup:Wait() opsgroup:Unloading() end - + elseif opsgroup:IsBoarding() then - + local carrierGroup=opsgroup:_GetMyCarrierGroup() local carrier=opsgroup:_GetMyCarrierElement() - + if carrierGroup and carrierGroup:IsAlive() then - + if carrier and carrier.unit and carrier.unit:IsAlive() then - + -- Load group into the carrier. carrierGroup:Load(opsgroup) - + else opsgroup:E(opsgroup.lid.."ERROR: Group cannot board assigned carrier UNIT as it is NOT alive!") end - + else opsgroup:E(opsgroup.lid.."ERROR: Group cannot board assigned carrier GROUP as it is NOT alive!") end - + elseif opsgroup:IsEngaging() then - + -- Nothing to do really. - + else - + -- Trigger DetourReached event. opsgroup:DetourReached() - + if waypoint.detour==0 then opsgroup:FullStop() elseif waypoint.detour==1 then @@ -7051,19 +7051,19 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid) else opsgroup:E("ERROR: waypoint.detour should be 0 or 1") end - + end - + else -- Check if the group is still pathfinding. if opsgroup.ispathfinding then opsgroup.ispathfinding=false - end + end -- Increase passing counter. - waypoint.npassed=waypoint.npassed+1 - + waypoint.npassed=waypoint.npassed+1 + -- Call event function. opsgroup:PassingWaypoint(waypoint) end @@ -7122,25 +7122,25 @@ end -- @param #string roe ROE of group. Default is value set in `SetDefaultROE` (usually `ENUMS.ROE.ReturnFire`). -- @return #OPSGROUP self function OPSGROUP:SwitchROE(roe) - + if self:IsAlive() or self:IsInUtero() then self.option.ROE=roe or self.optionDefault.ROE - + if self:IsInUtero() then self:T2(self.lid..string.format("Setting current ROE=%d when GROUP is SPAWNED", self.option.ROE)) else - + self.group:OptionROE(self.option.ROE) - + self:T(self.lid..string.format("Setting current ROE=%d (%s)", self.option.ROE, self:_GetROEName(self.option.ROE))) end - - + + else self:E(self.lid.."WARNING: Cannot switch ROE! Group is not alive") end - + return self end @@ -7184,25 +7184,25 @@ end -- @param #string rot ROT of group. Default is value set in `:SetDefaultROT` (usually `ENUMS.ROT.PassiveDefense`). -- @return #OPSGROUP self function OPSGROUP:SwitchROT(rot) - + if self:IsAlive() or self:IsInUtero() then - + self.option.ROT=rot or self.optionDefault.ROT - + if self:IsInUtero() then - self:T2(self.lid..string.format("Setting current ROT=%d when GROUP is SPAWNED", self.option.ROT)) + self:T2(self.lid..string.format("Setting current ROT=%d when GROUP is SPAWNED", self.option.ROT)) else - + self.group:OptionROT(self.option.ROT) - + self:I(self.lid..string.format("Setting current ROT=%d (0=NoReaction, 1=Passive, 2=Evade, 3=ByPass, 4=AllowAbort)", self.option.ROT)) end - + else self:E(self.lid.."WARNING: Cannot switch ROT! Group is not alive") end - + return self end @@ -7224,26 +7224,26 @@ function OPSGROUP:SetDefaultAlarmstate(alarmstate) end --- Set current Alarm State of the group. --- +-- -- * 0 = "Auto" -- * 1 = "Green" -- * 2 = "Red" --- +-- -- @param #OPSGROUP self -- @param #number alarmstate Alarm state of group. Default is 0="Auto". -- @return #OPSGROUP self function OPSGROUP:SwitchAlarmstate(alarmstate) - + if self:IsAlive() or self:IsInUtero() then - + if self.isArmygroup or self.isNavygroup then - + self.option.Alarm=alarmstate or self.optionDefault.Alarm - + if self:IsInUtero() then self:T2(self.lid..string.format("Setting current Alarm State=%d when GROUP is SPAWNED", self.option.Alarm)) else - + if self.option.Alarm==0 then self.group:OptionAlarmStateAuto() elseif self.option.Alarm==1 then @@ -7255,16 +7255,16 @@ function OPSGROUP:SwitchAlarmstate(alarmstate) self.group:OptionAlarmStateAuto() self.option.Alarm=0 end - + self:T(self.lid..string.format("Setting current Alarm State=%d (0=Auto, 1=Green, 2=Red)", self.option.Alarm)) - + end - + end else self:E(self.lid.."WARNING: Cannot switch Alarm State! Group is not alive.") end - + return self end @@ -7284,7 +7284,7 @@ end -- @param #boolean OffSwitch If true, TACAN is off by default. -- @return #OPSGROUP self function OPSGROUP:SetDefaultTACAN(Channel, Morse, UnitName, Band, OffSwitch) - + self.tacanDefault={} self.tacanDefault.Channel=Channel or 74 self.tacanDefault.Morse=Morse or "XXX" @@ -7295,9 +7295,9 @@ function OPSGROUP:SetDefaultTACAN(Channel, Morse, UnitName, Band, OffSwitch) else Band=Band or "X" end - self.tacanDefault.Band=Band - - + self.tacanDefault.Band=Band + + if OffSwitch then self.tacanDefault.On=false else @@ -7315,19 +7315,19 @@ end function OPSGROUP:_SwitchTACAN(Tacan) if Tacan then - + self:SwitchTACAN(Tacan.Channel, Tacan.Morse, Tacan.BeaconName, Tacan.Band) - + else - + if self.tacanDefault.On then self:SwitchTACAN() else self:TurnOffTACAN() end - + end - + end --- Activate/switch TACAN beacon settings. @@ -7340,12 +7340,12 @@ end function OPSGROUP:SwitchTACAN(Channel, Morse, UnitName, Band) if self:IsInUtero() then - + self:T(self.lid..string.format("Switching TACAN to DEFAULT when group is spawned")) self:SetDefaultTACAN(Channel, Morse, UnitName, Band) elseif self:IsAlive() then - + Channel=Channel or self.tacanDefault.Channel Morse=Morse or self.tacanDefault.Morse Band=Band or self.tacanDefault.Band @@ -7364,7 +7364,7 @@ function OPSGROUP:SwitchTACAN(Channel, Morse, UnitName, Band) self:T(self.lid.."WARNING: Could not get TACAN unit. Trying first unit in the group") unit=self:GetUnit(1) end - + if unit and unit:IsAlive() then -- Unit ID. @@ -7372,16 +7372,16 @@ function OPSGROUP:SwitchTACAN(Channel, Morse, UnitName, Band) -- Type local Type=BEACON.Type.TACAN - + -- System - local System=BEACON.System.TACAN + local System=BEACON.System.TACAN if self.isAircraft then System=BEACON.System.TACAN_TANKER_Y end - + -- Tacan frequency. local Frequency=UTILS.TACANToFrequency(Channel, Band) - + -- Activate beacon. unit:CommandActivateBeacon(Type, System, Frequency, UnitID, Channel, Band, true, Morse, true) @@ -7392,10 +7392,10 @@ function OPSGROUP:SwitchTACAN(Channel, Morse, UnitName, Band) self.tacan.BeaconName=unit:GetName() self.tacan.BeaconUnit=unit self.tacan.On=true - - -- Debug info. + + -- Debug info. self:T(self.lid..string.format("Switching TACAN to Channel %d%s Morse %s on unit %s", self.tacan.Channel, self.tacan.Band, tostring(self.tacan.Morse), self.tacan.BeaconName)) - + else self:E(self.lid.."ERROR: Cound not set TACAN! Unit is not alive") end @@ -7442,12 +7442,12 @@ end -- @param #boolean OffSwitch If true, TACAN is off by default. -- @return #OPSGROUP self function OPSGROUP:SetDefaultICLS(Channel, Morse, UnitName, OffSwitch) - + self.iclsDefault={} self.iclsDefault.Channel=Channel or 1 self.iclsDefault.Morse=Morse or "XXX" self.iclsDefault.BeaconName=UnitName - + if OffSwitch then self.iclsDefault.On=false else @@ -7465,17 +7465,17 @@ end function OPSGROUP:_SwitchICLS(Icls) if Icls then - + self:SwitchICLS(Icls.Channel, Icls.Morse, Icls.BeaconName) - + else - + if self.iclsDefault.On then self:SwitchICLS() else self:TurnOffICLS() end - + end end @@ -7489,17 +7489,17 @@ end function OPSGROUP:SwitchICLS(Channel, Morse, UnitName) if self:IsInUtero() then - + self:SetDefaultICLS(Channel,Morse,UnitName) - + self:T2(self.lid..string.format("Switching ICLS to Channel %d Morse %s on unit %s when GROUP is SPAWNED", self.iclsDefault.Channel, tostring(self.iclsDefault.Morse), tostring(self.iclsDefault.BeaconName))) elseif self:IsAlive() then - + Channel=Channel or self.iclsDefault.Channel Morse=Morse or self.iclsDefault.Morse local unit=self:GetUnit(1) --Wrapper.Unit#UNIT - + if UnitName then if type(UnitName)=="number" then unit=self:GetUnit(UnitName) @@ -7507,7 +7507,7 @@ function OPSGROUP:SwitchICLS(Channel, Morse, UnitName) unit=UNIT:FindByName(UnitName) end end - + if not unit then self:T(self.lid.."WARNING: Could not get ICLS unit. Trying first unit in the group") unit=self:GetUnit(1) @@ -7516,11 +7516,11 @@ function OPSGROUP:SwitchICLS(Channel, Morse, UnitName) if unit and unit:IsAlive() then -- Unit ID. - local UnitID=unit:GetID() + local UnitID=unit:GetID() -- Activate beacon. unit:CommandActivateICLS(Channel, UnitID, Morse) - + -- Update info. self.icls.Channel=Channel self.icls.Morse=Morse @@ -7528,10 +7528,10 @@ function OPSGROUP:SwitchICLS(Channel, Morse, UnitName) self.icls.BeaconName=unit:GetName() self.icls.BeaconUnit=unit self.icls.On=true - + -- Debug info. self:T(self.lid..string.format("Switching ICLS to Channel %d Morse %s on unit %s", self.icls.Channel, tostring(self.icls.Morse), self.icls.BeaconName)) - + else self:E(self.lid.."ERROR: Cound not set ICLS! Unit is not alive.") end @@ -7563,7 +7563,7 @@ end -- @param #boolean OffSwitch If true, radio is OFF by default. -- @return #OPSGROUP self function OPSGROUP:SetDefaultRadio(Frequency, Modulation, OffSwitch) - + self.radioDefault={} self.radioDefault.Freq=Frequency or 251 self.radioDefault.Modu=Modulation or radio.modulation.AM @@ -7572,7 +7572,7 @@ function OPSGROUP:SetDefaultRadio(Frequency, Modulation, OffSwitch) else self.radioDefault.On=true end - + return self end @@ -7593,33 +7593,33 @@ end function OPSGROUP:SwitchRadio(Frequency, Modulation) if self:IsInUtero() then - + -- Set default radio. self:SetDefaultRadio(Frequency, Modulation) - + -- Debug info. self:T2(self.lid..string.format("Switching radio to frequency %.3f MHz %s when GROUP is SPAWNED", self.radioDefault.Freq, UTILS.GetModulationName(self.radioDefault.Modu))) - + elseif self:IsAlive() then - + Frequency=Frequency or self.radioDefault.Freq Modulation=Modulation or self.radioDefault.Modu if self.isAircraft and not self.radio.On then self.group:SetOption(AI.Option.Air.id.SILENCE, false) - end - + end + -- Give command self.group:CommandSetFrequency(Frequency, Modulation) - + -- Update current settings. self.radio.Freq=Frequency - self.radio.Modu=Modulation + self.radio.Modu=Modulation self.radio.On=true - + -- Debug info. self:T(self.lid..string.format("Switching radio to frequency %.3f MHz %s", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu))) - + else self:E(self.lid.."ERROR: Cound not set Radio! Group is not alive or not in utero any more") end @@ -7635,13 +7635,13 @@ function OPSGROUP:TurnOffRadio() if self:IsAlive() then if self.isAircraft then - + -- Set group to be silient. self.group:SetOption(AI.Option.Air.id.SILENCE, true) - + -- Radio is off. self.radio.On=false - + self:T(self.lid..string.format("Switching radio OFF")) else self:E(self.lid.."ERROR: Radio can only be turned off for aircraft!") @@ -7659,7 +7659,7 @@ end -- @param #number Formation The formation the groups flies in. -- @return #OPSGROUP self function OPSGROUP:SetDefaultFormation(Formation) - + self.optionDefault.Formation=Formation return self @@ -7672,25 +7672,25 @@ end function OPSGROUP:SwitchFormation(Formation) if self:IsAlive() then - + Formation=Formation or self.optionDefault.Formation - + if self.isAircraft then self.group:SetOption(AI.Option.Air.id.FORMATION, Formation) - + elseif self.isGround then - + -- Polymorphic and overwritten in ARMYGROUP. - + else self:E(self.lid.."ERROR: Formation can only be set for aircraft or ground units!") return self end - + -- Set current formation. self.option.Formation=Formation - + -- Debug info. self:T(self.lid..string.format("Switching formation to %d", self.option.Formation)) @@ -7723,7 +7723,7 @@ end function OPSGROUP:SwitchCallsign(CallsignName, CallsignNumber) if self:IsInUtero() then - + -- Set default callsign. We switch to this when group is spawned. self:SetDefaultCallsign(CallsignName, CallsignNumber) @@ -7738,7 +7738,7 @@ function OPSGROUP:SwitchCallsign(CallsignName, CallsignNumber) -- Debug. self:T(self.lid..string.format("Switching callsign to %d-%d", self.callsign.NumberSquad, self.callsign.NumberGroup)) - + -- Give command to change the callsign. self.group:CommandSetCallsign(self.callsign.NumberSquad, self.callsign.NumberGroup) @@ -7759,39 +7759,39 @@ end function OPSGROUP:_UpdatePosition() if self:IsExist() then - + -- Backup last state to monitor differences. self.positionLast=self.position or self:GetVec3() self.headingLast=self.heading or self:GetHeading() self.orientXLast=self.orientX or self:GetOrientationX() self.velocityLast=self.velocity or self.group:GetVelocityMPS() - + -- Current state. self.position=self:GetVec3() self.heading=self:GetHeading() self.orientX=self:GetOrientationX() self.velocity=self:GetVelocity() - + for _,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element element.vec3=self:GetVec3(element.name) end - + -- Update time. local Tnow=timer.getTime() self.dTpositionUpdate=self.TpositionUpdate and Tnow-self.TpositionUpdate or 0 self.TpositionUpdate=Tnow - + if not self.traveldist then self.traveldist=0 end - -- Travel distance since last check. + -- Travel distance since last check. self.travelds=UTILS.VecNorm(UTILS.VecSubstract(self.position, self.positionLast)) - - -- Add up travelled distance. + + -- Add up travelled distance. self.traveldist=self.traveldist+self.travelds - + end return self @@ -7837,12 +7837,12 @@ function OPSGROUP:_AllSimilarStatus(status) for _,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element - + self:T2(self.lid..string.format("Status=%s, element %s status=%s", status, element.name, element.status)) -- Dead units dont count ==> We wont return false for those. if element.status~=OPSGROUP.ElementStatus.DEAD then - + ---------- -- ALIVE ---------- @@ -7883,7 +7883,7 @@ function OPSGROUP:_AllSimilarStatus(status) element.status==OPSGROUP.ElementStatus.PARKING or element.status==OPSGROUP.ElementStatus.ENGINEON) then return false - end + end elseif status==OPSGROUP.ElementStatus.TAKEOFF then @@ -7905,7 +7905,7 @@ function OPSGROUP:_AllSimilarStatus(status) element.status==OPSGROUP.ElementStatus.SPAWNED or element.status==OPSGROUP.ElementStatus.PARKING or element.status==OPSGROUP.ElementStatus.ENGINEON or - element.status==OPSGROUP.ElementStatus.TAXIING or + element.status==OPSGROUP.ElementStatus.TAXIING or element.status==OPSGROUP.ElementStatus.TAKEOFF) then return false end @@ -7930,7 +7930,7 @@ function OPSGROUP:_AllSimilarStatus(status) end end - + else -- Element is dead. We don't care unless all are dead. end --DEAD @@ -7939,7 +7939,7 @@ function OPSGROUP:_AllSimilarStatus(status) -- Debug info. self:T2(self.lid..string.format("All %d elements have similar status %s ==> returning TRUE", #self.elements, status)) - + return true end @@ -7955,9 +7955,9 @@ function OPSGROUP:_UpdateStatus(element, newstatus, airbase) -- Update status of element. element.status=newstatus - + -- Debug - self:T3(self.lid..string.format("UpdateStatus element=%s: %s --> %s", element.name, oldstatus, newstatus)) + self:T3(self.lid..string.format("UpdateStatus element=%s: %s --> %s", element.name, oldstatus, newstatus)) for _,_element in pairs(self.elements) do local Element=_element -- #OPSGROUP.Element self:T3(self.lid..string.format("Element %s: %s", Element.name, Element.status)) @@ -7980,7 +7980,7 @@ function OPSGROUP:_UpdateStatus(element, newstatus, airbase) if self:_AllSimilarStatus(newstatus) then self:__Spawned(-0.5) end - + elseif newstatus==OPSGROUP.ElementStatus.PARKING then --- -- PARKING @@ -8005,7 +8005,7 @@ function OPSGROUP:_UpdateStatus(element, newstatus, airbase) if self:_AllSimilarStatus(newstatus) then self:__Taxiing(-0.5) end - + elseif newstatus==OPSGROUP.ElementStatus.TAKEOFF then --- -- TAKEOFF @@ -8090,13 +8090,13 @@ function OPSGROUP:GetElementByName(unitname) for _,_element in pairs(self.elements) do local element=_element --#OPSGROUP.Element - + if element.name==unitname then return element end - + end - + end return nil @@ -8109,47 +8109,47 @@ end function OPSGROUP:GetElementZoneBoundingBox(UnitName) local element=self:GetElementByName(UnitName) - + if element and element.status~=OPSGROUP.ElementStatus.DEAD then - + -- Create a new zone if necessary. element.zoneBoundingbox=element.zoneBoundingbox or ZONE_POLYGON_BASE:New(element.name.." Zone Bounding Box", {}) local l=element.length local w=element.width - + local X=self:GetOrientationX(element.name) local heading=math.deg(math.atan2(X.z, X.x)) - + env.info(string.format("FF l=%d w=%d h=%d", l, w, heading)) - + -- Set of edges facing "North" at the origin of the map. local b={} b[1]={x=l/2, y=-w/2} --DCS#Vec2 b[2]={x=l/2, y=w/2} --DCS#Vec2 b[3]={x=-l/2, y=w/2} --DCS#Vec2 b[4]={x=-l/2, y=-w/2} --DCS#Vec2 - + -- Rotate box to match current heading of the unit. for i,p in pairs(b) do b[i]=UTILS.Vec2Rotate2D(p, heading) end -- Translate the zone to the positon of the unit. - local vec2=self:GetVec2(element.name) + local vec2=self:GetVec2(element.name) local d=UTILS.Vec2Norm(vec2) local h=UTILS.Vec2Hdg(vec2) for i,p in pairs(b) do b[i]=UTILS.Vec2Translate(p, d, h) - end + end - -- Update existing zone. + -- Update existing zone. element.zoneBoundingbox:UpdateFromVec2(b) - + return element.zoneBoundingbox end - - return nil + + return nil end --- Get the loading zone of the element. @@ -8159,13 +8159,13 @@ end function OPSGROUP:GetElementZoneLoad(UnitName) local element=self:GetElementByName(UnitName) - + if element and element.status~=OPSGROUP.ElementStatus.DEAD then - + element.zoneLoad=element.zoneLoad or ZONE_POLYGON_BASE:New(element.name.." Zone Load", {}) - self:_GetElementZoneLoader(element, element.zoneLoad, self.carrierLoader) - + self:_GetElementZoneLoader(element, element.zoneLoad, self.carrierLoader) + return element.zoneLoad end @@ -8179,13 +8179,13 @@ end function OPSGROUP:GetElementZoneUnload(UnitName) local element=self:GetElementByName(UnitName) - + if element and element.status~=OPSGROUP.ElementStatus.DEAD then - + element.zoneUnload=element.zoneUnload or ZONE_POLYGON_BASE:New(element.name.." Zone Unload", {}) - self:_GetElementZoneLoader(element, element.zoneUnload, self.carrierUnloader) - + self:_GetElementZoneLoader(element, element.zoneUnload, self.carrierUnloader) + return element.zoneUnload end @@ -8204,29 +8204,29 @@ function OPSGROUP:_GetElementZoneLoader(Element, Zone, Loader) local l=Element.length local w=Element.width - + -- Orientation 3D vector where the "nose" is pointing. local X=self:GetOrientationX(Element.name) - + -- Heading in deg. local heading=math.deg(math.atan2(X.z, X.x)) - + env.info(string.format("FF l=%d w=%d h=%d", l, w, heading)) - + -- Bounding box at the origin of the map facing "North". local b={} - + -- Create polygon rectangles. if Loader.type:lower()=="front" then - table.insert(b, {x= l/2, y=-Loader.width/2}) -- left, low + table.insert(b, {x= l/2, y=-Loader.width/2}) -- left, low table.insert(b, {x= l/2+Loader.length, y=-Loader.width/2}) -- left, up table.insert(b, {x= l/2+Loader.length, y= Loader.width/2}) -- right, up table.insert(b, {x= l/2, y= Loader.width/2}) -- right, low elseif Loader.type:lower()=="back" then - table.insert(b, {x=-l/2, y=-Loader.width/2}) -- left, low + table.insert(b, {x=-l/2, y=-Loader.width/2}) -- left, low table.insert(b, {x=-l/2-Loader.length, y=-Loader.width/2}) -- left, up table.insert(b, {x=-l/2-Loader.length, y= Loader.width/2}) -- right, up - table.insert(b, {x=-l/2, y= Loader.width/2}) -- right, low + table.insert(b, {x=-l/2, y= Loader.width/2}) -- right, low elseif Loader.type:lower()=="left" then table.insert(b, {x= Loader.length/2, y= -w/2}) -- right, up table.insert(b, {x= Loader.length/2, y= -w/2-Loader.width}) -- left, up @@ -8248,28 +8248,28 @@ function OPSGROUP:_GetElementZoneLoader(Element, Zone, Loader) table.insert(b, {x=b[3].x-Loader.length, y=b[3].y+Loader.width}) table.insert(b, {x=b[4].x-Loader.length, y=b[4].y-Loader.width}) end - + -- Rotate edges to match the current heading of the unit. for i,p in pairs(b) do b[i]=UTILS.Vec2Rotate2D(p, heading) - end + end -- Translate box to the current position of the unit. - local vec2=self:GetVec2(Element.name) + local vec2=self:GetVec2(Element.name) local d=UTILS.Vec2Norm(vec2) local h=UTILS.Vec2Hdg(vec2) - + for i,p in pairs(b) do b[i]=UTILS.Vec2Translate(p, d, h) end - - -- Update existing zone. + + -- Update existing zone. Zone:UpdateFromVec2(b) - + return Zone end - - return nil + + return nil end @@ -8282,7 +8282,7 @@ function OPSGROUP:GetElementAlive() local element=_element --#OPSGROUP.Element if element.status~=OPSGROUP.ElementStatus.DEAD then if element.unit and element.unit:IsAlive() then - return element + return element end end end @@ -8309,7 +8309,7 @@ function OPSGROUP:GetNelements(status) end end - + return n end @@ -8327,7 +8327,7 @@ end function OPSGROUP:GetAmmoTot() local units=self.group:GetUnits() - + local Ammo={} --#OPSGROUP.Ammo Ammo.Total=0 Ammo.Guns=0 @@ -8340,15 +8340,15 @@ function OPSGROUP:GetAmmoTot() Ammo.MissilesAS=0 Ammo.MissilesCR=0 Ammo.MissilesSA=0 - + for _,_unit in pairs(units) do local unit=_unit --Wrapper.Unit#UNIT - + if unit and unit:IsAlive()~=nil then - + -- Get ammo of the unit. local ammo=self:GetAmmoUnit(unit) - + -- Add up total. Ammo.Total=Ammo.Total+ammo.Total Ammo.Guns=Ammo.Guns+ammo.Guns @@ -8361,9 +8361,9 @@ function OPSGROUP:GetAmmoTot() Ammo.MissilesAS=Ammo.MissilesAS+ammo.MissilesAS Ammo.MissilesCR=Ammo.MissilesCR+ammo.MissilesCR Ammo.MissilesSA=Ammo.MissilesSA+ammo.MissilesSA - + end - + end return Ammo @@ -8459,7 +8459,7 @@ function OPSGROUP:GetAmmoUnit(unit, display) nmissilesAA=nmissilesAA+Nammo elseif MissileCategory==Weapon.MissileCategory.SAM then nmissiles=nmissiles+Nammo - nmissilesSA=nmissilesSA+Nammo + nmissilesSA=nmissilesSA+Nammo elseif MissileCategory==Weapon.MissileCategory.ANTI_SHIP then nmissiles=nmissiles+Nammo nmissilesAS=nmissilesAS+Nammo @@ -8468,7 +8468,7 @@ function OPSGROUP:GetAmmoUnit(unit, display) nmissilesAG=nmissilesAG+Nammo elseif MissileCategory==Weapon.MissileCategory.CRUISE then nmissiles=nmissiles+Nammo - nmissilesCR=nmissilesCR+Nammo + nmissilesCR=nmissilesCR+Nammo elseif MissileCategory==Weapon.MissileCategory.OTHER then nmissiles=nmissiles+Nammo nmissilesAG=nmissilesAG+Nammo @@ -8476,11 +8476,11 @@ function OPSGROUP:GetAmmoUnit(unit, display) -- Debug info. text=text..string.format("- %d %s missiles of type %s\n", Nammo, self:_MissileCategoryName(MissileCategory), _weaponName) - + elseif Category==Weapon.Category.TORPEDO then - + -- Add up all rockets. - ntorps=ntorps+Nammo + ntorps=ntorps+Nammo -- Debug info. text=text..string.format("- %d torpedos of type %s\n", Nammo, _weaponName) @@ -8549,7 +8549,7 @@ end -- @param Wrapper.Object#OBJECT Object The object. -- @return Core.Point#COORDINATE The coordinate of the object. function OPSGROUP:_CoordinateFromObject(Object) - + if Object then if Object:IsInstanceOf("COORDINATE") then return Object @@ -8563,7 +8563,7 @@ function OPSGROUP:_CoordinateFromObject(Object) end else self:E(self.lid.."ERROR: Object passed is nil!") - end + end return nil end @@ -8577,24 +8577,24 @@ function OPSGROUP:_AddElementByName(unitname) local unit=UNIT:FindByName(unitname) if unit then - + -- TODO: this is wrong when grouping is used! local unittemplate=unit:GetTemplate() - + local element={} --#OPSGROUP.Element -- Name and status. element.name=unitname - element.status=OPSGROUP.ElementStatus.INUTERO - + element.status=OPSGROUP.ElementStatus.INUTERO + -- Unit and group. element.unit=unit element.DCSunit=Unit.getByName(unitname) element.group=unit:GetGroup() - + -- Skill etc. - element.skill=unittemplate.skill or "Unknown" + element.skill=unittemplate.skill or "Unknown" if element.skill=="Client" or element.skill=="Player" then element.ai=false element.client=CLIENT:FindByName(unitname) @@ -8605,47 +8605,47 @@ function OPSGROUP:_AddElementByName(unitname) -- Descriptors and type/category. element.descriptors=unit:GetDesc() self:I({desc=element.descriptors}) - + element.category=unit:GetUnitCategory() element.categoryname=unit:GetCategoryName() element.typename=unit:GetTypeName() - + -- Ammo. element.ammo0=self:GetAmmoUnit(unit, false) - + -- Life points. element.life=unit:GetLife() element.life0=math.max(unit:GetLife0(), element.life) -- Some units report a life0 that is smaller than its initial life points. - + -- Size and dimensions. element.size, element.length, element.height, element.width=unit:GetObjectSize() - + -- Weight and cargo. element.weightEmpty=element.descriptors.massEmpty or 666 - element.weightCargo=0 - element.weight=element.weightEmpty+element.weightCargo - + element.weightCargo=0 + element.weight=element.weightEmpty+element.weightCargo + -- Looks like only aircraft have a massMax value in the descriptors. element.weightMaxTotal=element.descriptors.massMax or element.weightEmpty+2*95 --If max mass is not given, we assume 10 soldiers. - + if self.isArmygroup then - + element.weightMaxTotal=element.weightEmpty+10*95 --If max mass is not given, we assume 10 soldiers. - + elseif self.isNavygroup then - + element.weightMaxTotal=element.weightEmpty+10*1000 - + end - + -- Max cargo weight: unit:SetCargoBayWeightLimit() element.weightMaxCargo=unit.__.CargoBayWeightLimit - + -- FLIGHTGROUP specific. if self.isFlightgroup then - element.callsign=element.unit:GetCallsign() + element.callsign=element.unit:GetCallsign() element.modex=unittemplate.onboard_num element.payload=unittemplate.payload element.pylons=unittemplate.payload and unittemplate.payload.pylons or nil @@ -8656,7 +8656,7 @@ function OPSGROUP:_AddElementByName(unitname) -- Debug text. local text=string.format("Adding element %s: status=%s, skill=%s, life=%.1f/%.1f category=%s (%d), type=%s, size=%.1f (L=%.1f H=%.1f W=%.1f), weight=%.1f/%.1f (cargo=%.1f/%.1f)", - element.name, element.status, element.skill, element.life, element.life0, element.categoryname, element.category, element.typename, + element.name, element.status, element.skill, element.life, element.life0, element.categoryname, element.category, element.typename, element.size, element.length, element.height, element.width, element.weight, element.weightMaxTotal, element.weightCargo, element.weightMaxCargo) self:I(self.lid..text) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 5165d1360..5e32ecf85 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -6,7 +6,7 @@ -- -- ### Contributions: -- --- * FlightControl : Rework to OO framework +-- * FlightControl : Rework to OO framework. -- -- @module Utils -- @image MOOSE.JPG diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index d015bbde9..2f3c1603e 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -362,6 +362,14 @@ AIRBASE.TheChannel = { -- * AIRBASE.Syria.Beirut_Rafic_Hariri -- * AIRBASE.Syria.An_Nasiriyah -- * AIRBASE.Syria.Abu_al_Duhur +-- * AIRBASE.Syria.H4 +-- * AIRBASE.Syria.Gaziantep +-- * AIRBASE.Syria.Rosh_Pina +-- * AIRBASE.Syria.Sayqal +-- * AIRBASE.Syria.Shayrat +-- * AIRBASE.Syria.Tiyas +-- * AIRBASE.Syria.Tha_lah +-- * AIRBASE.Syria.Naqoura -- -- @field Syria AIRBASE.Syria={ @@ -398,6 +406,14 @@ AIRBASE.Syria={ ["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri", ["An_Nasiriyah"]="An Nasiriyah", ["Abu_al_Duhur"]="Abu al-Duhur", + ["H4"]="H4", + ["Gaziantep"]="Gaziantep", + ["Rosh_Pina"]="Rosh Pina", + ["Sayqal"]="Sayqal", + ["Shayrat"]="Shayrat", + ["Tiyas"]="Tiyas", + ["Tha_lah"]="Tha'lah", + ["Naqoura"]="Naqoura", } diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 5db0632ab..0e98f49e4 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1429,7 +1429,7 @@ end -- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted). -- @param #number WeaponType (optional) Enum for weapon type ID. This value is only required if you want the group firing to use a specific weapon, for instance using the task on a ship to force it to fire guided missiles at targets within cannon range. See http://wiki.hoggit.us/view/DCS_enum_weapon_flag -- @param #number Altitude (Optional) Altitude in meters. --- @param #number ASL Altitude is above mean sea level. Default is above ground level. +-- @param #number ASL Altitude is above mean sea level. Default is above ground level. -- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Altitude, ASL ) @@ -1451,7 +1451,7 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Alti DCSTask.params.expendQty = AmmoCount DCSTask.params.expendQtyEnabled = true end - + if Altitude then DCSTask.params.altitude=Altitude end @@ -1459,7 +1459,7 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType, Alti if WeaponType then DCSTask.params.weaponType=WeaponType end - + self:I(DCSTask) return DCSTask @@ -2908,7 +2908,7 @@ end --- Set option for Rules of Engagement (ROE). -- @param Wrapper.Controllable#CONTROLLABLE self -- @param #number ROEvalue ROE value. See ENUMS.ROE. --- @return Wrapper.Controllable#CONTROLLABLE self +-- @return #CONTROLLABLE self function CONTROLLABLE:OptionROE(ROEvalue) local DCSControllable = self:GetDCSObject() @@ -2950,8 +2950,8 @@ function CONTROLLABLE:OptionROEHoldFirePossible() end --- Weapons Hold: AI will hold fire under all circumstances. --- @param Wrapper.Controllable#CONTROLLABLE self --- @return Wrapper.Controllable#CONTROLLABLE self +-- @param #CONTROLLABLE self +-- @return #CONTROLLABLE self function CONTROLLABLE:OptionROEHoldFire() self:F2( { self.ControllableName } ) @@ -3549,7 +3549,7 @@ end -- Note that when WayPointInitialize is called, the Mission of the controllable is RESTARTED! -- @param #CONTROLLABLE self -- @param #table WayPoints If WayPoints is given, then use the route. --- @return #CONTROLLABLE +-- @return #CONTROLLABLE self function CONTROLLABLE:WayPointInitialize( WayPoints ) self:F( { WayPoints } ) @@ -3580,7 +3580,7 @@ end -- @param #number WayPoint The waypoint number. Note that the start waypoint on the route is WayPoint 1! -- @param #number WayPointIndex When defining multiple WayPoint functions for one WayPoint, use WayPointIndex to set the sequence of actions. -- @param #function WayPointFunction The waypoint function to be called when the controllable moves over the waypoint. The waypoint function takes variable parameters. --- @return #CONTROLLABLE +-- @return #CONTROLLABLE self function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunction, ... ) self:F2( { WayPoint, WayPointIndex, WayPointFunction } ) @@ -3596,7 +3596,7 @@ end -- @param #CONTROLLABLE self -- @param #number WayPoint The WayPoint from where to execute the mission. -- @param #number WaitTime The amount seconds to wait before initiating the mission. --- @return #CONTROLLABLE +-- @return #CONTROLLABLE self function CONTROLLABLE:WayPointExecute( WayPoint, WaitTime ) self:F( { WayPoint, WaitTime } ) @@ -3679,7 +3679,9 @@ end --- Sets Controllable Option for A2A attack range for AIR FIGHTER units. -- @param #CONTROLLABLE self --- @param #number Defines the range: MAX_RANGE = 0, NEZ_RANGE = 1, HALF_WAY_RMAX_NEZ = 2, TARGET_THREAT_EST = 3, RANDOM_RANGE = 4. Defaults to 3. See: https://wiki.hoggitworld.com/view/DCS_option_missileAttack +-- @param #number range Defines the range +-- @return #CONTROLLABLE self +-- @usage Range can be one of MAX_RANGE = 0, NEZ_RANGE = 1, HALF_WAY_RMAX_NEZ = 2, TARGET_THREAT_EST = 3, RANDOM_RANGE = 4. Defaults to 3. See: https://wiki.hoggitworld.com/view/DCS_option_missileAttack function CONTROLLABLE:OptionAAAttackRange(range) self:F2( { self.ControllableName } ) -- defaults to 3 @@ -3705,7 +3707,7 @@ end -- @param #number EngageRange Engage range limit in percent (a number between 0 and 100). Default 100. -- @return #CONTROLLABLE self function CONTROLLABLE:OptionEngageRange(EngageRange) - self:F2( { self.ControllableName } ) + self:F2( { self.ControllableName } ) -- Set default if not specified. EngageRange=EngageRange or 100 if EngageRange < 0 or EngageRange > 100 then @@ -3714,9 +3716,9 @@ function CONTROLLABLE:OptionEngageRange(EngageRange) local DCSControllable = self:GetDCSObject() if DCSControllable then local Controller = self:_GetController() - if Controller then + if Controller then if self:IsGround() then - self:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION, EngageRange) + self:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION, EngageRange) end end return self @@ -3730,10 +3732,11 @@ end -- @param #number radius Radius of the relocation zone, default 500 -- @param #boolean onroad If true, route on road (less problems with AI way finding), default true -- @param #boolean shortcut If true and onroad is set, take a shorter route - if available - off road, default false +-- @return #CONTROLLABLE self function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortcut) - self:F2( { self.ControllableName } ) + self:F2( { self.ControllableName } ) - local _coord = self:GetCoordinate() + local _coord = self:GetCoordinate() local _radius = radius or 500 local _speed = speed or 20 local _tocoord = _coord:GetRandomCoordinateInRadius(_radius,100) @@ -3741,7 +3744,7 @@ function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortc local _grptsk = {} local _candoroad = false local _shortcut = shortcut or false - + -- create a DCS Task an push it on the group -- TaskGroundOnRoad(ToCoordinate,Speed,OffRoadFormation,Shortcut,FromCoordinate,WaypointFunction,WaypointFunctionArguments) if onroad then @@ -3751,5 +3754,26 @@ function CONTROLLABLE:RelocateGroundRandomInRadius(speed, radius, onroad, shortc self:TaskRouteToVec2(_tocoord:GetVec2(),_speed,"Off Road") end - return self + return self +end + +--- Defines how long a GROUND unit/group will move to avoid an ongoing attack. +-- @param #CONTROLLABLE self +-- @param #number Seconds Any positive number: AI will disperse, but only for the specified time before continuing their route. 0: AI will not disperse. +-- @return #CONTROLLABLE self +function CONTROLLABLE:OptionDisperseOnAttack(Seconds) + self:F2( { self.ControllableName } ) + -- Set default if not specified. + local seconds = Seconds or 0 + local DCSControllable = self:GetDCSObject() + if DCSControllable then + local Controller = self:_GetController() + if Controller then + if self:IsGround() then + self:SetOption(AI.Option.GROUND.id.DISPERSE_ON_ATTACK, seconds) + end + end + return self + end + return nil end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 4ab573ec0..1a2f00fa8 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2169,40 +2169,6 @@ function GROUP:GetThreatLevel() return threatlevelMax end ---- Get the unit in the group with the highest threat level, which is still alive. --- @param #GROUP self --- @return Wrapper.Unit#UNIT The most dangerous unit in the group. --- @return #number Threat level of the unit. -function GROUP:GetHighestThreat() - - -- Get units of the group. - local units=self:GetUnits() - - if units then - - local threat=nil ; local maxtl=0 - for _,_unit in pairs(units or {}) do - local unit=_unit --Wrapper.Unit#UNIT - - if unit and unit:IsAlive() then - - -- Threat level of group. - local tl=unit:GetThreatLevel() - - -- Check if greater the current threat. - if tl>maxtl then - maxtl=tl - threat=unit - end - end - end - - return threat, maxtl - end - - return nil, nil -end - --- Returns true if the first unit of the GROUP is in the air. -- @param Wrapper.Group#GROUP self diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 1d1df1b63..65199d97d 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -154,8 +154,8 @@ function UNIT:Name() return self.UnitName end - ---- @param #UNIT self +--- Get the DCS unit object. +-- @param #UNIT self -- @return DCS#Unit function UNIT:GetDCSObject() @@ -1429,7 +1429,7 @@ function UNIT:GetTemplateFuel() return nil end ---- GROUND - Switch on/off radar emissions +--- GROUND - Switch on/off radar emissions. -- @param #UNIT self -- @param #boolean switch function UNIT:EnableEmission(switch) diff --git a/README.md b/README.md index 7ba71d91c..976a47f96 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build status](https://ci.appveyor.com/api/projects/status/1y8nfmx7lwsn33tt?svg=true)](https://ci.appveyor.com/project/Applevangelist/MOOSE) + # MOOSE framework MOOSE is a **M**ission **O**bject **O**riented **S**cripting **E**nvironment, and is meant for mission designers in DCS World. @@ -50,9 +52,7 @@ This repository contains all the demonstration missions in packed format (*.miz) This repository contains all the demonstration missions in unpacked format. That means that there is no .miz file included, but all the .miz contents are unpacked. - - - + ## [MOOSE Web Site](https://flightcontrol-master.github.io/MOOSE_DOCS/) Documentation on the MOOSE class hierarchy, usage guides and background information can be found here for normal users, beta testers and contributors.