Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
Applevangelist 2023-02-23 10:20:22 +01:00
commit d0e6a29486
19 changed files with 1089 additions and 927 deletions

View File

@ -305,8 +305,8 @@ EVENTS = {
-- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object. -- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object.
-- @field #string PlaceName The name of the airbase. -- @field #string PlaceName The name of the airbase.
-- --
-- @field #table weapon The weapon used during the event. -- @field DCS#Weapon weapon The weapon used during the event.
-- @field #table Weapon -- @field DCS#Weapon Weapon The weapon used during the event.
-- @field #string WeaponName Name of the weapon. -- @field #string WeaponName Name of the weapon.
-- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon. -- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon.
-- --

View File

@ -1160,12 +1160,15 @@ do -- COORDINATE
--- Return the 3D distance in meters between the target COORDINATE and the COORDINATE. --- Return the 3D distance in meters between the target COORDINATE and the COORDINATE.
-- @param #COORDINATE self -- @param #COORDINATE self
-- @param #COORDINATE TargetCoordinate The target COORDINATE. -- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3.
-- @return DCS#Distance Distance The distance in meters. -- @return DCS#Distance Distance The distance in meters.
function COORDINATE:Get3DDistance( TargetCoordinate ) function COORDINATE:Get3DDistance( TargetCoordinate )
local TargetVec3 = TargetCoordinate:GetVec3() --local TargetVec3 = TargetCoordinate:GetVec3()
local TargetVec3 = {x=TargetCoordinate.x, y=TargetCoordinate.y, z=TargetCoordinate.z}
local SourceVec3 = self:GetVec3() local SourceVec3 = self:GetVec3()
return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5 --local dist=( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
local dist=UTILS.VecDist3D(TargetVec3, SourceVec3)
return dist
end end

View File

@ -555,7 +555,7 @@ function ZONE_BASE:GetZoneMaybe()
end end
end end
-- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists. --- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @param #string PropertyName The name of a the TriggerZone Property to be retrieved. -- @param #string PropertyName The name of a the TriggerZone Property to be retrieved.
-- @return #string The Value of the TriggerZone Property with the given PropertyName, or nil if absent. -- @return #string The Value of the TriggerZone Property with the given PropertyName, or nil if absent.
@ -569,7 +569,7 @@ function ZONE_BASE:GetProperty(PropertyName)
return self.Properties[PropertyName] return self.Properties[PropertyName]
end end
-- Returns the zone Properties table. --- Returns the zone Properties table.
-- @param #ZONE_BASE self -- @param #ZONE_BASE self
-- @return #table The Key:Value table of TriggerZone properties of the zone. -- @return #table The Key:Value table of TriggerZone properties of the zone.
function ZONE_BASE:GetAllProperties() function ZONE_BASE:GetAllProperties()

View File

@ -451,8 +451,8 @@ do -- Types
--- Vec3 type is a 3D-vector. --- Vec3 type is a 3D-vector.
-- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain. -- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain.
-- @type Vec3 -- @type Vec3
-- @field #Distance x is directed to the north -- @field #Distance x is directed to the North
-- @field #Distance z is directed to the east -- @field #Distance z is directed to the East
-- @field #Distance y is directed up -- @field #Distance y is directed up
--- Vec2 is a 2D-vector for the ground plane as a reference plane. --- Vec2 is a 2D-vector for the ground plane as a reference plane.
@ -703,10 +703,11 @@ do -- Weapon
--- Weapon.Category enum that stores weapon categories. --- Weapon.Category enum that stores weapon categories.
-- @type Weapon.Category -- @type Weapon.Category
-- @field SHELL -- @field #number SHELL Shell.
-- @field MISSILE -- @field #number MISSILE Missile
-- @field ROCKET -- @field #number ROCKET Rocket.
-- @field BOMB -- @field #number BOMB Bomb.
-- @field #number TORPEDO Torpedo.
--- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB). --- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).

View File

@ -103,6 +103,7 @@
-- @field #number coalition The coalition of the arty group. -- @field #number coalition The coalition of the arty group.
-- @field #boolean respawnafterdeath Respawn arty group after all units are dead. -- @field #boolean respawnafterdeath Respawn arty group after all units are dead.
-- @field #number respawndelay Respawn delay in seconds. -- @field #number respawndelay Respawn delay in seconds.
-- @field #number dtTrack Time interval in seconds for weapon tracking.
-- @extends Core.Fsm#FSM_CONTROLLABLE -- @extends Core.Fsm#FSM_CONTROLLABLE
--- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can --- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can
@ -693,7 +694,7 @@ ARTY.db={
--- Arty script version. --- Arty script version.
-- @field #string version -- @field #string version
ARTY.version="1.2.0" ARTY.version="1.3.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -802,6 +803,9 @@ function ARTY:New(group, alias)
self.ismobile=false self.ismobile=false
end end
-- Set track time interval.
self.dtTrack=0.2
-- Set speed to 0.7 of maximum. -- Set speed to 0.7 of maximum.
self.Speed=self.SpeedMax * 0.7 self.Speed=self.SpeedMax * 0.7
@ -1497,6 +1501,15 @@ function ARTY:SetStatusInterval(interval)
return self return self
end end
--- Set time interval for weapon tracking.
-- @param #ARTY self
-- @param #number interval Time interval in seconds. Default 0.2 seconds.
-- @return self
function ARTY:SetTrackInterval(interval)
self.dtTrack=interval or 0.2
return self
end
--- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed. --- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed.
-- @param #ARTY self -- @param #ARTY self
-- @param #number waittime Time in seconds. Default 300 seconds. -- @param #number waittime Time in seconds. Default 300 seconds.
@ -2129,6 +2142,95 @@ end
-- Event Handling -- Event Handling
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Function called during tracking of weapon.
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #ARTY self ARTY object.
-- @param #ARTY.Target target Target of the weapon.
function ARTY._FuncTrack(weapon, self, target)
-- Coordinate and distance to target.
local _coord=weapon.coordinate
local _dist=_coord:Get2DDistance(target.coord)
local _destroyweapon=false
-- Debug
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
if target.weapontype==ARTY.WeaponType.IlluminationShells then
-- Check if within distace.
if _dist<target.radius then
-- Get random coordinate within certain radius of the target.
local _cr=target.coord:GetRandomCoordinateInRadius(target.radius)
-- Get random altitude over target.
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
-- Adjust explosion height of coordinate.
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
-- Create illumination flare.
_ci:IlluminationBomb(self.illuPower)
-- Destroy actual shell.
_destroyweapon=true
end
elseif target.weapontype==ARTY.WeaponType.SmokeShells then
if _dist<target.radius then
-- Get random coordinate within a certain radius.
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
-- Fire smoke at this coordinate.
_cr:Smoke(self.smokeColor)
-- Destroy actual shell.
_destroyweapon=true
end
end
if _destroyweapon then
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
-- Destroy weapon and stop timer.
weapon:Destroy()
-- No more tracking.
weapon.tracking=false
end
end
--- Function called after impact of weapon.
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #ARTY self ARTY object.
-- @param #ARTY.Target target Target of the weapon.
function ARTY._FuncImpact(weapon, self, target)
-- Debug info.
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
-- Get impact coordinate.
local _impactcoord=weapon:GetImpactCoordinate()
-- Create a "nuclear" explosion and blast at the impact point.
if target.weapontype==ARTY.WeaponType.TacticalNukes then
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
--SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
self:ScheduleOnce(1.0, ARTY._NuclearBlast, self, _impactcoord)
end
end
--- Eventhandler for shot event. --- Eventhandler for shot event.
-- @param #ARTY self -- @param #ARTY self
-- @param Core.Event#EVENTDATA EventData -- @param Core.Event#EVENTDATA EventData
@ -2162,128 +2264,32 @@ function ARTY:OnEventShot(EventData)
self:T(self.lid..text) self:T(self.lid..text)
MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug) MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug)
-- Last known position of the weapon fired.
local _lastpos={x=0, y=0, z=0}
--- Track the position of the weapon if it is supposed to model a tac nuke, illumination or smoke shell.
-- @param #table _weapon
local function _TrackWeapon(_data)
-- When the pcall status returns false the weapon has hit.
local _weaponalive,_currpos = pcall(
function()
return _data.weapon:getPoint()
end)
-- Debug
self:T3(self.lid..string.format("ARTY %s: Weapon still in air: %s", self.groupname, tostring(_weaponalive)))
-- Destroy weapon before impact.
local _destroyweapon=false
if _weaponalive then
-- Update last position.
_lastpos={x=_currpos.x, y=_currpos.y, z=_currpos.z}
-- Coordinate and distance to target.
local _coord=COORDINATE:NewFromVec3(_lastpos)
local _dist=_coord:Get2DDistance(_data.target.coord)
-- Debug
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
if _data.target.weapontype==ARTY.WeaponType.IlluminationShells then
-- Check if within distace.
if _dist<_data.target.radius then
-- Get random coordinate within certain radius of the target.
local _cr=_data.target.coord:GetRandomCoordinateInRadius(_data.target.radius)
-- Get random altitude over target.
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
-- Adjust explosion height of coordinate.
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
-- Create illumination flare.
_ci:IlluminationBomb(self.illuPower)
-- Destroy actual shell.
_destroyweapon=true
end
elseif _data.target.weapontype==ARTY.WeaponType.SmokeShells then
if _dist<_data.target.radius then
-- Get random coordinate within a certain radius.
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
-- Fire smoke at this coordinate.
_cr:Smoke(self.smokeColor)
-- Destroy actual shell.
_destroyweapon=true
end
end
if _destroyweapon then
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
-- Destroy weapon and stop timer.
_data.weapon:destroy()
return nil
else
-- TODO: Make dt input parameter.
local dt=0.02
self:T3(self.lid..string.format("ARTY %s tracking weapon again in %.3f seconds", self.groupname, dt))
-- Check again in 0.05 seconds.
return timer.getTime() + dt
end
else
-- Get impact coordinate.
local _impactcoord=COORDINATE:NewFromVec3(_lastpos)
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
-- Create a "nuclear" explosion and blast at the impact point.
if _data.target.weapontype==ARTY.WeaponType.TacticalNukes then
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
end
-- Stop timer.
return nil
end
end
-- Start track the shell if we want to model a tactical nuke. -- Start track the shell if we want to model a tactical nuke.
local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0 local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0
local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0 local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0
local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0 local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0
if _tracknuke or _trackillu or _tracksmoke then if _tracknuke or _trackillu or _tracksmoke then
self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname)) -- Debug info.
self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname))
local _peter={} -- Create a weapon object.
_peter.weapon=EventData.weapon local weapon=WEAPON:New(EventData.weapon)
_peter.target=UTILS.DeepCopy(self.currentTarget)
timer.scheduleFunction(_TrackWeapon, _peter, timer.getTime() + 2.0) -- Set time step for tracking.
weapon:SetTimeStepTrack(self.dtTrack)
-- Copy target. We need a copy because it might already be overwritten with the next target during flight of weapon.
local target=UTILS.DeepCopy(self.currentTarget)
-- Set callback functions.
weapon:SetFuncTrack(ARTY._FuncTrack, self, target)
weapon:SetFuncImpact(ARTY._FuncImpact, self, target)
-- Start tracking in 2 sec (arty ammo should fly a bit).
weapon:StartTrack(2)
end end
-- Get current ammo. -- Get current ammo.
@ -3931,9 +3937,10 @@ function ARTY:GetAmmo(display)
return nammo, nshells, nrockets, nmissiles return nammo, nshells, nrockets, nmissiles
end end
for _,unit in pairs(units) do for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then if unit then
-- Output. -- Output.
local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName()) local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName())

View File

@ -26,6 +26,7 @@
--- FOX class. --- FOX class.
-- @type FOX -- @type FOX
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity level.
-- @field #boolean Debug Debug mode. Messages to all about status. -- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string lid Class id string for output to DCS log file. -- @field #string lid Class id string for output to DCS log file.
-- @field #table menuadded Table of groups the menu was added for. -- @field #table menuadded Table of groups the menu was added for.
@ -124,6 +125,7 @@
-- @field #FOX -- @field #FOX
FOX = { FOX = {
ClassName = "FOX", ClassName = "FOX",
verbose = 0,
Debug = false, Debug = false,
lid = nil, lid = nil,
menuadded = {}, menuadded = {},
@ -168,7 +170,7 @@ FOX = {
--- Missile data table. --- Missile data table.
-- @type FOX.MissileData -- @type FOX.MissileData
-- @field Wrapper.Unit#UNIT weapon Missile weapon unit. -- @field DCS#Weapon weapon Missile weapon object.
-- @field #boolean active If true the missile is active. -- @field #boolean active If true the missile is active.
-- @field #string missileType Type of missile. -- @field #string missileType Type of missile.
-- @field #string missileName Name of missile. -- @field #string missileName Name of missile.
@ -185,6 +187,8 @@ FOX = {
-- @field #string targetName Name of the target unit or "unknown". -- @field #string targetName Name of the target unit or "unknown".
-- @field #string targetOrig Name of the "original" target, i.e. the one right after launched. -- @field #string targetOrig Name of the "original" target, i.e. the one right after launched.
-- @field #FOX.PlayerData targetPlayer Player that was targeted or nil. -- @field #FOX.PlayerData targetPlayer Player that was targeted or nil.
-- @field Core.Point#COORDINATE missileCoord Missile coordinate during tracking.
-- @field Wrapper.Weapon#WEAPON Weapon Weapon object.
--- Main radio menu on group level. --- Main radio menu on group level.
-- @field #table MenuF10 Root menu table on group level. -- @field #table MenuF10 Root menu table on group level.
@ -196,7 +200,7 @@ FOX.MenuF10Root=nil
--- FOX class version. --- FOX class version.
-- @field #string version -- @field #string version
FOX.version="0.6.1" FOX.version="0.8.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@ -500,6 +504,7 @@ function FOX:SetDisableF10Menu()
return self return self
end end
--- Enable F10 menu for all players. --- Enable F10 menu for all players.
-- @param #FOX self -- @param #FOX self
-- @return #FOX self -- @return #FOX self
@ -510,6 +515,15 @@ function FOX:SetEnableF10Menu()
return self return self
end end
--- Set verbosity level.
-- @param #FOX self
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
-- @return #FOX self
function FOX:SetVerbosity(VerbosityLevel)
self.verbose=VerbosityLevel or 0
return self
end
--- Set default player setting for missile destruction. --- Set default player setting for missile destruction.
-- @param #FOX self -- @param #FOX self
-- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed. -- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed.
@ -605,7 +619,9 @@ function FOX:onafterStatus(From, Event, To)
local clock=UTILS.SecondsToClock(time) local clock=UTILS.SecondsToClock(time)
-- Status. -- Status.
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate)) if self.verbose>=1 then
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate))
end
-- Check missile status. -- Check missile status.
self:_CheckMissileStatus() self:_CheckMissileStatus()
@ -713,7 +729,9 @@ function FOX:_CheckMissileStatus()
if #self.missiles==0 then if #self.missiles==0 then
text=text.." none" text=text.." none"
end end
self:I(self.lid..text) if self.verbose>=2 then
self:I(self.lid..text)
end
-- Remove inactive missiles. -- Remove inactive missiles.
for i=#self.missiles,1,-1 do for i=#self.missiles,1,-1 do
@ -743,7 +761,7 @@ function FOX:_IsProtected(targetunit)
if targetgroup then if targetgroup then
local targetname=targetgroup:GetName() local targetname=targetgroup:GetName()
for _,_group in pairs(self.protectedset:GetSetObjects()) do for _,_group in pairs(self.protectedset:GetSet()) do
local group=_group --Wrapper.Group#GROUP local group=_group --Wrapper.Group#GROUP
if group then if group then
@ -762,6 +780,277 @@ function FOX:_IsProtected(targetunit)
return false return false
end end
--- Function called from weapon tracking.
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #FOX self FOX object.
-- @param #FOX.MissileData missile Fired missile
function FOX._FuncTrack(weapon, self, missile)
-- Missile coordinate.
local missileCoord= missile.missileCoord:UpdateFromVec3(weapon.vec3) --COORDINATE:NewFromVec3(_lastBombPos)
-- Missile velocity in m/s.
local missileVelocity=weapon:GetSpeed() --UTILS.VecNorm(_ordnance:getVelocity())
-- Update missile target if necessary.
self:GetMissileTarget(missile)
-- Target unit of the missile.
local target=nil --Wrapper.Unit#UNIT
if missile.targetUnit then
-----------------------------------
-- Missile has a specific target --
-----------------------------------
if missile.targetPlayer then
-- Target is a player.
if missile.targetPlayer.destroy==true then
target=missile.targetUnit
end
else
-- Check if unit is protected.
if self:_IsProtected(missile.targetUnit) then
target=missile.targetUnit
end
end
else
------------------------------------
-- Missile has NO specific target --
------------------------------------
-- TODO: This might cause a problem with wingman. Even if the shooter itself is excluded from the check, it's wingmen are not.
-- That would trigger the distance check right after missile launch if things to wrong.
--
-- Possible solutions:
-- * Time check: enable this check after X seconds after missile was fired. What is X?
-- * Coalition check. But would not work in training situations where blue on blue is valid!
-- * At least enable it for surface-to-air missiles.
local function _GetTarget(_unit)
local unit=_unit --Wrapper.Unit#UNIT
-- Player position.
local playerCoord=unit:GetCoordinate()
-- Distance.
local dist=missileCoord:Get3DDistance(playerCoord)
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
if dist<=self.explosiondist then
return unit
end
end
-- Distance to closest player.
local mindist=nil
-- Loop over players.
for _,_player in pairs(self.players) do
local player=_player --#FOX.PlayerData
-- Check that player was not the one who launched the missile.
if player.unitname~=missile.shooterName then
-- Player position.
local playerCoord=player.unit:GetCoordinate()
-- Distance.
local dist=missileCoord:Get3DDistance(playerCoord)
-- Distance from shooter to player.
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
mindist=dist
target=player.unit
end
end
end
if self.protectedset then
-- Distance to closest protected unit.
mindist=nil
for _,_group in pairs(self.protectedset:GetSet()) do
local group=_group --Wrapper.Group#GROUP
for _,_unit in pairs(group:GetUnits()) do
local unit=_unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
-- Check that player was not the one who launched the missile.
if unit:GetName()~=missile.shooterName then
-- Player position.
local playerVec3=unit:GetVec3()
-- Distance.
local dist=missileCoord:Get3DDistance(playerVec3)
-- Distance from shooter to player.
local Dshooter2player=missile.shotCoord:Get3DDistance(playerVec3)
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
mindist=dist
target=unit
end
end
end
end
end
end
if target then
self:T(self.lid..string.format("Missile %s with NO explicit target got closest unit to missile as target %s. Dist=%s m", missile.missileType, target:GetName(), tostring(mindist)))
end
end
-- Check if missile has a valid target.
if target then
-- Target coordinate.
local targetVec3=target:GetVec3() --target:GetCoordinate()
-- Distance from missile to target.
local distance=missileCoord:Get3DDistance(targetVec3)
-- Distance missile to shooter.
local distShooter=nil
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetVec3())
end
-- Debug output.
if self.Debug then
local bearing=missileCoord:HeadingTo(targetVec3)
local eta=distance/missileVelocity
-- Debug distance check.
self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta))
end
-- Distroy missile if it's getting too close.
local destroymissile=distance<=self.explosiondist
-- Check BIG missiles.
if self.explosiondist2 and distance<=self.explosiondist2 and not destroymissile then
destroymissile=missile.explosive>=self.bigmissilemass
end
-- If missile is 150 m from target ==> destroy missile if in safe zone.
if destroymissile and self:_CheckCoordSafe(targetVec3) then
-- Destroy missile.
self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance))
weapon:Destroy()
-- Missile is not active any more.
missile.active=false
-- Debug smoke.
if self.Debug then
missileCoord:SmokeRed()
end
-- Create event.
self:MissileDestroyed(missile)
-- Little explosion for the visual effect.
if self.explosionpower>0 and distance>50 and (distShooter==nil or (distShooter and distShooter>50)) then
missileCoord:Explosion(self.explosionpower)
end
-- Target was a player.
if missile.targetPlayer then
-- Message to target.
local text=string.format("Destroying missile. %s", self:_DeadText())
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
-- Increase dead counter.
missile.targetPlayer.dead=missile.targetPlayer.dead+1
end
-- We could disable the tracking here but then the impact function would not be called.
--weapon.tracking=false
else
-- Time step.
local dt=1.0
if distance>50000 then
-- > 50 km
dt=self.dt50 --=5.0
elseif distance>10000 then
-- 10-50 km
dt=self.dt10 --=1.0
elseif distance>5000 then
-- 5-10 km
dt=self.dt05 --0.5
elseif distance>1000 then
-- 1-5 km
dt=self.dt01 --0.1
else
-- < 1 km
dt=self.dt00 --0.01
end
-- Set time step.
weapon:SetTimeStepTrack(dt)
end
else
-- No current target.
self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName))
weapon:SetTimeStepTrack(0.1)
end
end
--- Callback function on impact or destroy otherwise.
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
-- @param #FOX self FOX object.
-- @param #FOX.MissileData missile Fired missile.
function FOX._FuncImpact(weapon, self, missile)
if missile.targetPlayer then
-- Get human player.
local player=missile.targetPlayer
-- Check for player and distance < 10 km.
if player and player.unit:IsAlive() then -- and missileCoord and player.unit:GetCoordinate():Get3DDistance(missileCoord)<10*1000 then
local text=string.format("Missile defeated. Well done, %s!", player.name)
MESSAGE:New(text, 10):ToClient(player.client)
-- Increase defeated counter.
player.defeated=player.defeated+1
end
end
-- Missile is not active any more.
missile.active=false
--Terminate the timer.
self:T(FOX.lid..string.format("Terminating missile track timer."))
weapon.tracking=false
end
--- Missle launch event. --- Missle launch event.
-- @param #FOX self -- @param #FOX self
-- @param #string From From state. -- @param #string From From state.
@ -820,302 +1109,17 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
end end
end end
-- Init missile position. -- Set callback function for tracking.
local _lastBombPos = {x=0,y=0,z=0} missile.Weapon:SetFuncTrack(FOX._FuncTrack, self, missile)
-- Missile coordinate. -- Set callback function for impact.
local missileCoord = nil --Core.Point#COORDINATE missile.Weapon:SetFuncImpact(FOX._FuncImpact, self, missile)
-- Target unit of the missile.
local target=nil --Wrapper.Unit#UNIT
--- Function monitoring the position of a bomb until impact.
local function trackMissile(_ordnance)
-- When the pcall returns a failure the weapon has hit.
local _status,_bombPos = pcall(
function()
return _ordnance:getPoint()
end)
-- Check if status is not nil. If so, we have a valid point.
if _status then
----------------------------------------------
-- Still in the air. Remember this position --
----------------------------------------------
-- Missile position.
_lastBombPos = {x=_bombPos.x, y=_bombPos.y, z=_bombPos.z}
-- Missile coordinate.
missileCoord=COORDINATE:NewFromVec3(_lastBombPos)
-- Missile velocity in m/s.
local missileVelocity=UTILS.VecNorm(_ordnance:getVelocity())
-- Update missile target if necessary.
self:GetMissileTarget(missile)
if missile.targetUnit then
-----------------------------------
-- Missile has a specific target --
-----------------------------------
if missile.targetPlayer then
-- Target is a player.
if missile.targetPlayer.destroy==true then
target=missile.targetUnit
end
else
-- Check if unit is protected.
if self:_IsProtected(missile.targetUnit) then
target=missile.targetUnit
end
end
else
------------------------------------
-- Missile has NO specific target --
------------------------------------
-- TODO: This might cause a problem with wingman. Even if the shooter itself is excluded from the check, it's wingmen are not.
-- That would trigger the distance check right after missile launch if things to wrong.
--
-- Possible solutions:
-- * Time check: enable this check after X seconds after missile was fired. What is X?
-- * Coalition check. But would not work in training situations where blue on blue is valid!
-- * At least enable it for surface-to-air missiles.
local function _GetTarget(_unit)
local unit=_unit --Wrapper.Unit#UNIT
-- Player position.
local playerCoord=unit:GetCoordinate()
-- Distance.
local dist=missileCoord:Get3DDistance(playerCoord)
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
if dist<=self.explosiondist then
return unit
end
end
-- Distance to closest player.
local mindist=nil
-- Loop over players.
for _,_player in pairs(self.players) do
local player=_player --#FOX.PlayerData
-- Check that player was not the one who launched the missile.
if player.unitname~=missile.shooterName then
-- Player position.
local playerCoord=player.unit:GetCoordinate()
-- Distance.
local dist=missileCoord:Get3DDistance(playerCoord)
-- Distance from shooter to player.
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
mindist=dist
target=player.unit
end
end
end
if self.protectedset then
-- Distance to closest protected unit.
mindist=nil
for _,_group in pairs(self.protectedset:GetSet()) do
local group=_group --Wrapper.Group#GROUP
for _,_unit in pairs(group:GetUnits()) do
local unit=_unit --Wrapper.Unit#UNIT
if unit and unit:IsAlive() then
-- Check that player was not the one who launched the missile.
if unit:GetName()~=missile.shooterName then
-- Player position.
local playerCoord=unit:GetCoordinate()
-- Distance.
local dist=missileCoord:Get3DDistance(playerCoord)
-- Distance from shooter to player.
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
mindist=dist
target=unit
end
end
end
end
end
end
if target then
self:T(self.lid..string.format("Missile %s with NO explicit target got closest unit to missile as target %s. Dist=%s m", missile.missileType, target:GetName(), tostring(mindist)))
end
end
-- Check if missile has a valid target.
if target then
-- Target coordinate.
local targetCoord=target:GetCoordinate()
-- Distance from missile to target.
local distance=missileCoord:Get3DDistance(targetCoord)
-- Distance missile to shooter.
local distShooter=nil
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetCoordinate())
end
-- Debug output.
if self.Debug then
local bearing=targetCoord:HeadingTo(missileCoord)
local eta=distance/missileVelocity
-- Debug distance check.
self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta))
end
-- Distroy missile if it's getting too close.
local destroymissile=distance<=self.explosiondist
-- Check BIG missiles.
if self.explosiondist2 and distance<=self.explosiondist2 and not destroymissile then
destroymissile=missile.explosive>=self.bigmissilemass
end
-- If missile is 150 m from target ==> destroy missile if in safe zone.
if destroymissile and self:_CheckCoordSafe(targetCoord) then
-- Destroy missile.
self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance))
_ordnance:destroy()
-- Missile is not active any more.
missile.active=false
-- Debug smoke.
if self.Debug then
missileCoord:SmokeRed()
targetCoord:SmokeGreen()
end
-- Create event.
self:MissileDestroyed(missile)
-- Little explosion for the visual effect.
if self.explosionpower>0 and distance>50 and (distShooter==nil or (distShooter and distShooter>50)) then
missileCoord:Explosion(self.explosionpower)
end
-- Target was a player.
if missile.targetPlayer then
-- Message to target.
local text=string.format("Destroying missile. %s", self:_DeadText())
MESSAGE:New(text, 10):ToGroup(target:GetGroup())
-- Increase dead counter.
missile.targetPlayer.dead=missile.targetPlayer.dead+1
end
-- Terminate timer.
return nil
else
-- Time step.
local dt=1.0
if distance>50000 then
-- > 50 km
dt=self.dt50 --=5.0
elseif distance>10000 then
-- 10-50 km
dt=self.dt10 --=1.0
elseif distance>5000 then
-- 5-10 km
dt=self.dt05 --0.5
elseif distance>1000 then
-- 1-5 km
dt=self.dt01 --0.1
else
-- < 1 km
dt=self.dt00 --0.01
end
-- Check again in dt seconds.
return timer.getTime()+dt
end
else
-- Destroy missile.
self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName))
return timer.getTime()+0.1
-- No target ==> terminate timer.
--return nil
end
else
-------------------------------------
-- Missile does not exist any more --
-------------------------------------
if target then
-- Get human player.
local player=self:_GetPlayerFromUnit(target)
-- Check for player and distance < 10 km.
if player and player.unit:IsAlive() then -- and missileCoord and player.unit:GetCoordinate():Get3DDistance(missileCoord)<10*1000 then
local text=string.format("Missile defeated. Well done, %s!", player.name)
MESSAGE:New(text, 10):ToClient(player.client)
-- Increase defeated counter.
player.defeated=player.defeated+1
end
end
-- Missile is not active any more.
missile.active=false
--Terminate the timer.
self:T(FOX.lid..string.format("Terminating missile track timer."))
return nil
end -- _status check
end -- end function trackBomb
-- Weapon is not yet "alife" just yet. Start timer with a little delay. -- Weapon is not yet "alife" just yet. Start timer with a little delay.
self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds.")) self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds."))
timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001) --timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001)
missile.Weapon:StartTrack(0.0001)
end end
@ -1247,29 +1251,28 @@ end
function FOX:OnEventShot(EventData) function FOX:OnEventShot(EventData)
self:T2({eventshot=EventData}) self:T2({eventshot=EventData})
if EventData.Weapon==nil then -- Nil checks.
return if EventData.Weapon==nil or EventData.IniDCSUnit==nil or EventData.weapon==nil then
end
if EventData.IniDCSUnit==nil then
return return
end end
-- Create a weapon object.
local weapon=WEAPON:New(EventData.weapon)
-- Weapon data. -- Weapon data.
local _weapon = EventData.WeaponName local _weapon = weapon:GetTypeName()
local _target = EventData.Weapon:getTarget() local _target = EventData.Weapon:getTarget()
local _targetName = "unknown" local _targetName = "unknown"
local _targetUnit = nil --Wrapper.Unit#UNIT local _targetUnit = nil --Wrapper.Unit#UNIT
-- Weapon descriptor. -- Weapon descriptor.
local desc=EventData.Weapon:getDesc() local desc=weapon.desc
self:T2({desc=desc}) self:T2({desc=desc})
-- Weapon category: 0=Shell, 1=Missile, 2=Rocket, 3=BOMB
local weaponcategory=desc.category
-- Missile category: 1=AAM, 2=SAM, 6=OTHER -- Missile category: 1=AAM, 2=SAM, 6=OTHER
local missilecategory=desc.missileCategory local missilecategory=desc.missileCategory
-- Missile range.
local missilerange=nil local missilerange=nil
if missilecategory then if missilecategory then
missilerange=desc.rangeMaxAltMax missilerange=desc.rangeMaxAltMax
@ -1279,8 +1282,8 @@ function FOX:OnEventShot(EventData)
self:T2(FOX.lid.."EVENT SHOT: FOX") self:T2(FOX.lid.."EVENT SHOT: FOX")
self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s", tostring(EventData.IniUnitName))) self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s", tostring(EventData.IniUnitName)))
self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s", tostring(EventData.IniGroupName))) self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s", tostring(EventData.IniGroupName)))
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(_weapon))) self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(weapon:GetTypeName())))
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weaponcategory))) self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weapon:GetCategory())))
self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s", tostring(missilecategory))) self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s", tostring(missilecategory)))
self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s", tostring(missilerange))) self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s", tostring(missilerange)))
@ -1292,7 +1295,7 @@ function FOX:OnEventShot(EventData)
end end
-- Track missiles of type AAM=1, SAM=2 or OTHER=6 -- Track missiles of type AAM=1, SAM=2 or OTHER=6
local _track = weaponcategory==1 and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6) local _track = weapon:IsMissile() and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
-- Only track missiles -- Only track missiles
if _track then if _track then
@ -1301,6 +1304,7 @@ function FOX:OnEventShot(EventData)
missile.active=true missile.active=true
missile.weapon=EventData.weapon missile.weapon=EventData.weapon
missile.Weapon=weapon
missile.missileType=_weapon missile.missileType=_weapon
missile.missileRange=missilerange missile.missileRange=missilerange
missile.missileName=EventData.weapon:getName() missile.missileName=EventData.weapon:getName()
@ -1313,6 +1317,7 @@ function FOX:OnEventShot(EventData)
missile.fuseDist=desc.fuseDist missile.fuseDist=desc.fuseDist
missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass
missile.targetOrig=missile.targetName missile.targetOrig=missile.targetName
missile.missileCoord=COORDINATE:New(0,0,0)
-- Set missile target name, unit and player. -- Set missile target name, unit and player.
self:GetMissileTarget(missile) self:GetMissileTarget(missile)
@ -1631,7 +1636,7 @@ end
--- Check if a coordinate lies within a safe training zone. --- Check if a coordinate lies within a safe training zone.
-- @param #FOX self -- @param #FOX self
-- @param Core.Point#COORDINATE coord Coordinate to check. -- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec3.
-- @return #boolean True if safe. -- @return #boolean True if safe.
function FOX:_CheckCoordSafe(coord) function FOX:_CheckCoordSafe(coord)
@ -1643,7 +1648,9 @@ function FOX:_CheckCoordSafe(coord)
-- Loop over all zones. -- Loop over all zones.
for _,_zone in pairs(self.safezones) do for _,_zone in pairs(self.safezones) do
local zone=_zone --Core.Zone#ZONE local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsCoordinateInZone(coord) local Vec2={x=coord.x, y=coord.z}
local inzone=zone:IsVec2InZone(Vec2)
--local inzone=zone:IsCoordinateInZone(coord)
if inzone then if inzone then
return true return true
end end
@ -1654,7 +1661,7 @@ end
--- Check if a coordinate lies within a launch zone. --- Check if a coordinate lies within a launch zone.
-- @param #FOX self -- @param #FOX self
-- @param Core.Point#COORDINATE coord Coordinate to check. -- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec2.
-- @return #boolean True if in launch zone. -- @return #boolean True if in launch zone.
function FOX:_CheckCoordLaunch(coord) function FOX:_CheckCoordLaunch(coord)
@ -1666,7 +1673,9 @@ function FOX:_CheckCoordLaunch(coord)
-- Loop over all zones. -- Loop over all zones.
for _,_zone in pairs(self.launchzones) do for _,_zone in pairs(self.launchzones) do
local zone=_zone --Core.Zone#ZONE local zone=_zone --Core.Zone#ZONE
local inzone=zone:IsCoordinateInZone(coord) local Vec2={x=coord.x, y=coord.z}
local inzone=zone:IsVec2InZone(Vec2)
--local inzone=zone:IsCoordinateInZone(coord)
if inzone then if inzone then
return true return true
end end

File diff suppressed because it is too large Load Diff

View File

@ -68,7 +68,7 @@ ARMYGROUP = {
--- Army Group version. --- Army Group version.
-- @field #string version -- @field #string version
ARMYGROUP.version="0.9.0" ARMYGROUP.version="1.0.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list

View File

@ -158,6 +158,7 @@
-- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate. -- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate.
-- @field Core.Point#COORDINATE missionEgressCoord Mission egress waypoint coordinate. -- @field Core.Point#COORDINATE missionEgressCoord Mission egress waypoint coordinate.
-- @field #number missionWaypointRadius Random radius in meters. -- @field #number missionWaypointRadius Random radius in meters.
-- @field #boolean legionReturn If `true`, assets return to their legion (default). If `false`, they will stay alive.
-- --
-- @field #table enrouteTasks Mission enroute tasks. -- @field #table enrouteTasks Mission enroute tasks.
-- --
@ -639,7 +640,7 @@ AUFTRAG.Category={
--- AUFTRAG class version. --- AUFTRAG class version.
-- @field #string version -- @field #string version
AUFTRAG.version="0.9.10" AUFTRAG.version="1.1.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -648,6 +649,7 @@ AUFTRAG.version="0.9.10"
-- TODO: Replace engageRange by missionRange. Here and in other classes. CTRL+H is your friend! -- TODO: Replace engageRange by missionRange. Here and in other classes. CTRL+H is your friend!
-- TODO: Mission success options damaged, destroyed. -- TODO: Mission success options damaged, destroyed.
-- TODO: F10 marker to create new missions. -- TODO: F10 marker to create new missions.
-- DONE: Add option that assets do not return to their legion.
-- DONE: Add Capture zone task. -- DONE: Add Capture zone task.
-- DONE: Add orbit mission for moving anker points. -- DONE: Add orbit mission for moving anker points.
-- DONE: Add recovery tanker mission for boat ops. -- DONE: Add recovery tanker mission for boat ops.
@ -1039,7 +1041,7 @@ end
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Where to orbit. -- @param Core.Point#COORDINATE Coordinate Where to orbit.
-- @param #number Altitude Orbit altitude in feet above sea level. Default is y component of `Coordinate`. -- @param #number Altitude Orbit altitude in feet above sea level. Default is y component of `Coordinate`.
-- @param #number Speed Orbit speed in knots. Default 350 KIAS. -- @param #number Speed Orbit indicated airspeed in knots at the set altitude ASL. Default 350 KIAS.
-- @param #number Heading Heading of race-track pattern in degrees. If not specified, a circular orbit is performed. -- @param #number Heading Heading of race-track pattern in degrees. If not specified, a circular orbit is performed.
-- @param #number Leg Length of race-track in NM. If not specified, a circular orbit is performed. -- @param #number Leg Length of race-track in NM. If not specified, a circular orbit is performed.
-- @return #AUFTRAG self -- @return #AUFTRAG self
@ -1058,7 +1060,8 @@ function AUFTRAG:NewORBIT(Coordinate, Altitude, Speed, Heading, Leg)
end end
-- Orbit speed in m/s. -- Orbit speed in m/s.
mission.orbitSpeed = UTILS.KnotsToMps(UTILS.KnotsToAltKIAS(Speed or 350, UTILS.MetersToFeet(mission.orbitAltitude))) --mission.orbitSpeed = UTILS.KnotsToMps(UTILS.KnotsToAltKIAS(Speed or 350, UTILS.MetersToFeet(mission.orbitAltitude)))
mission.orbitSpeed = UTILS.TasToIas(UTILS.KnotsToMps(Speed or 350), mission.orbitAltitude)
-- Mission speed in km/h. -- Mission speed in km/h.
mission.missionSpeed = UTILS.KnotsToKmph(Speed or 350) mission.missionSpeed = UTILS.KnotsToKmph(Speed or 350)
@ -1093,7 +1096,7 @@ end
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Position where to orbit around. -- @param Core.Point#COORDINATE Coordinate Position where to orbit around.
-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. -- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`.
-- @param #number Speed Orbit speed in knots. Default 350 KIAS. -- @param #number Speed Orbit indicated airspeed in knots at the set altitude ASL. Default 350 KIAS.
-- @return #AUFTRAG self -- @return #AUFTRAG self
function AUFTRAG:NewORBIT_CIRCLE(Coordinate, Altitude, Speed) function AUFTRAG:NewORBIT_CIRCLE(Coordinate, Altitude, Speed)
@ -1106,7 +1109,7 @@ end
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Where to orbit. -- @param Core.Point#COORDINATE Coordinate Where to orbit.
-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. -- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`.
-- @param #number Speed Orbit speed in knots. Default 350 KIAS. -- @param #number Speed Orbit indicated airspeed in knots at the set altitude ASL. Default 350 KIAS.
-- @param #number Heading Heading of race-track pattern in degrees. Default random in [0, 360) degrees. -- @param #number Heading Heading of race-track pattern in degrees. Default random in [0, 360) degrees.
-- @param #number Leg Length of race-track in NM. Default 10 NM. -- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @return #AUFTRAG self -- @return #AUFTRAG self
@ -1124,7 +1127,7 @@ end
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Wrapper.Group#GROUP Group Group where to orbit around. Can also be a UNIT object. -- @param Wrapper.Group#GROUP Group Group where to orbit around. Can also be a UNIT object.
-- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft. -- @param #number Altitude Orbit altitude in feet. Default is 6,000 ft.
-- @param #number Speed Orbit speed in knots. Default 350 KIAS. -- @param #number Speed Orbit indicated airspeed in knots at the set altitude ASL. Default 350 KIAS.
-- @param #number Leg Length of race-track in NM. Default nil. -- @param #number Leg Length of race-track in NM. Default nil.
-- @param #number Heading Heading of race-track pattern in degrees. Default is heading of the group. -- @param #number Heading Heading of race-track pattern in degrees. Default is heading of the group.
-- @param DCS#Vec2 OffsetVec2 Offset 2D-vector {x=0, y=0} in NM with respect to the group. Default directly overhead. Can also be given in polar coordinates `{r=5, phi=45}`. -- @param DCS#Vec2 OffsetVec2 Offset 2D-vector {x=0, y=0} in NM with respect to the group. Default directly overhead. Can also be given in polar coordinates `{r=5, phi=45}`.
@ -1172,7 +1175,7 @@ end
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Where to orbit. -- @param Core.Point#COORDINATE Coordinate Where to orbit.
-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. -- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`.
-- @param #number Speed Orbit speed in knots. Default 350 kts. -- @param #number Speed Orbit indicated airspeed in knots at the set altitude ASL. Default 350 KIAS.
-- @param #number Heading Heading of race-track pattern in degrees. Default random in [0, 360) degrees. -- @param #number Heading Heading of race-track pattern in degrees. Default random in [0, 360) degrees.
-- @param #number Leg Length of race-track in NM. Default 10 NM. -- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @return #AUFTRAG self -- @return #AUFTRAG self
@ -1199,7 +1202,7 @@ end
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Where to orbit. -- @param Core.Point#COORDINATE Coordinate Where to orbit.
-- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`. -- @param #number Altitude Orbit altitude in feet. Default is y component of `Coordinate`.
-- @param #number Speed Orbit speed in knots. Default 350 kts. -- @param #number Speed Orbit indicated airspeed in knots at the set altitude ASL. Default 350 KIAS.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). -- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM. -- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @param #number RefuelSystem Refueling system (0=boom, 1=probe). This info is *only* for AIRWINGs so they launch the right tanker type. -- @param #number RefuelSystem Refueling system (0=boom, 1=probe). This info is *only* for AIRWINGs so they launch the right tanker type.
@ -1296,11 +1299,7 @@ end
function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, TargetTypes) function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, TargetTypes)
-- Ensure given TargetTypes parameter is a table. -- Ensure given TargetTypes parameter is a table.
if TargetTypes then TargetTypes=UTILS.EnsureTable(TargetTypes, true)
if type(TargetTypes)~="table" then
TargetTypes={TargetTypes}
end
end
-- Create ORBIT first. -- Create ORBIT first.
local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAP:GetCoordinate(), Altitude or 10000, Speed or 350, Heading, Leg) local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAP:GetCoordinate(), Altitude or 10000, Speed or 350, Heading, Leg)
@ -1342,11 +1341,8 @@ end
function AUFTRAG:NewCAPGROUP(Grp, Altitude, Speed, RelHeading, Leg, OffsetDist, OffsetAngle, UpdateDistance, TargetTypes, EngageRange) function AUFTRAG:NewCAPGROUP(Grp, Altitude, Speed, RelHeading, Leg, OffsetDist, OffsetAngle, UpdateDistance, TargetTypes, EngageRange)
-- Ensure given TargetTypes parameter is a table. -- Ensure given TargetTypes parameter is a table.
if TargetTypes then TargetTypes=UTILS.EnsureTable(TargetTypes, true)
if type(TargetTypes)~="table" then
TargetTypes={TargetTypes}
end
end
-- Six NM astern. -- Six NM astern.
local OffsetVec2={r=OffsetDist or 6, phi=OffsetAngle or 180} local OffsetVec2={r=OffsetDist or 6, phi=OffsetAngle or 180}
@ -1395,11 +1391,7 @@ end
function AUFTRAG:NewCAS(ZoneCAS, Altitude, Speed, Coordinate, Heading, Leg, TargetTypes) function AUFTRAG:NewCAS(ZoneCAS, Altitude, Speed, Coordinate, Heading, Leg, TargetTypes)
-- Ensure given TargetTypes parameter is a table. -- Ensure given TargetTypes parameter is a table.
if TargetTypes then TargetTypes=UTILS.EnsureTable(TargetTypes, true)
if type(TargetTypes)~="table" then
TargetTypes={TargetTypes}
end
end
-- Create ORBIT first. -- Create ORBIT first.
local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAS:GetCoordinate(), Altitude or 10000, Speed, Heading, Leg) local mission=AUFTRAG:NewORBIT(Coordinate or ZoneCAS:GetCoordinate(), Altitude or 10000, Speed, Heading, Leg)
@ -2428,9 +2420,9 @@ function AUFTRAG:NewFromTarget(Target, MissionType)
elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then elseif MissionType==AUFTRAG.Type.BOMBRUNWAY then
mission=self:NewBOMBRUNWAY(Target, Altitude) mission=self:NewBOMBRUNWAY(Target, Altitude)
elseif MissionType==AUFTRAG.Type.CAS then elseif MissionType==AUFTRAG.Type.CAS then
mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,Target:GetAverageCoordinate(),Heading,Leg,TargetTypes) mission=self:NewCAS(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, Target:GetAverageCoordinate(), Heading, Leg, TargetTypes)
elseif MissionType==AUFTRAG.Type.CASENHANCED then elseif MissionType==AUFTRAG.Type.CASENHANCED then
mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000),Altitude,Speed,RangeMax,NoEngageZoneSet,TargetTypes) mission=self:NewCASENHANCED(ZONE_RADIUS:New(Target:GetName(),Target:GetVec2(),1000), Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes)
elseif MissionType==AUFTRAG.Type.INTERCEPT then elseif MissionType==AUFTRAG.Type.INTERCEPT then
mission=self:NewINTERCEPT(Target) mission=self:NewINTERCEPT(Target)
elseif MissionType==AUFTRAG.Type.SEAD then elseif MissionType==AUFTRAG.Type.SEAD then
@ -2668,6 +2660,17 @@ function AUFTRAG:SetTeleport(Switch)
return self return self
end end
--- **[LEGION, COMMANDER, CHIEF]** Set whether assigned assets return to their legion once the mission is over. This is only applicable to **army** and **navy** groups, *i.e.* aircraft
-- will always return.
-- @param #AUFTRAG self
-- @param #boolean Switch If `true`, assets will return. If `false`, assets will not return and stay where it finishes its last mission. If `nil`, let asset decide.
-- @return #AUFTRAG self
function AUFTRAG:SetReturnToLegion(Switch)
self.legionReturn=Switch
self:T(self.lid..string.format("Setting ReturnToLetion=%s", tostring(self.legionReturn)))
return self
end
--- Set mission push time. This is the time the mission is executed. If the push time is not passed, the group will wait at the mission execution waypoint. --- Set mission push time. This is the time the mission is executed. If the push time is not passed, the group will wait at the mission execution waypoint.
-- @param #AUFTRAG self -- @param #AUFTRAG self
@ -3375,7 +3378,7 @@ function AUFTRAG:GetPriority()
return self.prio return self.prio
end end
--- Get casualties, i.e. number of units that died during this mission. --- Get casualties, *i.e.* number of own units that died during this mission.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @return #number Number of dead units. -- @return #number Number of dead units.
function AUFTRAG:GetCasualties() function AUFTRAG:GetCasualties()
@ -3903,6 +3906,15 @@ function AUFTRAG:onafterStatus(From, Event, To)
local Nassigned=self.Nassigned and self.Nassigned-self.Ndead or 0 local Nassigned=self.Nassigned and self.Nassigned-self.Ndead or 0
-- check conditions if set
local conditionDone=false
if self.conditionFailureSet then
conditionDone = self:EvalConditionsAny(self.conditionFailure)
end
if self.conditionSuccessSet and not conditionDone then
conditionDone = self:EvalConditionsAny(self.conditionSuccess)
end
-- Check if mission is not OVER yet. -- Check if mission is not OVER yet.
if self:IsNotOver() then if self:IsNotOver() then
@ -3916,6 +3928,11 @@ function AUFTRAG:onafterStatus(From, Event, To)
-- Cancel mission if stop time passed. -- Cancel mission if stop time passed.
self:Cancel() self:Cancel()
elseif conditionDone then
-- Cancel mission if conditions were met.
self:Cancel()
elseif self.durationExe and self.Texecuting and Tnow-self.Texecuting>self.durationExe then elseif self.durationExe and self.Texecuting and Tnow-self.Texecuting>self.durationExe then
-- Backup repeat values -- Backup repeat values
@ -4015,17 +4032,6 @@ function AUFTRAG:onafterStatus(From, Event, To)
self:I(self.lid..text) self:I(self.lid..text)
end end
-- check conditions if set
if self.conditionFailureSet then
local failed = self:EvalConditionsAny(self.conditionFailure)
if failed then self:__Failed(-1) end
end
if self.conditionSuccessSet then
local success = self:EvalConditionsAny(self.conditionSuccess)
if success then self:__Success(-1) end
end
-- Ready to evaluate mission outcome? -- Ready to evaluate mission outcome?
local ready2evaluate=self.Tover and Tnow-self.Tover>=self.dTevaluate or false local ready2evaluate=self.Tover and Tnow-self.Tover>=self.dTevaluate or false

View File

@ -216,7 +216,7 @@ FLIGHTGROUP.Players={}
--- FLIGHTGROUP class version. --- FLIGHTGROUP class version.
-- @field #string version -- @field #string version
FLIGHTGROUP.version="0.8.4" FLIGHTGROUP.version="1.0.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list

View File

@ -1984,9 +1984,9 @@ end
--- Count total number of assets of the legion. --- Count total number of assets of the legion.
-- @param #LEGION self -- @param #LEGION self
-- @param #boolean InStock If `true`, only assets that are in the warehouse stock/inventory are counted. -- @param #boolean InStock If `true`, only assets that are in the warehouse stock/inventory are counted. If `false`, only assets that are NOT in stock (i.e. spawned) are counted. If `nil`, all assets are counted.
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types. -- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
-- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`. -- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `GROUP.Attribute.AIR_BOMBER`.
-- @return #number Amount of asset groups in stock. -- @return #number Amount of asset groups in stock.
function LEGION:CountAssets(InStock, MissionTypes, Attributes) function LEGION:CountAssets(InStock, MissionTypes, Attributes)
@ -3147,10 +3147,18 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
-- Distance factor. -- Distance factor.
local distance=0 local distance=0
if TargetVec2 and OrigVec2 then if TargetVec2 and OrigVec2 then
-- Distance in NM. -- Distance in NM.
distance=UTILS.MetersToNM(UTILS.VecDist2D(OrigVec2, TargetVec2)) distance=UTILS.MetersToNM(UTILS.VecDist2D(OrigVec2, TargetVec2))
-- Round: 55 NM ==> 5.5 ==> 6, 63 NM ==> 6.3 ==> 6
distance=UTILS.Round(distance/10, 0) if asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then
-- Round: 55 NM ==> 5.5 ==> 6, 63 NM ==> 6.3 ==> 6
distance=UTILS.Round(distance/10, 0)
else
-- For ground units the distance is a more important factor
distance=UTILS.Round(distance, 0)
end
end end
-- Reduce score for legions that are futher away. -- Reduce score for legions that are futher away.

View File

@ -90,7 +90,7 @@ NAVYGROUP = {
--- NavyGroup version. --- NavyGroup version.
-- @field #string version -- @field #string version
NAVYGROUP.version="0.7.9" NAVYGROUP.version="1.0.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -199,11 +199,6 @@ function NAVYGROUP:New(group)
-- @param #number Speed Speed in knots until next waypoint is reached. -- @param #number Speed Speed in knots until next waypoint is reached.
--- Triggers the FSM event "TurnIntoWind". --- Triggers the FSM event "TurnIntoWind".
-- @function [parent=#NAVYGROUP] TurnIntoWind -- @function [parent=#NAVYGROUP] TurnIntoWind
-- @param #NAVYGROUP self -- @param #NAVYGROUP self
@ -1038,6 +1033,7 @@ end
-- @param #number Speed Speed in knots to the next waypoint. -- @param #number Speed Speed in knots to the next waypoint.
-- @param #number Depth Depth in meters to the next waypoint. -- @param #number Depth Depth in meters to the next waypoint.
function NAVYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Depth) function NAVYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Depth)
-- Is transition allowed? We assume yes until proven otherwise. -- Is transition allowed? We assume yes until proven otherwise.
local allowed=true local allowed=true
local trepeat=nil local trepeat=nil
@ -1057,6 +1053,9 @@ function NAVYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Depth)
elseif self:IsHolding() then elseif self:IsHolding() then
self:T(self.lid.."Update route denied. Group is holding position!") self:T(self.lid.."Update route denied. Group is holding position!")
return false return false
elseif self:IsEngaging() then
self:T(self.lid.."Update route allowed. Group is engaging!")
return true
end end
-- Check for a current task. -- Check for a current task.
@ -1075,6 +1074,9 @@ function NAVYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Depth)
elseif task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then elseif task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then
-- For relocate -- For relocate
self:T2(self.lid.."Allowing update route for Task: Relocate Cohort") self:T2(self.lid.."Allowing update route for Task: Relocate Cohort")
elseif task.dcstask.id==AUFTRAG.SpecialTask.REARMING then
-- For rearming
self:T2(self.lid.."Allowing update route for Task: Rearming")
else else
local taskname=task and task.description or "No description" local taskname=task and task.description or "No description"
self:T(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s", self.taskcurrent, tostring(taskname))) self:T(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s", self.taskcurrent, tostring(taskname)))
@ -1106,7 +1108,6 @@ function NAVYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Depth)
end end
return allowed return allowed
end end
--- On after "UpdateRoute" event. --- On after "UpdateRoute" event.

View File

@ -496,11 +496,13 @@ OPSGROUP.CargoStatus={
-- @field #OPSGROUP opsgroup The cargo opsgroup. -- @field #OPSGROUP opsgroup The cargo opsgroup.
-- @field #boolean delivered If `true`, group was delivered. -- @field #boolean delivered If `true`, group was delivered.
-- @field #boolean disembarkActivation If `true`, group is activated. If `false`, group is late activated. -- @field #boolean disembarkActivation If `true`, group is activated. If `false`, group is late activated.
-- @field Core.Zone#ZONE disembarkZone Zone where this group is disembarked to.
-- @field Core.Set#SET_OPSGROUP disembarkCarriers Carriers where this group is directly disembared to.
-- @field #string status Status of the cargo group. Not used yet. -- @field #string status Status of the cargo group. Not used yet.
--- OpsGroup version. --- OpsGroup version.
-- @field #string version -- @field #string version
OPSGROUP.version="0.9.0" OPSGROUP.version="1.0.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -624,6 +626,9 @@ function OPSGROUP:New(group)
-- Set Default altitude. -- Set Default altitude.
self:SetDefaultAltitude() self:SetDefaultAltitude()
-- Group will return to its legion when done.
self:SetReturnToLegion()
-- Laser. -- Laser.
self.spot={} self.spot={}
self.spot.On=false self.spot.On=false
@ -1009,6 +1014,20 @@ function OPSGROUP:_SetLegion(Legion)
return self return self
end end
--- **[GROUND, NAVAL]** Set whether this group should return to its legion once all mission etc are finished. Only for ground and naval groups. Aircraft will
-- @param #OPSGROUP self
-- @param #boolean Switch If `true` or `nil`, group will return. If `false`, group will not return and stay where it finishes its last mission.
-- @return #OPSGROUP self
function OPSGROUP:SetReturnToLegion(Switch)
if Switch==false then
self.legionReturn=false
else
self.legionReturn=true
end
self:T(self.lid..string.format("Setting ReturnToLetion=%s", tostring(self.legionReturn)))
return self
end
--- Set default cruise speed. --- Set default cruise speed.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #number Speed Speed in knots. -- @param #number Speed Speed in knots.
@ -1603,6 +1622,31 @@ function OPSGROUP:SetReturnOnOutOfAmmo()
return self return self
end end
--- Set max weight that each unit of the group can handle.
-- @param #OPSGROUP self
-- @param #number Weight Max weight of cargo in kg the unit can carry.
-- @param #string UnitName Name of the Unit. If not given, weight is set for all units of the group.
-- @return #OPSGROUP self
function OPSGROUP:SetCargoBayLimit(Weight, UnitName)
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
if UnitName==nil or UnitName==element.name then
element.weightMaxCargo=Weight
if element.unit then
element.unit:SetCargoBayWeightLimit(Weight)
end
end
end
return self
end
--- Check if an element of the group has line of sight to a coordinate. --- Check if an element of the group has line of sight to a coordinate.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. Can also be a DCS#Vec3. -- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. Can also be a DCS#Vec3.
@ -1639,7 +1683,7 @@ function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate)
-- Check los for the given element. -- Check los for the given element.
if Element.unit and Element.unit:IsAlive() then if Element.unit and Element.unit:IsAlive() then
local vec3=Element.unit:GetVec3() local vec3=Element.unit:GetVec3()
local los=checklos(Element) local los=checklos(vec3)
return los return los
end end
else else
@ -4360,7 +4404,7 @@ function OPSGROUP:_UpdateTask(Task, Mission)
if self:IsArmygroup() or self:IsNavygroup() then if self:IsArmygroup() or self:IsNavygroup() then
-- Especially NAVYGROUP needs a full stop as patrol ad infinitum -- Especially NAVYGROUP needs a full stop as patrol ad infinitum
self:FullStop() self:__FullStop(0.1)
else else
-- FLIGHTGROUP not implemented (intended!) for this AUFTRAG type. -- FLIGHTGROUP not implemented (intended!) for this AUFTRAG type.
end end
@ -5313,7 +5357,8 @@ function OPSGROUP:onafterMissionStart(From, Event, To, Mission)
-- IMMOBILE Group -- IMMOBILE Group
--- ---
env.info(self.lid.."FF Immobile GROUP") -- Debug info.
self:T(self.lid.."Immobile GROUP!")
-- Add waypoint task. UpdateRoute is called inside. -- Add waypoint task. UpdateRoute is called inside.
local Clock=Mission.Tpush and UTILS.SecondsToClock(Mission.Tpush) or 5 local Clock=Mission.Tpush and UTILS.SecondsToClock(Mission.Tpush) or 5
@ -5614,6 +5659,11 @@ function OPSGROUP:onafterMissionDone(From, Event, To, Mission)
self:_SwitchICLS() self:_SwitchICLS()
end end
-- Return to legion?
if self.legion and Mission.legionReturn~=nil then
self:SetReturnToLegion(Mission.legionReturn)
end
-- Delay before check if group is done. -- Delay before check if group is done.
local delay=1 local delay=1
@ -5940,7 +5990,7 @@ function OPSGROUP:RouteToMission(mission, delay)
formation=ENUMS.Formation.Vehicle.OffRoad formation=ENUMS.Formation.Vehicle.OffRoad
end end
waypoint=ARMYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, formation, false) waypoint=ARMYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, formation, false)
elseif self:IsNavygroup() then elseif self:IsNavygroup() then
@ -7043,6 +7093,8 @@ function OPSGROUP:SetLaserTarget(Target)
-- Set coordinate. -- Set coordinate.
self.spot.Coordinate:UpdateFromVec3(self.spot.vec3) self.spot.Coordinate:UpdateFromVec3(self.spot.vec3)
self.spot.Coordinate:MarkToAll("Target Laser",ReadOnly,Text)
end end
end end
@ -7881,9 +7933,15 @@ function OPSGROUP:_CheckCargoTransport()
self.cargoTZC=nil self.cargoTZC=nil
end end
-- Get current mission (if any).
local mission=self:GetMissionCurrent()
-- Check if there is anything in the queue. -- Check if there is anything in the queue.
if not self.cargoTransport and not self:IsOnMission() then if (not self.cargoTransport) and (mission==nil or mission.type==AUFTRAG.Type.NOTHING) then
self.cargoTransport=self:_GetNextCargoTransport() self.cargoTransport=self:_GetNextCargoTransport()
if self.cargoTransport and mission then
self:MissionCancel(mission)
end
if self.cargoTransport and not self:IsActive() then if self.cargoTransport and not self:IsActive() then
self:Activate() self:Activate()
end end
@ -7957,7 +8015,7 @@ function OPSGROUP:_CheckCargoTransport()
end end
-- Boarding finished ==> Transport cargo. -- Boarding finished ==> Transport cargo.
if gotcargo and self.cargoTransport:_CheckRequiredCargos(self.cargoTZC) and not boarding then if gotcargo and self.cargoTransport:_CheckRequiredCargos(self.cargoTZC, self) and not boarding then
self:T(self.lid.."Boarding finished ==> Loaded") self:T(self.lid.."Boarding finished ==> Loaded")
self:LoadingDone() self:LoadingDone()
else else
@ -8969,7 +9027,7 @@ function OPSGROUP:onafterLoading(From, Event, To)
-- Check if current mission is using this ops transport. -- Check if current mission is using this ops transport.
if isOnMission then if isOnMission then
local mission=cargo.opsgroup:GetMissionCurrent() local mission=cargo.opsgroup:GetMissionCurrent()
if mission and mission.opstransport and mission.opstransport.uid==self.cargoTransport.uid then if mission and ((mission.opstransport and mission.opstransport.uid==self.cargoTransport.uid) or mission.type==AUFTRAG.Type.NOTHING) then
isOnMission=not isHolding isOnMission=not isHolding
end end
end end
@ -9332,6 +9390,8 @@ function OPSGROUP:onafterUnloading(From, Event, To)
-- Set carrier status to UNLOADING. -- Set carrier status to UNLOADING.
self:_NewCarrierStatus(OPSGROUP.CarrierStatus.UNLOADING) self:_NewCarrierStatus(OPSGROUP.CarrierStatus.UNLOADING)
self:T(self.lid.."Unloading..")
-- Deploy zone. -- Deploy zone.
local zone=self.cargoTZC.DisembarkZone or self.cargoTZC.DeployZone --Core.Zone#ZONE local zone=self.cargoTZC.DisembarkZone or self.cargoTZC.DeployZone --Core.Zone#ZONE
@ -9343,9 +9403,16 @@ function OPSGROUP:onafterUnloading(From, Event, To)
if cargo.opsgroup:IsLoaded(self.groupname) and not cargo.opsgroup:IsDead() then if cargo.opsgroup:IsLoaded(self.groupname) and not cargo.opsgroup:IsDead() then
-- Disembark to carrier. -- Disembark to carrier.
local needscarrier=false --#boolean
local carrier=nil --Ops.OpsGroup#OPSGROUP.Element local carrier=nil --Ops.OpsGroup#OPSGROUP.Element
local carrierGroup=nil --Ops.OpsGroup#OPSGROUP local carrierGroup=nil --Ops.OpsGroup#OPSGROUP
local disembarkToCarriers=cargo.disembarkCarriers~=nil or self.cargoTZC.disembarkToCarriers
-- Set specifc zone for this cargo.
if cargo.disembarkZone then
zone=cargo.disembarkZone
end
self:T(self.lid..string.format("Unloading cargo %s to zone %s", cargo.opsgroup:GetName(), zone and zone:GetName() or "No Zone Found!"))
-- Try to get the OPSGROUP if deploy zone is a ship. -- Try to get the OPSGROUP if deploy zone is a ship.
if zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then if zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then
@ -9356,17 +9423,22 @@ function OPSGROUP:onafterUnloading(From, Event, To)
carrier=carrierGroup:GetElementByName(shipname) carrier=carrierGroup:GetElementByName(shipname)
end end
if self.cargoTZC.DisembarkCarriers and #self.cargoTZC.DisembarkCarriers>0 then if disembarkToCarriers then
needscarrier=true -- Debug info.
self:T(self.lid..string.format("Trying to find disembark carriers in zone %s", zone:GetName()))
carrier, carrierGroup=self.cargoTransport:FindTransferCarrierForCargo(cargo.opsgroup, zone, self.cargoTZC) -- Disembarkcarriers.
local disembarkCarriers=cargo.disembarkCarriers or self.cargoTZC.DisembarkCarriers
-- Try to find a carrier that can take the cargo.
carrier, carrierGroup=self.cargoTransport:FindTransferCarrierForCargo(cargo.opsgroup, zone, disembarkCarriers, self.cargoTZC.DeployAirbase)
--TODO: max unloading time if transfer carrier does not arrive in the zone. --TODO: max unloading time if transfer carrier does not arrive in the zone.
end end
if needscarrier==false or (needscarrier and carrier and carrierGroup) then if (disembarkToCarriers and carrier and carrierGroup) or (not disembarkToCarriers) then
-- Cargo was delivered (somehow). -- Cargo was delivered (somehow).
cargo.delivered=true cargo.delivered=true
@ -9385,7 +9457,7 @@ function OPSGROUP:onafterUnloading(From, Event, To)
elseif zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then elseif zone and zone:IsInstanceOf("ZONE_AIRBASE") and zone:GetAirbase():IsShip() then
--- ---
-- Delivered to a ship via helo or VTOL -- Delivered to a ship via helo that landed on its platform
--- ---
-- Issue warning. -- Issue warning.
@ -9408,7 +9480,7 @@ function OPSGROUP:onafterUnloading(From, Event, To)
else else
-- Get disembark zone of this TZC. -- Get disembark zone of this TZC.
local DisembarkZone=self.cargoTransport:GetDisembarkZone(self.cargoTZC) local DisembarkZone=cargo.disembarkZone or self.cargoTransport:GetDisembarkZone(self.cargoTZC)
local Coordinate=nil local Coordinate=nil
@ -9431,7 +9503,7 @@ function OPSGROUP:onafterUnloading(From, Event, To)
Coordinate=zoneCarrier:GetRandomCoordinate() Coordinate=zoneCarrier:GetRandomCoordinate()
else else
env.info(string.format("FF ERROR carrier element nil!")) self:E(self.lid..string.format("ERROR carrier element nil!"))
end end
end end
@ -10258,7 +10330,7 @@ function OPSGROUP:_CheckGroupDone(delay)
-- Passed FINAL waypoint -- Passed FINAL waypoint
--- ---
if self.legion then if self.legion and self.legionReturn then
self:T(self.lid..string.format("Passed final WP, adinfinitum=FALSE, LEGION set ==> RTZ")) self:T(self.lid..string.format("Passed final WP, adinfinitum=FALSE, LEGION set ==> RTZ"))
if self.isArmygroup then if self.isArmygroup then

View File

@ -172,6 +172,7 @@ OPSTRANSPORT.Status={
-- @field #table TransportPaths Path for Transport. Each elment of the table is of type `#OPSTRANSPORT.Path`. -- @field #table TransportPaths Path for Transport. Each elment of the table is of type `#OPSTRANSPORT.Path`.
-- @field #table RequiredCargos Required cargos. -- @field #table RequiredCargos Required cargos.
-- @field #table DisembarkCarriers Carriers where the cargo is directly disembarked to. -- @field #table DisembarkCarriers Carriers where the cargo is directly disembarked to.
-- @field #boolean disembarkToCarriers If `true`, cargo is supposed to embark to another carrier.
-- @field #boolean disembarkActivation If true, troops are spawned in late activated state when disembarked from carrier. -- @field #boolean disembarkActivation If true, troops are spawned in late activated state when disembarked from carrier.
-- @field #boolean disembarkInUtero If true, troops are disembarked "in utero". -- @field #boolean disembarkInUtero If true, troops are disembarked "in utero".
-- @field #boolean assets Cargo assets. -- @field #boolean assets Cargo assets.
@ -195,7 +196,7 @@ _OPSTRANSPORTID=0
--- Army Group version. --- Army Group version.
-- @field #string version -- @field #string version
OPSTRANSPORT.version="0.6.1" OPSTRANSPORT.version="0.7.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -204,6 +205,7 @@ OPSTRANSPORT.version="0.6.1"
-- TODO: Trains. -- TODO: Trains.
-- TODO: Stop transport. -- TODO: Stop transport.
-- TODO: Improve pickup and transport paths. -- TODO: Improve pickup and transport paths.
-- DONE: Disembark parameters per cargo group.
-- DONE: Special transport cohorts/legions. Similar to mission. -- DONE: Special transport cohorts/legions. Similar to mission.
-- DONE: Cancel transport. -- DONE: Cancel transport.
-- DONE: Allow multiple pickup/depoly zones. -- DONE: Allow multiple pickup/depoly zones.
@ -516,8 +518,10 @@ end
-- @param Core.Set#SET_GROUP GroupSet Set of groups to be transported. Can also be passed as a single GROUP or OPSGROUP object. -- @param Core.Set#SET_GROUP GroupSet Set of groups to be transported. Can also be passed as a single GROUP or OPSGROUP object.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @param #boolean DisembarkActivation If `true`, cargo group is activated when disembarked. If `false`, cargo groups are late activated when disembarked. Default `nil` (usually activated). -- @param #boolean DisembarkActivation If `true`, cargo group is activated when disembarked. If `false`, cargo groups are late activated when disembarked. Default `nil` (usually activated).
-- @param Core.Zone#ZONE DisembarkZone Zone where the groups disembark to.
-- @param Core.Set#SET_OPSGROUP DisembarkCarriers Carrier groups where the cargo directly disembarks to.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
function OPSTRANSPORT:AddCargoGroups(GroupSet, TransportZoneCombo, DisembarkActivation) function OPSTRANSPORT:AddCargoGroups(GroupSet, TransportZoneCombo, DisembarkActivation, DisembarkZone, DisembarkCarriers)
-- Use default TZC if no transport zone combo is provided. -- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault TransportZoneCombo=TransportZoneCombo or self.tzcDefault
@ -526,7 +530,7 @@ function OPSTRANSPORT:AddCargoGroups(GroupSet, TransportZoneCombo, DisembarkActi
if GroupSet:IsInstanceOf("GROUP") or GroupSet:IsInstanceOf("OPSGROUP") then if GroupSet:IsInstanceOf("GROUP") or GroupSet:IsInstanceOf("OPSGROUP") then
-- We got a single GROUP or OPSGROUP object. -- We got a single GROUP or OPSGROUP object.
local cargo=self:_CreateCargoGroupData(GroupSet, TransportZoneCombo, DisembarkActivation) local cargo=self:_CreateCargoGroupData(GroupSet, TransportZoneCombo, DisembarkActivation, DisembarkZone, DisembarkCarriers)
if cargo then if cargo then
@ -722,7 +726,7 @@ function OPSTRANSPORT:GetDisembarkActivation(TransportZoneCombo)
return TransportZoneCombo.disembarkActivation return TransportZoneCombo.disembarkActivation
end end
--- Set transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked. --- Set/add transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Core.Set#SET_GROUP Carriers Carrier set. Can also be passed as a #GROUP, #OPSGROUP or #SET_OPSGROUP object. -- @param Core.Set#SET_GROUP Carriers Carrier set. Can also be passed as a #GROUP, #OPSGROUP or #SET_OPSGROUP object.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
@ -735,11 +739,26 @@ function OPSTRANSPORT:SetDisembarkCarriers(Carriers, TransportZoneCombo)
-- Use default TZC if no transport zone combo is provided. -- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault TransportZoneCombo=TransportZoneCombo or self.tzcDefault
-- Set that we want to disembark to carriers.
TransportZoneCombo.disembarkToCarriers=true
self:_AddDisembarkCarriers(Carriers, TransportZoneCombo.DisembarkCarriers)
return self
end
--- Set/add transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked.
-- @param #OPSTRANSPORT self
-- @param Core.Set#SET_GROUP Carriers Carrier set. Can also be passed as a #GROUP, #OPSGROUP or #SET_OPSGROUP object.
-- @param #table Table the table to add.
-- @return #OPSTRANSPORT self
function OPSTRANSPORT:_AddDisembarkCarriers(Carriers, Table)
if Carriers:IsInstanceOf("GROUP") or Carriers:IsInstanceOf("OPSGROUP") then if Carriers:IsInstanceOf("GROUP") or Carriers:IsInstanceOf("OPSGROUP") then
local carrier=self:_GetOpsGroupFromObject(Carriers) local carrier=self:_GetOpsGroupFromObject(Carriers)
if carrier then if carrier then
table.insert(TransportZoneCombo.DisembarkCarriers, carrier) table.insert(Table, carrier)
end end
elseif Carriers:IsInstanceOf("SET_GROUP") or Carriers:IsInstanceOf("SET_OPSGROUP") then elseif Carriers:IsInstanceOf("SET_GROUP") or Carriers:IsInstanceOf("SET_OPSGROUP") then
@ -747,7 +766,7 @@ function OPSTRANSPORT:SetDisembarkCarriers(Carriers, TransportZoneCombo)
for _,object in pairs(Carriers:GetSet()) do for _,object in pairs(Carriers:GetSet()) do
local carrier=self:_GetOpsGroupFromObject(object) local carrier=self:_GetOpsGroupFromObject(object)
if carrier then if carrier then
table.insert(TransportZoneCombo.DisembarkCarriers, carrier) table.insert(Table, carrier)
end end
end end
@ -755,7 +774,7 @@ function OPSTRANSPORT:SetDisembarkCarriers(Carriers, TransportZoneCombo)
self:E(self.lid.."ERROR: Carriers must be a GROUP, OPSGROUP, SET_GROUP or SET_OPSGROUP object!") self:E(self.lid.."ERROR: Carriers must be a GROUP, OPSGROUP, SET_GROUP or SET_OPSGROUP object!")
end end
return self
end end
--- Get transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked. --- Get transfer carrier(s). These are carrier groups, where the cargo is directly loaded into when disembarked.
@ -1568,13 +1587,21 @@ end
-- @return #boolean If true, all possible cargo was delivered. -- @return #boolean If true, all possible cargo was delivered.
function OPSTRANSPORT:IsDelivered(Nmin) function OPSTRANSPORT:IsDelivered(Nmin)
local is=self:is(OPSTRANSPORT.Status.DELIVERED) local is=self:is(OPSTRANSPORT.Status.DELIVERED)
Nmin=Nmin or 0
if Nmin>self.Ncargo then -- Nmin=Nmin or 0
Nmin=self.Ncargo -- if Nmin>self.Ncargo then
end -- Nmin=self.Ncargo
if self.Ndelivered<Nmin then -- end
is=false --
-- if self.Ndelivered<Nmin then
-- is=false
-- end
-- Check if Ndelivered is at least Nmin (if given)
if is==false and Nmin and self.Ndelivered>=math.min(self.Ncargo, Nmin) then
is=true
end end
return is return is
end end
@ -1928,31 +1955,72 @@ end
--- Check if all required cargos are loaded. --- Check if all required cargos are loaded.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @return #boolean If true, all required cargos are loaded or there is no required cargo. -- @param Ops.OpsGroup#OPSGROUP CarrierGroup The carrier group asking.
function OPSTRANSPORT:_CheckRequiredCargos(TransportZoneCombo) -- @return #boolean If true, all required cargos are loaded or there is no required cargo or asking carrier is full.
function OPSTRANSPORT:_CheckRequiredCargos(TransportZoneCombo, CarrierGroup)
-- Use default TZC if no transport zone combo is provided. -- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault TransportZoneCombo=TransportZoneCombo or self.tzcDefault
local requiredCargos=TransportZoneCombo.RequiredCargos -- Use input or take all cargos.
local requiredCargos=TransportZoneCombo.Cargos
-- Check if required cargos was set by user.
if TransportZoneCombo.RequiredCargos and #TransportZoneCombo.RequiredCargos>0 then
requiredCargos=TransportZoneCombo.RequiredCargos
else
requiredCargos={}
for _,_cargo in pairs(TransportZoneCombo.Cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP.CargoGroup
table.insert(requiredCargos, cargo.opsgroup)
end
end
if requiredCargos==nil or #requiredCargos==0 then if requiredCargos==nil or #requiredCargos==0 then
return true return true
end end
-- All carrier names.
local carrierNames=self:_GetCarrierNames() local carrierNames=self:_GetCarrierNames()
local gotit=true -- Cargo groups not loaded yet.
local weightmin=nil
for _,_cargo in pairs(requiredCargos) do for _,_cargo in pairs(requiredCargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP local cargo=_cargo --Ops.OpsGroup#OPSGROUP
-- Is this cargo loaded into any carrier?
local isLoaded=cargo:IsLoaded(carrierNames)
if not cargo:IsLoaded(carrierNames) then if not isLoaded then
return false local weight=cargo:GetWeightTotal()
if weightmin==nil or weight<weightmin then
weightmin=weight
end
end end
end end
if weightmin then
-- Free space of carrier.
local freeSpace=CarrierGroup:GetFreeCargobayMax(true)
-- Debug info.
self:T(self.lid..string.format("Check required cargos for carrier=%s free=%.1f, weight=%.1f", CarrierGroup:GetName(), freeSpace, weightmin))
if weightmin<freeSpace then
-- This group can still take cargo.
return false
else
-- This group is full! Even if there is cargo left, we cannot transport it.
return true
end
end
-- No cargo left.
return true return true
end end
@ -1986,24 +2054,25 @@ end
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Ops.OpsGroup#OPSGROUP CargoGroup The cargo group that needs to be loaded into a carrier unit/element of the carrier group. -- @param Ops.OpsGroup#OPSGROUP CargoGroup The cargo group that needs to be loaded into a carrier unit/element of the carrier group.
-- @param Core.Zone#ZONE Zone (Optional) Zone where the carrier must be in. -- @param Core.Zone#ZONE Zone (Optional) Zone where the carrier must be in.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. -- @param #table DisembarkCarriers Disembark carriers.
-- @param Wrapper.Airbase#AIRBASE DeployAirbase Airbase where to deploy.
-- @return Ops.OpsGroup#OPSGROUP.Element New carrier element for cargo or nil. -- @return Ops.OpsGroup#OPSGROUP.Element New carrier element for cargo or nil.
-- @return Ops.OpsGroup#OPSGROUP New carrier group for cargo or nil. -- @return Ops.OpsGroup#OPSGROUP New carrier group for cargo or nil.
function OPSTRANSPORT:FindTransferCarrierForCargo(CargoGroup, Zone, TransportZoneCombo) function OPSTRANSPORT:FindTransferCarrierForCargo(CargoGroup, Zone, DisembarkCarriers, DeployAirbase)
-- Use default TZC if no transport zone combo is provided. -- Use default TZC if no transport zone combo is provided.
TransportZoneCombo=TransportZoneCombo or self.tzcDefault --TransportZoneCombo=TransportZoneCombo or self.tzcDefault
local carrier=nil --Ops.OpsGroup#OPSGROUP.Element local carrier=nil --Ops.OpsGroup#OPSGROUP.Element
local carrierGroup=nil --Ops.OpsGroup#OPSGROUP local carrierGroup=nil --Ops.OpsGroup#OPSGROUP
--TODO: maybe sort the carriers wrt to largest free cargo bay. Or better smallest free cargo bay that can take the cargo group weight. --TODO: maybe sort the carriers wrt to largest free cargo bay. Or better smallest free cargo bay that can take the cargo group weight.
for _,_carrier in pairs(TransportZoneCombo.DisembarkCarriers) do for _,_carrier in pairs(DisembarkCarriers or {}) do
local carrierGroup=_carrier --Ops.OpsGroup#OPSGROUP local carrierGroup=_carrier --Ops.OpsGroup#OPSGROUP
-- First check if carrier is alive and loading cargo. -- First check if carrier is alive and loading cargo.
if carrierGroup and carrierGroup:IsAlive() and (carrierGroup:IsLoading() or TransportZoneCombo.DeployAirbase) then if carrierGroup and carrierGroup:IsAlive() and (carrierGroup:IsLoading() or DeployAirbase) then
-- Find an element of the group that has enough free space. -- Find an element of the group that has enough free space.
carrier=carrierGroup:FindCarrierForCargo(CargoGroup) carrier=carrierGroup:FindCarrierForCargo(CargoGroup)
@ -2021,6 +2090,7 @@ function OPSTRANSPORT:FindTransferCarrierForCargo(CargoGroup, Zone, TransportZon
end end
end end
self:T2(self.lid.."Could NOT find any carrier that is ALIVE and LOADING (or DELOYAIRBASE))!")
return nil, nil return nil, nil
end end
@ -2029,8 +2099,10 @@ end
-- @param Wrapper.Group#GROUP group The GROUP or OPSGROUP object. -- @param Wrapper.Group#GROUP group The GROUP or OPSGROUP object.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
-- @param #boolean DisembarkActivation If `true`, cargo group is activated when disembarked. -- @param #boolean DisembarkActivation If `true`, cargo group is activated when disembarked.
-- @param Core.Zone#ZONE DisembarkZone Disembark zone, where the cargo is spawned when delivered.
-- @param Core.Set#SET_OPSGROUP DisembarkCarriers Disembark carriers cargo is directly loaded into when delivered.
-- @return Ops.OpsGroup#OPSGROUP.CargoGroup Cargo group data. -- @return Ops.OpsGroup#OPSGROUP.CargoGroup Cargo group data.
function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, DisembarkActivation) function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, DisembarkActivation, DisembarkZone, DisembarkCarriers)
-- Get ops group. -- Get ops group.
local opsgroup=self:_GetOpsGroupFromObject(group) local opsgroup=self:_GetOpsGroupFromObject(group)
@ -2051,8 +2123,12 @@ function OPSTRANSPORT:_CreateCargoGroupData(group, TransportZoneCombo, Disembark
cargo.opsgroup=opsgroup cargo.opsgroup=opsgroup
cargo.delivered=false cargo.delivered=false
cargo.status="Unknown" cargo.status="Unknown"
cargo.disembarkActivation=DisembarkActivation
cargo.tzcUID=TransportZoneCombo cargo.tzcUID=TransportZoneCombo
cargo.disembarkZone=DisembarkZone
if DisembarkCarriers then
cargo.disembarkCarriers={}
self:_AddDisembarkCarriers(DisembarkCarriers, cargo.disembarkCarriers)
end
return cargo return cargo
end end
@ -2077,8 +2153,10 @@ function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier, TransportZone
if mycarrier and mycarrier:IsUnloading() then if mycarrier and mycarrier:IsUnloading() then
-- Get disembark carriers.
local carriers=mycarrier.cargoTransport:GetDisembarkCarriers(mycarrier.cargoTZC) local carriers=mycarrier.cargoTransport:GetDisembarkCarriers(mycarrier.cargoTZC)
-- Check if carrier is in the list.
for _,_carrier in pairs(carriers) do for _,_carrier in pairs(carriers) do
local carrier=_carrier --Ops.OpsGroup#OPSGROUP local carrier=_carrier --Ops.OpsGroup#OPSGROUP
if Carrier:GetName()==carrier:GetName() then if Carrier:GetName()==carrier:GetName() then
@ -2086,6 +2164,20 @@ function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier, TransportZone
end end
end end
if mycarrier.cargoTZC and mycarrier.cargoTZC.Cargos then
for _,_cargodata in pairs(mycarrier.cargoTZC.Cargos) do
local cargodata=_cargodata --Ops.OpsGroup#OPSGROUP.CargoGroup
if cargo:GetName()==cargodata.opsgroup:GetName() then
for _,_carrier in pairs(cargodata.disembarkCarriers) do
local carrier=_carrier --Ops.OpsGroup#OPSGROUP
if Carrier:GetName()==carrier:GetName() then
return true
end
end
end
end
end
end end
return false return false

View File

@ -44,6 +44,7 @@ SOCKET = {
} }
--- Data type. This is the keyword the socket listener uses. --- Data type. This is the keyword the socket listener uses.
-- @type SOCKET.DataType
-- @field #string TEXT Plain text. -- @field #string TEXT Plain text.
-- @field #string BOMBRESULT Range bombing. -- @field #string BOMBRESULT Range bombing.
-- @field #string STRAFERESULT Range strafeing result. -- @field #string STRAFERESULT Range strafeing result.

View File

@ -1222,6 +1222,20 @@ function UTILS.HdgDiff(h1, h2)
return math.abs(delta) return math.abs(delta)
end end
--- Returns the heading from one vec3 to another vec3.
-- @param DCS#Vec3 a From vec3.
-- @param DCS#Vec3 b To vec3.
-- @return #number Heading in degrees.
function UTILS.HdgTo(a, b)
local dz=b.z-a.z
local dx=b.x-a.x
local heading=math.deg(math.atan2(dz, dx))
if heading < 0 then
heading = 360 + heading
end
return heading
end
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged. --- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.

View File

@ -1768,6 +1768,7 @@ do -- Cargo
["tt_DSHK"] = 6, ["tt_DSHK"] = 6,
["HL_KORD"] = 6, ["HL_KORD"] = 6,
["HL_DSHK"] = 6, ["HL_DSHK"] = 6,
["CCKW_353"] = 16, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers
} }
-- Assuming that each passenger weighs 95 kg on average. -- Assuming that each passenger weighs 95 kg on average.

View File

@ -4,7 +4,7 @@
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- --
-- ### Contributions: **Applevangelist** -- ### Contributions: **Applevangelist**, **funkyfranky**
-- --
-- === -- ===
-- --
@ -12,12 +12,12 @@
-- @image Wrapper_Scenery.JPG -- @image Wrapper_Scenery.JPG
--- SCENERY Class
--- @type SCENERY -- @type SCENERY
-- @field #string ClassName -- @field #string ClassName Name of the class.
-- @field #string SceneryName -- @field #string SceneryName Name of the scenery object.
-- @field #DCS.Object SceneryObject -- @field DCS#Object SceneryObject DCS scenery object.
-- @field #number Life0 -- @field #number Life0 Initial life points.
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
@ -37,12 +37,16 @@ SCENERY = {
--- Register scenery object as POSITIONABLE. --- Register scenery object as POSITIONABLE.
--@param #SCENERY self --@param #SCENERY self
--@param #string SceneryName Scenery name. --@param #string SceneryName Scenery name.
--@param #DCS.Object SceneryObject DCS scenery object. --@param DCS#Object SceneryObject DCS scenery object.
--@return #SCENERY Scenery object. --@return #SCENERY Scenery object.
function SCENERY:Register( SceneryName, SceneryObject ) function SCENERY:Register( SceneryName, SceneryObject )
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
self.SceneryName = SceneryName self.SceneryName = SceneryName
self.SceneryObject = SceneryObject self.SceneryObject = SceneryObject
if self.SceneryObject then if self.SceneryObject then
self.Life0 = self.SceneryObject:getLife() self.Life0 = self.SceneryObject:getLife()
else else
@ -53,7 +57,7 @@ end
--- Obtain DCS Object from the SCENERY Object. --- Obtain DCS Object from the SCENERY Object.
--@param #SCENERY self --@param #SCENERY self
--@return #DCS.Object DCS scenery object. --@return DCS#Object DCS scenery object.
function SCENERY:GetDCSObject() function SCENERY:GetDCSObject()
return self.SceneryObject return self.SceneryObject
end end
@ -69,7 +73,7 @@ function SCENERY:GetLife()
return life return life
end end
--- Get current initial life points from the SCENERY Object. --- Get initial life points of the SCENERY Object.
--@param #SCENERY self --@param #SCENERY self
--@return #number life --@return #number life
function SCENERY:GetLife0() function SCENERY:GetLife0()
@ -90,7 +94,7 @@ function SCENERY:IsDead()
return self:GetLife() < 1 and true or false return self:GetLife() < 1 and true or false
end end
--- Get the threat level of a SCENERY object. Always 0. --- Get the threat level of a SCENERY object. Always 0 as scenery does not pose a threat to anyone.
--@param #SCENERY self --@param #SCENERY self
--@return #number Threat level 0. --@return #number Threat level 0.
--@return #string "Scenery". --@return #string "Scenery".

View File

@ -167,7 +167,7 @@ end
--- Get the DCS unit object. --- Get the DCS unit object.
-- @param #UNIT self -- @param #UNIT self
-- @return DCS#Unit -- @return DCS#Unit The DCS unit object.
function UNIT:GetDCSObject() function UNIT:GetDCSObject()
local DCSUnit = Unit.getByName( self.UnitName ) local DCSUnit = Unit.getByName( self.UnitName )