Merge branch 'develop' into FF/Ops

This commit is contained in:
Frank 2022-03-25 11:00:17 +01:00
commit 24559c475b
25 changed files with 1744 additions and 693 deletions

View File

@ -9,7 +9,7 @@
-- * Create polygon zones. -- * Create polygon zones.
-- * Create moving zones around a unit. -- * Create moving zones around a unit.
-- * Create moving zones around a group. -- * Create moving zones around a group.
-- * Provide the zone behaviour. Some zones are static, while others are moveable. -- * Provide the zone behavior. Some zones are static, while others are moveable.
-- * Enquiry if a coordinate is within a zone. -- * Enquiry if a coordinate is within a zone.
-- * Smoke zones. -- * Smoke zones.
-- * Set a zone probability to control zone selection. -- * Set a zone probability to control zone selection.
@ -20,10 +20,10 @@
-- * Draw zones (circular and polygon) on the F10 map. -- * Draw zones (circular and polygon) on the F10 map.
-- --
-- --
-- There are essentially two core functions that zones accomodate: -- There are essentially two core functions that zones accommodate:
-- --
-- * Test if an object is within the zone boundaries. -- * Test if an object is within the zone boundaries.
-- * Provide the zone behaviour. Some zones are static, while others are moveable. -- * Provide the zone behavior. Some zones are static, while others are moveable.
-- --
-- The object classes are using the zone classes to test the zone boundaries, which can take various forms: -- The object classes are using the zone classes to test the zone boundaries, which can take various forms:
-- --
@ -1219,7 +1219,7 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
while gotit==false and N<=Nmax do while gotit==false and N<=Nmax do
gotit=_checkSurface(point) gotit=_checkSurface(point)
if gotit then if gotit then
env.info(string.format("Got random coordinate with surface type %d after N=%d/%d iterations", land.getSurfaceType(point), N, Nmax)) --env.info(string.format("Got random coordinate with surface type %d after N=%d/%d iterations", land.getSurfaceType(point), N, Nmax))
else else
point=_getpoint() point=_getpoint()
N=N+1 N=N+1
@ -2126,12 +2126,12 @@ end
-- --
-- ## Declare a ZONE_POLYGON directly in the DCS mission editor! -- ## Declare a ZONE_POLYGON directly in the DCS mission editor!
-- --
-- You can declare a ZONE_POLYGON using the DCS mission editor by adding the ~ZONE_POLYGON tag in the group name. -- You can declare a ZONE_POLYGON using the DCS mission editor by adding the #ZONE_POLYGON tag in the group name.
-- --
-- So, imagine you have a group declared in the mission editor, with group name `DefenseZone~ZONE_POLYGON`. -- So, imagine you have a group declared in the mission editor, with group name `DefenseZone#ZONE_POLYGON`.
-- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration. -- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration.
-- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group. -- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group.
-- The ZONE_POLYGON name will be the group name without the ~ZONE_POLYGON tag. -- The ZONE_POLYGON name will be the group name without the #ZONE_POLYGON tag.
-- --
-- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method. -- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method.
-- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object -- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object

View File

@ -674,25 +674,27 @@ end
function AUTOLASE:CanLase(Recce,Unit) function AUTOLASE:CanLase(Recce,Unit)
local canlase = false local canlase = false
-- cooldown? -- cooldown?
local name = Recce:GetName() if Recce and Recce:IsAlive() == true then
local cooldown = self.RecceUnits[name].cooldown and self.forcecooldown local name = Recce:GetName()
if cooldown then local cooldown = self.RecceUnits[name].cooldown and self.forcecooldown
local Tdiff = timer.getAbsTime() - self.RecceUnits[name].timestamp if cooldown then
if Tdiff < self.cooldowntime then local Tdiff = timer.getAbsTime() - self.RecceUnits[name].timestamp
return false if Tdiff < self.cooldowntime then
else return false
self.RecceUnits[name].cooldown = false else
self.RecceUnits[name].cooldown = false
end
end
-- calculate LOS
local reccecoord = Recce:GetCoordinate()
local unitcoord = Unit:GetCoordinate()
local islos = reccecoord:IsLOS(unitcoord,2.5)
-- calculate distance
local distance = math.floor(reccecoord:Get3DDistance(unitcoord))
local lasedistance = self:GetLosFromUnit(Recce)
if distance <= lasedistance and islos then
canlase = true
end end
end
-- calculate LOS
local reccecoord = Recce:GetCoordinate()
local unitcoord = Unit:GetCoordinate()
local islos = reccecoord:IsLOS(unitcoord,2.5)
-- calculate distance
local distance = math.floor(reccecoord:Get3DDistance(unitcoord))
local lasedistance = self:GetLosFromUnit(Recce)
if distance <= lasedistance and islos then
canlase = true
end end
return canlase return canlase
end end

View File

@ -98,6 +98,7 @@
-- * Patriot -- * Patriot
-- * Rapier -- * Rapier
-- * Roland -- * Roland
-- * Silkworm (though strictly speaking this is a surface to ship missile)
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19 -- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19
-- * and from HDS (see note below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2 -- * and from HDS (see note below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2
-- --
@ -356,6 +357,7 @@ MANTIS.SamData = {
["Avenger"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Avenger" }, ["Avenger"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Avenger" },
["Chaparrel"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" }, ["Chaparrel"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Linebacker" }, ["Linebacker"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Linebacker" },
["Silkworm"] = { Range=90, Blindspot=1, Height=0.2, Type="Long", Radar="Silkworm" },
-- units from HDS Mod, multi launcher options is tricky -- units from HDS Mod, multi launcher options is tricky
["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"}, ["SA-10B"] = { Range=75, Blindspot=0, Height=18, Type="Medium" , Radar="SA-10B"},
["SA-17"] = { Range=50, Blindspot=3, Height=30, Type="Medium", Radar="SA-17" }, ["SA-17"] = { Range=50, Blindspot=3, Height=30, Type="Medium", Radar="SA-17" },

View File

@ -1704,7 +1704,7 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = PlayerMessage =
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s", string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )%s%s%s%s%s",
@ -1760,7 +1760,7 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
self:F( { ReportMissions, ScoreMissions, PenaltyMissions } ) self:F( { ReportMissions, ScoreMissions, PenaltyMissions } )
local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions local PlayerScore = ScoreHits + ScoreDestroys + ScoreCoalitionChanges + ScoreGoals + ScoreMissions
local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + ScoreGoals + PenaltyMissions local PlayerPenalty = PenaltyHits + PenaltyDestroys + PenaltyCoalitionChanges + PenaltyGoals + PenaltyMissions
PlayerMessage = PlayerMessage =
string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )", string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties )",

View File

@ -19,7 +19,7 @@
-- --
-- ### Authors: **FlightControl**, **applevangelist** -- ### Authors: **FlightControl**, **applevangelist**
-- --
-- Last Update: Nov 2021 -- Last Update: Feb 2022
-- --
-- === -- ===
-- --
@ -33,7 +33,7 @@
--- Make SAM sites execute evasive and defensive behaviour when being fired upon. --- Make SAM sites execute evasive and defensive behaviour when being fired upon.
-- --
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon. -- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
-- Once a HARM attack is detected, SEADwill shut down the radars of the attacked SAM site and take evasive action by moving the SAM -- Once a HARM attack is detected, SEAD will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
-- vehicles around (*if* they are drivable, that is). There's a component of randomness in detection and evasion, which is based on the -- vehicles around (*if* they are drivable, that is). There's a component of randomness in detection and evasion, which is based on the
-- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a -- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a
-- period of time to stay defensive, before it takes evasive actions. -- period of time to stay defensive, before it takes evasive actions.
@ -79,6 +79,7 @@ SEAD = {
["Kh25"] = "Kh25", ["Kh25"] = "Kh25",
["BGM_109"] = "BGM_109", ["BGM_109"] = "BGM_109",
["AGM_154"] = "AGM_154", ["AGM_154"] = "AGM_154",
["HY-2"] = "HY-2",
} }
--- Missile enumerators - from DCS ME and Wikipedia --- Missile enumerators - from DCS ME and Wikipedia
@ -98,6 +99,7 @@ SEAD = {
["Kh25"] = {25, 0.8}, ["Kh25"] = {25, 0.8},
["BGM_109"] = {460, 0.705}, --in-game ~465kn ["BGM_109"] = {460, 0.705}, --in-game ~465kn
["AGM_154"] = {130, 0.61}, ["AGM_154"] = {130, 0.61},
["HY-2"] = {90,1},
} }
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
@ -141,7 +143,7 @@ function SEAD:New( SEADGroupPrefixes, Padding )
self:AddTransition("*", "ManageEvasion", "*") self:AddTransition("*", "ManageEvasion", "*")
self:AddTransition("*", "CalculateHitZone", "*") self:AddTransition("*", "CalculateHitZone", "*")
self:I("*** SEAD - Started Version 0.4.2") self:I("*** SEAD - Started Version 0.4.3")
return self return self
end end
@ -267,9 +269,10 @@ end
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired -- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
-- @param #number height Height when the missile was fired -- @param #number height Height when the missile was fired
-- @param Wrapper.Group#GROUP SEADGroup Attacker group -- @param Wrapper.Group#GROUP SEADGroup Attacker group
-- @param #string SEADWeaponName Weapon Name
-- @return #SEAD self -- @return #SEAD self
function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup) function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup,SEADWeaponName)
self:T("**** Calculating hit zone") self:T("**** Calculating hit zone for " .. (SEADWeaponName or "None"))
if SEADWeapon and SEADWeapon:isExist() then if SEADWeapon and SEADWeapon:isExist() then
--local pos = SEADWeapon:getPoint() --local pos = SEADWeapon:getPoint()
@ -285,6 +288,9 @@ function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADG
-- velocity -- velocity
local wpndata = SEAD.HarmData["AGM_88"] local wpndata = SEAD.HarmData["AGM_88"]
if string.find(SEADWeaponName,"154",1) then
wpndata = SEAD.HarmData["AGM_154"]
end
local mveloc = math.floor(wpndata[2] * 340.29) local mveloc = math.floor(wpndata[2] * 340.29)
local c1 = (2*mheight*9.81)/(mveloc^2) local c1 = (2*mheight*9.81)/(mveloc^2)
local c2 = (mveloc^2) / 9.81 local c2 = (mveloc^2) / 9.81
@ -459,14 +465,15 @@ function SEAD:HandleEventShot( EventData )
local _targetskill = "Random" local _targetskill = "Random"
local _targetgroupname = "none" local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target local _target = EventData.Weapon:getTarget() -- Identify target
if not _target or self.debug then -- AGM-88 w/o target data if not _target or self.debug then -- AGM-88 or 154 w/o target data
if string.find(SEADWeaponName,"AGM_88",1,true) then self:E("***** SEAD - No target data for " .. (SEADWeaponName or "None"))
self:I("**** Tracking AGM-88 with no target data.") if string.find(SEADWeaponName,"AGM_88",1,true) or string.find(SEADWeaponName,"AGM_154",1,true) then
self:I("**** Tracking AGM-88/154 with no target data.")
local pos0 = SEADPlane:GetCoordinate() local pos0 = SEADPlane:GetCoordinate()
local fheight = SEADPlane:GetHeight() local fheight = SEADPlane:GetHeight()
self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup) self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
return self
end end
return self
end end
local targetcat = _target:getCategory() -- Identify category local targetcat = _target:getCategory() -- Identify category
local _targetUnit = nil -- Wrapper.Unit#UNIT local _targetUnit = nil -- Wrapper.Unit#UNIT

View File

@ -4039,7 +4039,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu
self:_DebugMessage(string.format("Removing group %s", group:GetName()), 5) self:_DebugMessage(string.format("Removing group %s", group:GetName()), 5)
local opsgroup=_DATABASE:GetOpsGroup(group:GetName()) local opsgroup=_DATABASE:GetOpsGroup(group:GetName())
if opsgroup then if opsgroup then
opsgroup:Despawn(0, true) opsgroup:Despawn(0, true)
opsgroup:__Stop(-0.01) opsgroup:__Stop(-0.01)
else else
@ -4107,6 +4107,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
local cargobay={} local cargobay={}
local cargobaytot=0 local cargobaytot=0
local cargobaymax=0 local cargobaymax=0
local weights={}
for _i,_unit in pairs(group:GetUnits()) do for _i,_unit in pairs(group:GetUnits()) do
local unit=_unit --Wrapper.Unit#UNIT local unit=_unit --Wrapper.Unit#UNIT
local Desc=unit:GetDesc() local Desc=unit:GetDesc()
@ -4115,8 +4116,9 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
local unitweight=forceweight or Desc.massEmpty local unitweight=forceweight or Desc.massEmpty
if unitweight then if unitweight then
weight=weight+unitweight weight=weight+unitweight
weights[_i]=unitweight
end end
local cargomax=0 local cargomax=0
local massfuel=Desc.fuelMassMax or 0 local massfuel=Desc.fuelMassMax or 0
local massempty=Desc.massEmpty or 0 local massempty=Desc.massEmpty or 0
@ -4165,6 +4167,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
asset.speedmax=SpeedMax asset.speedmax=SpeedMax
asset.size=smax asset.size=smax
asset.weight=weight asset.weight=weight
asset.weights=weights
asset.DCSdesc=Descriptors asset.DCSdesc=Descriptors
asset.attribute=attribute asset.attribute=attribute
asset.cargobay=cargobay asset.cargobay=cargobay
@ -5449,9 +5452,59 @@ end
-- @param #WAREHOUSE.Assetitem asset The asset that is dead. -- @param #WAREHOUSE.Assetitem asset The asset that is dead.
-- @param #WAREHOUSE.Pendingitem request The request of the dead asset. -- @param #WAREHOUSE.Pendingitem request The request of the dead asset.
function WAREHOUSE:onafterAssetDead(From, Event, To, asset, request) function WAREHOUSE:onafterAssetDead(From, Event, To, asset, request)
-- Debug message.
local text=string.format("Asset %s from request id=%d is dead!", asset.templatename, request.uid) local text=string.format("Asset %s from request id=%d is dead!", asset.templatename, request.uid)
self:T(self.lid..text) self:T(self.lid..text)
self:_DebugMessage(text)
-- Here I need to get rid of the #CARGO at the end to obtain the original name again!
local groupname=asset.spawngroupname --self:_GetNameWithOut(group)
-- Dont trigger a Remove event for the group sets.
local NoTriggerEvent=true
if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
---
-- Easy case: Group can simply be removed from the cargogroupset.
---
-- Remove dead group from cargo group set.
request.cargogroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed selfpropelled cargo %s: ncargo=%d.", groupname, request.cargogroupset:Count()))
else
---
-- Complicated case: Dead unit could be:
-- 1.) A Cargo unit (e.g. waiting to be picked up).
-- 2.) A Transport unit which itself holds cargo groups.
---
-- Check if this a cargo or transport group.
local istransport=not asset.iscargo --self:_GroupIsTransport(group, request)
if istransport==true then
-- Whole carrier group is dead. Remove it from the carrier group set.
request.transportgroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed transport %s: ntransport=%d", groupname, request.transportgroupset:Count()))
elseif istransport==false then
-- This must have been an alive cargo group that was killed outside the carrier, e.g. waiting to be transported or waiting to be put back.
-- Remove dead group from cargo group set.
request.cargogroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed transported cargo %s outside carrier: ncargo=%d", groupname, request.cargogroupset:Count()))
-- This as well?
--request.transportcargoset:RemoveCargosByName(RemoveCargoNames)
else
--self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName()))
end
end
end end
@ -6556,7 +6609,8 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData)
end end
end end
--self:I(self.lid..string.format("Warehouse %s captured event dead or crash or unit %s.", self.alias, tostring(EventData.IniUnitName))) -- Debug info.
self:T2(self.lid..string.format("Warehouse %s captured event dead or crash or unit %s", self.alias, tostring(EventData.IniUnitName)))
-- Check if an asset unit was destroyed. -- Check if an asset unit was destroyed.
if EventData.IniGroup then if EventData.IniGroup then
@ -6571,7 +6625,7 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData)
if wid==self.uid then if wid==self.uid then
-- Debug message. -- Debug message.
self:T(self.lid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.", self.alias, EventData.IniUnitName)) self:T(self.lid..string.format("Warehouse %s captured event dead or crash of its asset unit %s", self.alias, EventData.IniUnitName))
-- Loop over all pending requests and get the one belonging to this unit. -- Loop over all pending requests and get the one belonging to this unit.
for _,request in pairs(self.pending) do for _,request in pairs(self.pending) do
@ -6581,7 +6635,7 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData)
if request.uid==rid then if request.uid==rid then
-- Update cargo and transport group sets of this request. We need to know if this job is finished. -- Update cargo and transport group sets of this request. We need to know if this job is finished.
self:_UnitDead(EventData.IniUnit, request) self:_UnitDead(EventData.IniUnit, EventData.IniGroup, request)
end end
end end
@ -6594,38 +6648,46 @@ end
-- This is important in order to determine if a job is done and can be removed from the (pending) queue. -- This is important in order to determine if a job is done and can be removed from the (pending) queue.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @param Wrapper.Unit#UNIT deadunit Unit that died. -- @param Wrapper.Unit#UNIT deadunit Unit that died.
-- @param Wrapper.Group#GROUP deadgroup Group of unit that died.
-- @param #WAREHOUSE.Pendingitem request Request that needs to be updated. -- @param #WAREHOUSE.Pendingitem request Request that needs to be updated.
function WAREHOUSE:_UnitDead(deadunit, request) function WAREHOUSE:_UnitDead(deadunit, deadgroup, request)
self:F(self.lid.."FF unit dead "..deadunit:GetName())
-- Flare unit. -- Find opsgroup.
if self.Debug then local opsgroup=_DATABASE:FindOpsGroup(deadgroup)
deadunit:FlareRed()
-- Check if we have an opsgroup.
if opsgroup then
-- Handled in OPSGROUP:onafterDead() now.
return nil
end end
-- Group the dead unit belongs to.
local group=deadunit:GetGroup()
-- Number of alive units in group. -- Number of alive units in group.
local nalive=group:CountAliveUnits() local nalive=deadgroup:CountAliveUnits()
-- Whole group is dead? -- Whole group is dead?
local groupdead=true local groupdead=false
if nalive>0 then if nalive>0 then
groupdead=false groupdead=false
else
groupdead=true
end end
-- Find asset.
local asset=self:FindAssetInDB(deadgroup)
-- Here I need to get rid of the #CARGO at the end to obtain the original name again! -- Here I need to get rid of the #CARGO at the end to obtain the original name again!
local unitname=self:_GetNameWithOut(deadunit) local unitname=self:_GetNameWithOut(deadunit)
local groupname=self:_GetNameWithOut(group) local groupname=self:_GetNameWithOut(deadgroup)
-- Group is dead! -- Group is dead!
if groupdead then if groupdead then
self:T(self.lid..string.format("Group %s (transport=%s) is dead!", groupname, tostring(self:_GroupIsTransport(group,request)))) -- Debug output.
self:T(self.lid..string.format("Group %s (transport=%s) is dead!", groupname, tostring(self:_GroupIsTransport(deadgroup,request))))
if self.Debug then if self.Debug then
group:SmokeWhite() deadgroup:SmokeWhite()
end end
-- Trigger AssetDead event. -- Trigger AssetDead event.
local asset=self:FindAssetInDB(group)
self:AssetDead(asset, request) self:AssetDead(asset, request)
end end
@ -6633,19 +6695,7 @@ function WAREHOUSE:_UnitDead(deadunit, request)
-- Dont trigger a Remove event for the group sets. -- Dont trigger a Remove event for the group sets.
local NoTriggerEvent=true local NoTriggerEvent=true
if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then if not request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
---
-- Easy case: Group can simply be removed from the cargogroupset.
---
-- Remove dead group from cargo group set.
if groupdead==true then
request.cargogroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed selfpropelled cargo %s: ncargo=%d.", groupname, request.cargogroupset:Count()))
end
else
--- ---
-- Complicated case: Dead unit could be: -- Complicated case: Dead unit could be:
@ -6653,10 +6703,7 @@ function WAREHOUSE:_UnitDead(deadunit, request)
-- 2.) A Transport unit which itself holds cargo groups. -- 2.) A Transport unit which itself holds cargo groups.
--- ---
-- Check if this a cargo or transport group. if not asset.iscargo then
local istransport=self:_GroupIsTransport(group,request)
if istransport==true then
-- Get the carrier unit table holding the cargo groups inside this carrier. -- Get the carrier unit table holding the cargo groups inside this carrier.
local cargogroupnames=request.carriercargo[unitname] local cargogroupnames=request.carriercargo[unitname]
@ -6671,25 +6718,8 @@ function WAREHOUSE:_UnitDead(deadunit, request)
end end
-- Whole carrier group is dead. Remove it from the carrier group set.
if groupdead then
request.transportgroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed transport %s: ntransport=%d", groupname, request.transportgroupset:Count()))
end
elseif istransport==false then
-- This must have been an alive cargo group that was killed outside the carrier, e.g. waiting to be transported or waiting to be put back.
-- Remove dead group from cargo group set.
if groupdead==true then
request.cargogroupset:Remove(groupname, NoTriggerEvent)
self:T(self.lid..string.format("Removed transported cargo %s outside carrier: ncargo=%d", groupname, request.cargogroupset:Count()))
-- This as well?
--request.transportcargoset:RemoveCargosByName(RemoveCargoNames)
end
else else
self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName())) self:E(self.lid..string.format("ERROR: Group %s is neither cargo nor transport!", deadgroup:GetName()))
end end
end end
@ -8065,57 +8095,12 @@ end
-- @return #number Request ID. -- @return #number Request ID.
function WAREHOUSE:_GetIDsFromGroup(group) function WAREHOUSE:_GetIDsFromGroup(group)
---@param #string text The text to analyse.
local function analyse(text)
-- Get rid of #0001 tail from spawn.
local unspawned=UTILS.Split(text, "#")[1]
-- Split keywords.
local keywords=UTILS.Split(unspawned, "_")
local _wid=nil -- warehouse UID
local _aid=nil -- asset UID
local _rid=nil -- request UID
-- Loop over keys.
for _,keys in pairs(keywords) do
local str=UTILS.Split(keys, "-")
local key=str[1]
local val=str[2]
if key:find("WID") then
_wid=tonumber(val)
elseif key:find("AID") then
_aid=tonumber(val)
elseif key:find("RID") then
_rid=tonumber(val)
end
end
return _wid,_aid,_rid
end
if group then if group then
-- Group name -- Group name
local name=group:GetName() local groupname=group:GetName()
-- Get asset id from group name. local wid, aid, rid=self:_GetIDsFromGroupName(groupname)
local wid,aid,rid=analyse(name)
-- Get Asset.
local asset=self:GetAssetByID(aid)
-- Get warehouse and request id from asset table.
if asset then
wid=asset.wid
rid=asset.rid
end
-- Debug info
self:T3(self.lid..string.format("Group Name = %s", tostring(name)))
self:T3(self.lid..string.format("Warehouse ID = %s", tostring(wid)))
self:T3(self.lid..string.format("Asset ID = %s", tostring(aid)))
self:T3(self.lid..string.format("Request ID = %s", tostring(rid)))
return wid,aid,rid return wid,aid,rid
else else
@ -8124,14 +8109,13 @@ function WAREHOUSE:_GetIDsFromGroup(group)
end end
--- Get warehouse id, asset id and request id from group name (alias). --- Get warehouse id, asset id and request id from group name (alias).
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @param Wrapper.Group#GROUP group The group from which the info is gathered. -- @param #string groupname Name of the group from which the info is gathered.
-- @return #number Warehouse ID. -- @return #number Warehouse ID.
-- @return #number Asset ID. -- @return #number Asset ID.
-- @return #number Request ID. -- @return #number Request ID.
function WAREHOUSE:_GetIDsFromGroupOLD(group) function WAREHOUSE:_GetIDsFromGroupName(groupname)
---@param #string text The text to analyse. ---@param #string text The text to analyse.
local function analyse(text) local function analyse(text)
@ -8162,25 +8146,26 @@ function WAREHOUSE:_GetIDsFromGroupOLD(group)
return _wid,_aid,_rid return _wid,_aid,_rid
end end
if group then
-- Group name -- Get asset id from group name.
local name=group:GetName() local wid,aid,rid=analyse(groupname)
-- Get ids -- Get Asset.
local wid,aid,rid=analyse(name) local asset=self:GetAssetByID(aid)
-- Debug info -- Get warehouse and request id from asset table.
self:T3(self.lid..string.format("Group Name = %s", tostring(name))) if asset then
self:T3(self.lid..string.format("Warehouse ID = %s", tostring(wid))) wid=asset.wid
self:T3(self.lid..string.format("Asset ID = %s", tostring(aid))) rid=asset.rid
self:T3(self.lid..string.format("Request ID = %s", tostring(rid)))
return wid,aid,rid
else
self:E("WARNING: Group not found in GetIDsFromGroup() function!")
end end
-- Debug info
self:T3(self.lid..string.format("Group Name = %s", tostring(groupname)))
self:T3(self.lid..string.format("Warehouse ID = %s", tostring(wid)))
self:T3(self.lid..string.format("Asset ID = %s", tostring(aid)))
self:T3(self.lid..string.format("Request ID = %s", tostring(rid)))
return wid,aid,rid
end end
--- Filter stock assets by descriptor and attribute. --- Filter stock assets by descriptor and attribute.

View File

@ -46,7 +46,7 @@
-- --
-- # The ARMYGROUP Concept -- # The ARMYGROUP Concept
-- --
-- This class enhances naval groups. -- This class enhances ground groups.
-- --
-- @field #ARMYGROUP -- @field #ARMYGROUP
ARMYGROUP = { ARMYGROUP = {
@ -115,6 +115,7 @@ function ARMYGROUP:New(group)
self:AddTransition("*", "Cruise", "Cruising") -- Cruise along the given route of waypoints. self:AddTransition("*", "Cruise", "Cruising") -- Cruise along the given route of waypoints.
self:AddTransition("*", "RTZ", "Returning") -- Group is returning to (home) zone. self:AddTransition("*", "RTZ", "Returning") -- Group is returning to (home) zone.
self:AddTransition("Holding", "Returned", "Returned") -- Group is returned to (home) zone, e.g. when unloaded from carrier.
self:AddTransition("Returning", "Returned", "Returned") -- Group is returned to (home) zone. self:AddTransition("Returning", "Returned", "Returned") -- Group is returned to (home) zone.
self:AddTransition("*", "Detour", "OnDetour") -- Make a detour to a coordinate and resume route afterwards. self:AddTransition("*", "Detour", "OnDetour") -- Make a detour to a coordinate and resume route afterwards.
@ -652,7 +653,10 @@ function ARMYGROUP:Status()
end end
end end
end end
else
-- Check damage of elements and group.
self:_CheckDamage()
end end
-- Check that group EXISTS. -- Check that group EXISTS.
@ -691,7 +695,7 @@ function ARMYGROUP:Status()
local text=string.format("State %s: Alive=%s", fsmstate, tostring(self:IsAlive())) local text=string.format("State %s: Alive=%s", fsmstate, tostring(self:IsAlive()))
self:I(self.lid..text) self:I(self.lid..text)
end end
end end
--- ---
@ -706,7 +710,6 @@ function ARMYGROUP:Status()
local name=element.name local name=element.name
local status=element.status local status=element.status
local unit=element.unit local unit=element.unit
--local life=unit:GetLifeRelative() or 0
local life,life0=self:GetLifePoints(element) local life,life0=self:GetLifePoints(element)
local life0=element.life0 local life0=element.life0
@ -926,7 +929,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
-- Next waypoint. -- Next waypoint.
local wp=UTILS.DeepCopy(self.waypoints[i]) --Ops.OpsGroup#OPSGROUP.Waypoint local wp=UTILS.DeepCopy(self.waypoints[i]) --Ops.OpsGroup#OPSGROUP.Waypoint
self:T({wp})
-- Speed. -- Speed.
if Speed then if Speed then
wp.speed=UTILS.KnotsToMps(tonumber(Speed)) wp.speed=UTILS.KnotsToMps(tonumber(Speed))
@ -981,6 +984,13 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
-- Insert a point on road. -- Insert a point on road.
if wp.action==ENUMS.Formation.Vehicle.OnRoad and (wp.coordinate or wp.roadcoord) then if wp.action==ENUMS.Formation.Vehicle.OnRoad and (wp.coordinate or wp.roadcoord) then
current=self:GetClosestRoad():WaypointGround(UTILS.MpsToKmph(self.speedWp), ENUMS.Formation.Vehicle.OnRoad)
table.insert(waypoints, 2, current)
-- Removing this for now as I don't see why it is necessary and it is very CPU intensive.
-- You only need the start and end waypoint on the road. Other waypoints on the road are not necessray.
--[[
-- take direct line if on road is too long -- take direct line if on road is too long
local wptable,length,valid=self:GetCoordinate():GetPathOnRoad(wp.coordinate or wp.roadcoord,true,false,false,false) or {} local wptable,length,valid=self:GetCoordinate():GetPathOnRoad(wp.coordinate or wp.roadcoord,true,false,false,false) or {}
@ -1002,7 +1012,8 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
else else
current=self:GetClosestRoad():WaypointGround(UTILS.MpsToKmph(self.speedWp), ENUMS.Formation.Vehicle.OnRoad) current=self:GetClosestRoad():WaypointGround(UTILS.MpsToKmph(self.speedWp), ENUMS.Formation.Vehicle.OnRoad)
table.insert(waypoints, count, current) table.insert(waypoints, count, current)
end end
]]
end end
-- Debug output. -- Debug output.
@ -1105,16 +1116,6 @@ end
-- @param #string To To state. -- @param #string To To state.
function ARMYGROUP:onafterOutOfAmmo(From, Event, To) function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
self:T(self.lid..string.format("Group is out of ammo at t=%.3f", timer.getTime())) self:T(self.lid..string.format("Group is out of ammo at t=%.3f", timer.getTime()))
-- Get current task.
local task=self:GetTaskCurrent()
if task then
if task.dcstask.id=="FireAtPoint" or task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
self:T(self.lid..string.format("Cancelling current %s task because out of ammo!", task.dcstask.id))
self:TaskCancel(task)
end
end
-- Fist, check if we want to rearm once out-of-ammo. -- Fist, check if we want to rearm once out-of-ammo.
--TODO: IsMobile() check --TODO: IsMobile() check
@ -1138,6 +1139,16 @@ function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
if self.rtzOnOutOfAmmo then if self.rtzOnOutOfAmmo then
self:__RTZ(-1) self:__RTZ(-1)
end end
-- Get current task.
local task=self:GetTaskCurrent()
if task then
if task.dcstask.id=="FireAtPoint" or task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
self:T(self.lid..string.format("Cancelling current %s task because out of ammo!", task.dcstask.id))
self:TaskCancel(task)
end
end
end end
@ -1223,9 +1234,6 @@ end
-- @param Core.Zone#ZONE Zone The zone to return to. -- @param Core.Zone#ZONE Zone The zone to return to.
-- @param #number Formation Formation of the group. -- @param #number Formation Formation of the group.
function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation) function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Zone. -- Zone.
local zone=Zone or self.homezone local zone=Zone or self.homezone
@ -1240,6 +1248,9 @@ function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation)
self:T(self.lid..string.format("RTZ to Zone %s", zone:GetName())) self:T(self.lid..string.format("RTZ to Zone %s", zone:GetName()))
local Coordinate=zone:GetRandomCoordinate() local Coordinate=zone:GetRandomCoordinate()
-- ID of current waypoint.
local uid=self:GetWaypointCurrentUID()
-- Add waypoint after current. -- Add waypoint after current.
local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true) local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true)
@ -1351,6 +1362,9 @@ function ARMYGROUP:onafterRetreat(From, Event, To, Zone, Formation)
-- Set if we want to resume route after reaching the detour waypoint. -- Set if we want to resume route after reaching the detour waypoint.
wp.detour=0 wp.detour=0
-- Cancel all missions.
self:CancelAllMissions()
end end
@ -1417,6 +1431,7 @@ end
function ARMYGROUP:onafterEngageTarget(From, Event, To, Target) function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
self:T(self.lid.."Engaging Target") self:T(self.lid.."Engaging Target")
-- Make sure this is a target.
if Target:IsInstanceOf("TARGET") then if Target:IsInstanceOf("TARGET") then
self.engage.Target=Target self.engage.Target=Target
else else
@ -1426,11 +1441,9 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
-- Target coordinate. -- Target coordinate.
self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate()) self.engage.Coordinate=UTILS.DeepCopy(self.engage.Target:GetCoordinate())
-- Get a coordinate close to the target.
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9) local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Backup ROE and alarm state. -- Backup ROE and alarm state.
self.engage.roe=self:GetROE() self.engage.roe=self:GetROE()
self.engage.alarmstate=self:GetAlarmstate() self.engage.alarmstate=self:GetAlarmstate()
@ -1442,6 +1455,10 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
-- ID of current waypoint. -- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid local uid=self:GetWaypointCurrent().uid
-- Set formation.
--TODO: make this input.
local Formation=ENUMS.Formation.Vehicle.Vee
-- Add waypoint after current. -- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true) self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
@ -1458,37 +1475,46 @@ function ARMYGROUP:_UpdateEngageTarget()
-- Get current position vector. -- Get current position vector.
local vec3=self.engage.Target:GetVec3() local vec3=self.engage.Target:GetVec3()
if vec3 then
-- Distance to last known position of target. -- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3()) local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
-- Check if target moved more than 100 meters.
if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9) -- Check if target moved more than 100 meters.
if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- Add waypoint after current. -- ID of current waypoint.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true) local uid=self:GetWaypointCurrent().uid
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Set if we want to resume route after reaching the detour waypoint. -- Add waypoint after current.
self.engage.Waypoint.detour=0 self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
-- Set if we want to resume route after reaching the detour waypoint.
self.engage.Waypoint.detour=0
end
else
-- Could not get position of target (not alive any more?) ==> Disengage.
self:Disengage()
end end
else else
-- Target not alive any more == Disengage. -- Target not alive any more ==> Disengage.
self:Disengage() self:Disengage()
end end
@ -1587,8 +1613,7 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation
-- Speed in knots. -- Speed in knots.
Speed=Speed or self:GetSpeedCruise() Speed=Speed or self:GetSpeedCruise()
-- Formation -- Formation.
if not Formation then if not Formation then
if self.formationPerma then if self.formationPerma then
Formation = self.formationPerma Formation = self.formationPerma

View File

@ -94,6 +94,7 @@
-- @field Core.Set#SET_GROUP transportGroupSet Groups to be transported. -- @field Core.Set#SET_GROUP transportGroupSet Groups to be transported.
-- @field Core.Point#COORDINATE transportPickup Coordinate where to pickup the cargo. -- @field Core.Point#COORDINATE transportPickup Coordinate where to pickup the cargo.
-- @field Core.Point#COORDINATE transportDropoff Coordinate where to drop off the cargo. -- @field Core.Point#COORDINATE transportDropoff Coordinate where to drop off the cargo.
-- @field #number transportPickupRadius Radius in meters for pickup zone. Default 500 m.
-- --
-- @field Ops.OpsTransport#OPSTRANSPORT opstransport OPS transport assignment. -- @field Ops.OpsTransport#OPSTRANSPORT opstransport OPS transport assignment.
-- @field #number NcarriersMin Min number of required carrier assets. -- @field #number NcarriersMin Min number of required carrier assets.
@ -383,6 +384,7 @@ _AUFTRAGSNR=0
-- @field #string ARMOREDGUARD On guard - with armored groups. -- @field #string ARMOREDGUARD On guard - with armored groups.
-- @field #string BARRAGE Barrage. -- @field #string BARRAGE Barrage.
-- @field #string ARMORATTACK Armor attack. -- @field #string ARMORATTACK Armor attack.
-- @field #string CASENHANCED Enhanced CAS.
AUFTRAG.Type={ AUFTRAG.Type={
ANTISHIP="Anti Ship", ANTISHIP="Anti Ship",
AWACS="AWACS", AWACS="AWACS",
@ -415,6 +417,7 @@ AUFTRAG.Type={
ARMOREDGUARD="Armored Guard", ARMOREDGUARD="Armored Guard",
BARRAGE="Barrage", BARRAGE="Barrage",
ARMORATTACK="Armor Attack", ARMORATTACK="Armor Attack",
CASENHANCED="CAS Enhanced",
} }
--- Mission status of an assigned group. --- Mission status of an assigned group.
@ -557,7 +560,7 @@ AUFTRAG.Category={
--- AUFTRAG class version. --- AUFTRAG class version.
-- @field #string version -- @field #string version
AUFTRAG.version="0.8.1" AUFTRAG.version="0.8.4"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -1186,6 +1189,45 @@ function AUFTRAG:NewCAS(ZoneCAS, Altitude, Speed, Coordinate, Heading, Leg, Targ
return mission return mission
end end
--- **[AIR]** Create a CASENHANCED mission. Group(s) will go to the zone and patrol it randomly.
-- @param #AUFTRAG self
-- @param Core.Zone#ZONE CasZone The CAS zone.
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
-- @param #number Speed Speed in knots.
-- @param #number RangeMax Max range in NM. Only detected targets within this radius from the group will be engaged. Default is 25 NM.
-- @param #table TargetTypes Types of target attributes that will be engaged. See [DCS enum attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). Default `{"Helicopters", "Ground Units", "Light armed ships"}`.
-- @param Core.Set#SET_ZONE NoEngageZoneSet Set of zones in which targets are *not* engaged. Default is nowhere.
-- @return #AUFTRAG self
function AUFTRAG:NewCASENHANCED(CasZone, Altitude, Speed, RangeMax, NoEngageZoneSet, TargetTypes)
local mission=AUFTRAG:New(AUFTRAG.Type.CASENHANCED)
-- Ensure we got a ZONE and not just the zone name.
if type(CasZone)=="string" then
CasZone=ZONE:New(CasZone)
end
mission:_TargetFromObject(CasZone)
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.CASENHANCED)
mission:SetEngageDetected(RangeMax, TargetTypes or {"Helicopters", "Ground Units", "Light armed ships"}, CasZone, NoEngageZoneSet)
mission.optionROE=ENUMS.ROE.OpenFire
mission.optionROT=ENUMS.ROT.EvadeFire
mission.missionFraction=1.0
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or nil
mission.categories={AUFTRAG.Category.AIRCRAFT}
mission.DCStask=mission:GetDCSMissionTask()
return mission
end
--- **[AIR]** Create a FACA mission. --- **[AIR]** Create a FACA mission.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Wrapper.Group#GROUP Target Target group. Must be a GROUP object. -- @param Wrapper.Group#GROUP Target Target group. Must be a GROUP object.
@ -1492,8 +1534,9 @@ end
-- @param Core.Set#SET_GROUP TransportGroupSet The set group(s) to be transported. -- @param Core.Set#SET_GROUP TransportGroupSet The set group(s) to be transported.
-- @param Core.Point#COORDINATE DropoffCoordinate Coordinate where the helo will land drop off the the troops. -- @param Core.Point#COORDINATE DropoffCoordinate Coordinate where the helo will land drop off the the troops.
-- @param Core.Point#COORDINATE PickupCoordinate Coordinate where the helo will land to pick up the the cargo. Default is the fist transport group. -- @param Core.Point#COORDINATE PickupCoordinate Coordinate where the helo will land to pick up the the cargo. Default is the fist transport group.
-- @param #number PickupRadius Radius around the pickup coordinate in meters. Default 100 m.
-- @return #AUFTRAG self -- @return #AUFTRAG self
function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupCoordinate) function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupCoordinate, PickupRadius)
local mission=AUFTRAG:New(AUFTRAG.Type.TROOPTRANSPORT) local mission=AUFTRAG:New(AUFTRAG.Type.TROOPTRANSPORT)
@ -1509,14 +1552,16 @@ function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupC
mission:_TargetFromObject(mission.transportGroupSet) mission:_TargetFromObject(mission.transportGroupSet)
mission.transportPickup=PickupCoordinate or mission:GetTargetCoordinate() mission.transportPickup=PickupCoordinate or mission:GetTargetCoordinate()
mission.transportDropoff=DropoffCoordinate mission.transportDropoff=DropoffCoordinate
mission.transportPickupRadius=PickupRadius or 100
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.TROOPTRANSPORT) mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.TROOPTRANSPORT)
-- Debug. -- Debug.
mission.transportPickup:MarkToAll("Pickup") --mission.transportPickup:MarkToAll("Pickup Transport")
mission.transportDropoff:MarkToAll("Drop off") --mission.transportDropoff:MarkToAll("Drop off")
-- TODO: what's the best ROE here? -- TODO: what's the best ROE here?
mission.optionROE=ENUMS.ROE.ReturnFire mission.optionROE=ENUMS.ROE.ReturnFire
@ -1645,8 +1690,9 @@ end
-- @param Core.Zone#ZONE Zone The patrol zone. -- @param Core.Zone#ZONE Zone The patrol zone.
-- @param #number Speed Speed in knots. -- @param #number Speed Speed in knots.
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL. -- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
-- @param #string Formation Formation used during patrol.
-- @return #AUFTRAG self -- @return #AUFTRAG self
function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude) function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude, Formation)
local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE) local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE)
@ -1670,10 +1716,13 @@ function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude)
mission.categories={AUFTRAG.Category.ALL} mission.categories={AUFTRAG.Category.ALL}
mission.DCStask=mission:GetDCSMissionTask() mission.DCStask=mission:GetDCSMissionTask()
mission.DCStask.params.formation=Formation
return mission return mission
end end
--- **[GROUND]** Create a ARMORATTACK mission. Armoured ground group(s) will go to the zone and attack. --- **[GROUND]** Create a ARMORATTACK mission. Armoured ground group(s) will go to the zone and attack.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object. -- @param Wrapper.Positionable#POSITIONABLE Target The target to attack. Can be a GROUP, UNIT or STATIC object.
@ -3478,7 +3527,7 @@ function AUFTRAG:SetGroupStatus(opsgroup, status)
local groupsDone=self:CheckGroupsDone() local groupsDone=self:CheckGroupsDone()
-- Debug info. -- Debug info.
self:T2(self.lid..string.format("Setting OPSGROUP %s status to %s. IsNotOver=%s CheckGroupsDone=%s", opsgroup.groupname, self:GetGroupStatus(opsgroup), tostring(self:IsNotOver()), tostring(self:CheckGroupsDone()))) self:T2(self.lid..string.format("Setting OPSGROUP %s status to %s. IsNotOver=%s CheckGroupsDone=%s", opsgroup.groupname, self:GetGroupStatus(opsgroup), tostring(self:IsNotOver()), tostring(groupsDone)))
-- Check if ALL flights are done with their mission. -- Check if ALL flights are done with their mission.
if isNotOver and groupsDone then if isNotOver and groupsDone then
@ -4958,6 +5007,26 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable)
DCStask.params=param DCStask.params=param
table.insert(DCStasks, DCStask) table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.CASENHANCED then
-------------------------
-- CAS ENHANCED Mission --
-------------------------
local DCStask={}
DCStask.id="PatrolZone"
-- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP.
local param={}
param.zone=self:GetObjective()
param.altitude=self.missionAltitude
param.speed=self.missionSpeed
DCStask.params=param
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.ARMORATTACK then elseif self.type==AUFTRAG.Type.ARMORATTACK then
@ -5147,6 +5216,8 @@ function AUFTRAG:GetMissionTaskforMissionType(MissionType)
mtask=ENUMS.MissionTask.CAS mtask=ENUMS.MissionTask.CAS
elseif MissionType==AUFTRAG.Type.PATROLZONE then elseif MissionType==AUFTRAG.Type.PATROLZONE then
mtask=ENUMS.MissionTask.CAS mtask=ENUMS.MissionTask.CAS
elseif MissionType==AUFTRAG.Type.CASENHANCED then
mtask=ENUMS.MissionTask.CAS
elseif MissionType==AUFTRAG.Type.ESCORT then elseif MissionType==AUFTRAG.Type.ESCORT then
mtask=ENUMS.MissionTask.ESCORT mtask=ENUMS.MissionTask.ESCORT
elseif MissionType==AUFTRAG.Type.FACA then elseif MissionType==AUFTRAG.Type.FACA then

View File

@ -6,6 +6,14 @@
-- --
-- === -- ===
-- --
-- ## Missions:--- **Ops** -- Combat Search and Rescue.
--
-- ===
--
-- **CSAR** - MOOSE based Helicopter CSAR Operations.
--
-- ===
--
-- ## Missions: -- ## Missions:
-- --
-- ### [CSAR - Combat Search & Rescue](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CSAR) -- ### [CSAR - Combat Search & Rescue](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CSAR)
@ -118,7 +126,8 @@
-- self.SRSModulation = radio.modulation.AM -- modulation -- self.SRSModulation = radio.modulation.AM -- modulation
-- -- -- --
-- self.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection --shagrat -- self.csarUsePara = false -- If set to true, will use the LandingAfterEjection Event instead of Ejection --shagrat
-- -- self.wetfeettemplate = "man in floating thingy" -- if you use a mod to have a pilot in a rescue float, put the template name in here for wet feet spawns. Note: in conjunction with csarUsePara this might create dual ejected pilots in edge cases.
--
-- ## 3. Results -- ## 3. Results
-- --
-- Number of successful landings with save pilots and aggregated number of saved pilots is stored in these variables in the object: -- Number of successful landings with save pilots and aggregated number of saved pilots is stored in these variables in the object:
@ -227,8 +236,9 @@ CSAR = {
-- @field #number frequency Frequency of the NDB. -- @field #number frequency Frequency of the NDB.
-- @field #string player Player name if applicable. -- @field #string player Player name if applicable.
-- @field Wrapper.Group#GROUP group Spawned group object. -- @field Wrapper.Group#GROUP group Spawned group object.
-- @field #number timestamp Timestamp for approach process -- @field #number timestamp Timestamp for approach process.
-- @field #boolean alive Group is alive or dead/rescued -- @field #boolean alive Group is alive or dead/rescued.
-- @field #boolean wetfeet Group is spawned over (deep) water.
--- All slot / Limit settings --- All slot / Limit settings
-- @type CSAR.AircraftType -- @type CSAR.AircraftType
@ -244,11 +254,12 @@ CSAR.AircraftType["Mi-8MT"] = 12
CSAR.AircraftType["Mi-24P"] = 8 CSAR.AircraftType["Mi-24P"] = 8
CSAR.AircraftType["Mi-24V"] = 8 CSAR.AircraftType["Mi-24V"] = 8
CSAR.AircraftType["Bell-47"] = 2 CSAR.AircraftType["Bell-47"] = 2
CSAR.AircraftType["UH-60L"] = 10 CSAR.AircraftType["UH-60L"] = 10
CSAR.AircraftType["AH-64D_BLK_II"] = 2
--- CSAR class version. --- CSAR class version.
-- @field #string version -- @field #string version
CSAR.version="1.0.3" CSAR.version="1.0.4d"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@ -390,6 +401,10 @@ function CSAR:New(Coalition, Template, Alias)
-- added 0.1.3 -- added 0.1.3
self.csarUsePara = false -- shagrat set to true, will use the LandingAfterEjection Event instead of Ejection self.csarUsePara = false -- shagrat set to true, will use the LandingAfterEjection Event instead of Ejection
-- added 0.1.4
self.wetfeettemplate = nil
self.usewetfeet = false
-- WARNING - here\'ll be dragons -- WARNING - here\'ll be dragons
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua -- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
@ -502,8 +517,9 @@ end
-- @param #string Typename Typename of unit. -- @param #string Typename Typename of unit.
-- @param #number Frequency Frequency of the NDB in Hz -- @param #number Frequency Frequency of the NDB in Hz
-- @param #string Playername Name of Player (if applicable) -- @param #string Playername Name of Player (if applicable)
-- @param #boolean Wetfeet Ejected over water
-- @return #CSAR self. -- @return #CSAR self.
function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername) function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername,Wetfeet)
self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername}) self:T({"_CreateDownedPilotTrack",Groupname,Side,OriginalUnit,Description,Typename,Frequency,Playername})
-- create new entry -- create new entry
@ -519,6 +535,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript
DownedPilot.group = Group DownedPilot.group = Group
DownedPilot.timestamp = 0 DownedPilot.timestamp = 0
DownedPilot.alive = true DownedPilot.alive = true
DownedPilot.wetfeet = Wetfeet or false
-- Add Pilot -- Add Pilot
local PilotTable = self.downedPilots local PilotTable = self.downedPilots
@ -568,17 +585,23 @@ end
-- @param #number country Country for template. -- @param #number country Country for template.
-- @param Core.Point#COORDINATE point Coordinate to spawn at. -- @param Core.Point#COORDINATE point Coordinate to spawn at.
-- @param #number frequency Frequency of the pilot's beacon -- @param #number frequency Frequency of the pilot's beacon
-- @param #boolean wetfeet Spawn is over water
-- @return Wrapper.Group#GROUP group The #GROUP object. -- @return Wrapper.Group#GROUP group The #GROUP object.
-- @return #string alias The alias name. -- @return #string alias The alias name.
function CSAR:_SpawnPilotInField(country,point,frequency) function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet)
self:T({country,point,frequency}) self:T({country,point,frequency,tostring(wetfeet)})
local freq = frequency or 1000 local freq = frequency or 1000
local freq = freq / 1000 -- kHz local freq = freq / 1000 -- kHz
for i=1,10 do for i=1,10 do
math.random(i,10000) math.random(i,10000)
end end
if point:IsSurfaceTypeWater() then point.y = 0 end if point:IsSurfaceTypeWater() or wetfeet then
point.y = 0
end
local template = self.template local template = self.template
if self.usewetfeet and wetfeet then
template = self.wetfeettemplate
end
local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99)) local alias = string.format("Pilot %.2fkHz-%d", freq, math.random(1,99))
local coalition = self.coalition local coalition = self.coalition
local pilotcacontrol = self.allowDownedPilotCAcontrol -- Switch AI on/oof - is this really correct for CA? local pilotcacontrol = self.allowDownedPilotCAcontrol -- Switch AI on/oof - is this really correct for CA?
@ -644,13 +667,19 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description}) self:T({_coalition , _country, _point, _typeName, _unitName, _playerName, _freq, noMessage, _description})
local template = self.template local template = self.template
local wetfeet = false
local surface = _point:GetSurfaceType()
if surface == land.SurfaceType.WATER then
wetfeet = true
end
if not _freq then if not _freq then
_freq = self:_GenerateADFFrequency() _freq = self:_GenerateADFFrequency()
if not _freq then _freq = 333000 end --noob catch if not _freq then _freq = 333000 end --noob catch
end end
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq) local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq,wetfeet)
local _typeName = _typeName or "Pilot" local _typeName = _typeName or "Pilot"
@ -688,7 +717,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
local _GroupName = _spawnedGroup:GetName() or _alias local _GroupName = _spawnedGroup:GetName() or _alias
self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName) self:_CreateDownedPilotTrack(_spawnedGroup,_GroupName,_coalition,_unitName,_text,_typeName,_freq,_playerName,wetfeet)
self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc. self:_InitSARForPilot(_spawnedGroup, _unitName, _freq, noMessage) --shagrat use unitName to have the aircraft callsign / descriptive "name" etc.
@ -935,9 +964,19 @@ function CSAR:_EventHandler(EventData)
if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then if self.limitmaxdownedpilots and self:_ReachedPilotLimit() then
return return
end end
-- TODO: Over water check --- EVENTS.LandingAfterEjection NOT triggered by DCS, so handle csarUsePara = true case
-- might create dual pilots in edge cases
local wetfeet = false
local surface = _unit:GetCoordinate():GetSurfaceType()
if surface == land.SurfaceType.WATER then
wetfeet = true
end
-- all checks passed, get going. -- all checks passed, get going.
if self.csarUsePara == false then --shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land if self.csarUsePara == false or (self.csarUsePara and wetfeet ) then --shagrat check parameter LandingAfterEjection, if true don't spawn a Pilot from EJECTION event, wait for the Chute to land
local _freq = self:_GenerateADFFrequency() local _freq = self:_GenerateADFFrequency()
self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none") self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
return true return true
@ -1294,7 +1333,8 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
_time = self.landedStatus[_lookupKeyHeli] - 10 _time = self.landedStatus[_lookupKeyHeli] - 10
self.landedStatus[_lookupKeyHeli] = _time self.landedStatus[_lookupKeyHeli] = _time
end end
if _time <= 0 or _distance < self.loadDistance then --if _time <= 0 or _distance < self.loadDistance then
if _distance < self.loadDistance + 5 or _distance <= 13 then
if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true)
return true return true
@ -1999,6 +2039,9 @@ function CSAR:onafterStart(From, Event, To)
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart() self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
end end
self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also? self.mash = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterPrefixes(self.mashprefix):FilterStart() -- currently only GROUP objects, maybe support STATICs also?
if self.wetfeettemplate then
self.usewetfeet = true
end
self:__Status(-10) self:__Status(-10)
return self return self
end end

View File

@ -1017,11 +1017,12 @@ CTLD.UnitTypes = {
["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers. ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers.
--Actually it's longer, but the center coord is off-center of the model. --Actually it's longer, but the center coord is off-center of the model.
["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats ["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, -- 2 ppl **outside** the helo
} }
--- CTLD class version. --- CTLD class version.
-- @field #string version -- @field #string version
CTLD.version="1.0.9" CTLD.version="1.0.10"
--- Instantiate a new CTLD. --- Instantiate a new CTLD.
-- @param #CTLD self -- @param #CTLD self
@ -2095,11 +2096,18 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist, _ignoreweight)
-- cycle -- cycle
local index = 0 local index = 0
local found = {} local found = {}
local loadedmass = self:_GetUnitCargoMass(_unit) local loadedmass = 0
local unittype = _unit:GetTypeName() local unittype = "none"
local capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitCapabilities local capabilities = {}
local maxmass = capabilities.cargoweightlimit local maxmass = 2000
local maxloadable = maxmass - loadedmass local maxloadable = 2000
if not _ignoreweight then
loadedmass = self:_GetUnitCargoMass(_unit)
unittype = _unit:GetTypeName()
capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitCapabilities
maxmass = capabilities.cargoweightlimit or 2000
maxloadable = maxmass - loadedmass
end
self:T(self.lid .. " Max loadable mass: " .. maxloadable) self:T(self.lid .. " Max loadable mass: " .. maxloadable)
for _,_cargoobject in pairs (existingcrates) do for _,_cargoobject in pairs (existingcrates) do
local cargo = _cargoobject -- #CTLD_CARGO local cargo = _cargoobject -- #CTLD_CARGO
@ -2256,6 +2264,7 @@ end
-- @return #number mass in kgs -- @return #number mass in kgs
function CTLD:_GetUnitCargoMass(Unit) function CTLD:_GetUnitCargoMass(Unit)
self:T(self.lid .. " _GetUnitCargoMass") self:T(self.lid .. " _GetUnitCargoMass")
if not Unit then return 0 end
local unitname = Unit:GetName() local unitname = Unit:GetName()
local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo
local loadedmass = 0 -- #number local loadedmass = 0 -- #number

View File

@ -103,7 +103,7 @@
-- --
-- Strategically important zones, which should be captured can be added via the @{#CHIEF.AddStrategicZone}() function. -- Strategically important zones, which should be captured can be added via the @{#CHIEF.AddStrategicZone}() function.
-- --
-- If the zone is currently owned by another coalition and enemy ground troops are present in the zone, a CAS mission is lauchned. -- If the zone is currently owned by another coalition and enemy ground troops are present in the zone, a CAS and an ARTY mission are lauchned, provided assets are available.
-- --
-- Once the zone is cleaned of enemy forces, ground (infantry) troops are send there. These require a transportation via helicopters. -- Once the zone is cleaned of enemy forces, ground (infantry) troops are send there. These require a transportation via helicopters.
-- So in order to deploy our own troops, infantry assets with `AUFTRAG.Type.ONGUARD` and helicopters with `AUFTRAG.Type.OPSTRANSPORT` need to be available. -- So in order to deploy our own troops, infantry assets with `AUFTRAG.Type.ONGUARD` and helicopters with `AUFTRAG.Type.OPSTRANSPORT` need to be available.
@ -180,7 +180,7 @@ CHIEF.Strategy = {
--- CHIEF class version. --- CHIEF class version.
-- @field #string version -- @field #string version
CHIEF.version="0.1.0" CHIEF.version="0.1.1"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -1731,7 +1731,7 @@ function CHIEF:CheckOpsZoneQueue()
-- Has a patrol mission? -- Has a patrol mission?
local hasMissionPatrol=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ONGUARD) or stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARMOREDGUARD) local hasMissionPatrol=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ONGUARD) or stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARMOREDGUARD)
-- Has a CAS mission? -- Has a CAS mission?
local hasMissionCAS=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CAS) local hasMissionCAS=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CASENHANCED)
-- Has a ARTY mission? -- Has a ARTY mission?
local hasMissionARTY=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY) local hasMissionARTY=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY)
@ -1775,12 +1775,13 @@ function CHIEF:CheckOpsZoneQueue()
-- Recruite CAS assets. -- Recruite CAS assets.
local recruited=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.CAS, 1, 1) local recruited=self:RecruitAssetsForZone(stratzone, AUFTRAG.Type.CASENHANCED, 1, 1)
-- Debug message. -- Debug message.
self:T(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets=%s", tostring(recruited))) self:T(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets=%s", tostring(recruited)))
end end
if not hasMissionARTY then if not hasMissionARTY then
-- Debug message. -- Debug message.
@ -1811,7 +1812,7 @@ function CHIEF:CheckOpsZoneQueue()
local hasMissionPATROL=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.PATROLZONE) local hasMissionPATROL=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.PATROLZONE)
-- Has a CAS mission? -- Has a CAS mission?
local hasMissionCAS, CASMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CAS) local hasMissionCAS, CASMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.CASENHANCED)
local hasMissionARTY, ARTYMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY) local hasMissionARTY, ARTYMissions = stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY)
if ownercoalition==self.coalition and stratzone.opszone:IsEmpty() and hasMissionCAS then if ownercoalition==self.coalition and stratzone.opszone:IsEmpty() and hasMissionCAS then
@ -2208,7 +2209,7 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
end end
-- Recruite infantry assets. -- Recruite infantry assets.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, RangeMax, nil, nil, Categories, Attributes) local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, RangeMax, nil, nil, nil, Categories, Attributes)
if recruited then if recruited then
@ -2260,12 +2261,48 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
-- Attach mission to ops zone. -- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission) StratZone.opszone:_AddMission(self.coalition, MissionType, mission)
-- Set ops zone to transport.
transport.opszone=StratZone.opszone
transport.chief=self
transport.commander=self.commander
return true return true
else else
LEGION.UnRecruitAssets(assets) LEGION.UnRecruitAssets(assets)
return false return false
end end
elseif MissionType==AUFTRAG.Type.CASENHANCED then
-- Create Patrol zone mission.
local caszone = StratZone.opszone.zone
local coord = caszone:GetCoordinate()
local height = UTILS.MetersToFeet(coord:GetLandHeight())+2500
local Speed = 200
if assets[1] then
if assets[1].speedmax then
Speed = UTILS.KmphToKnots(assets[1].speedmax * 0.7) or 200
end
end
--local mission=AUFTRAG:NewCAS(caszone,height,Speed,coord,math.random(0,359),Leg)
local mission=AUFTRAG:NewCASENHANCED(caszone, height, Speed)
-- Add assets to mission.
for _,asset in pairs(assets) do
mission:AddAsset(asset)
end
-- Assign mission to legions.
self:MissionAssign(mission, legions)
-- Attach mission to ops zone.
StratZone.opszone:_AddMission(self.coalition, MissionType, mission)
return true
elseif MissionType==AUFTRAG.Type.CAS then elseif MissionType==AUFTRAG.Type.CAS then
-- Create Patrol zone mission. -- Create Patrol zone mission.

View File

@ -31,7 +31,8 @@
-- @field #string livery Livery of the cohort. -- @field #string livery Livery of the cohort.
-- @field #number skill Skill of cohort members. -- @field #number skill Skill of cohort members.
-- @field Ops.Legion#LEGION legion The LEGION object the cohort belongs to. -- @field Ops.Legion#LEGION legion The LEGION object the cohort belongs to.
-- @field #number Ngroups Number of asset OPS groups this cohort has. -- @field #number Ngroups Number of asset OPS groups this cohort has.
-- @field #number Nkilled Number of destroyed asset groups.
-- @field #number engageRange Mission range in meters. -- @field #number engageRange Mission range in meters.
-- @field #string attribute Generalized attribute of the cohort template group. -- @field #string attribute Generalized attribute of the cohort template group.
-- @field #table tacanChannel List of TACAN channels available to the cohort. -- @field #table tacanChannel List of TACAN channels available to the cohort.
@ -67,6 +68,7 @@ COHORT = {
skill = nil, skill = nil,
legion = nil, legion = nil,
Ngroups = nil, Ngroups = nil,
Ngroups = 0,
engageRange = nil, engageRange = nil,
tacanChannel = {}, tacanChannel = {},
weightAsset = 99999, weightAsset = 99999,
@ -275,12 +277,10 @@ end
--- Set number of units in groups. --- Set number of units in groups.
-- @param #COHORT self -- @param #COHORT self
-- @param #number nunits Number of units. Must be >=1 and <=4. Default 2. -- @param #number nunits Number of units. Default 2.
-- @return #COHORT self -- @return #COHORT self
function COHORT:SetGrouping(nunits) function COHORT:SetGrouping(nunits)
self.ngrouping=nunits or 2 self.ngrouping=nunits or 2
if self.ngrouping<1 then self.ngrouping=1 end
if self.ngrouping>4 then self.ngrouping=4 end
return self return self
end end
@ -919,36 +919,38 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
-- ARMY/NAVYGROUP combat ready? -- ARMY/NAVYGROUP combat ready?
--- ---
-- Disable this for now as it can cause problems - at least with transport and cargo assets.
--self:I("Attribute is: "..asset.attribute)
if flightgroup:IsArmygroup() then
-- check for fighting assets
if asset.attribute == WAREHOUSE.Attribute.GROUND_ARTILLERY or
asset.attribute == WAREHOUSE.Attribute.GROUND_TANK or
asset.attribute == WAREHOUSE.Attribute.GROUND_INFANTRY or
asset.attribute == WAREHOUSE.Attribute.GROUND_AAA or
asset.attribute == WAREHOUSE.Attribute.GROUND_SAM
then
combatready=true
end
else
combatready=false
end
-- Not ready when rearming, retreating or returning!
if flightgroup:IsRearming() or flightgroup:IsRetreating() or flightgroup:IsReturning() then if flightgroup:IsRearming() or flightgroup:IsRetreating() or flightgroup:IsReturning() then
combatready=false combatready=false
end end
end end
-- Check transport/cargo for combat readyness! -- Not ready when currently acting as ops transport carrier.
if flightgroup:IsLoading() or flightgroup:IsTransporting() or flightgroup:IsUnloading() or flightgroup:IsPickingup() or flightgroup:IsCarrier() then if flightgroup:IsLoading() or flightgroup:IsTransporting() or flightgroup:IsUnloading() or flightgroup:IsPickingup() or flightgroup:IsCarrier() then
combatready=false combatready=false
end end
-- Not ready when currently acting as ops transport cargo.
if flightgroup:IsCargo() or flightgroup:IsBoarding() or flightgroup:IsAwaitingLift() then if flightgroup:IsCargo() or flightgroup:IsBoarding() or flightgroup:IsAwaitingLift() then
combatready=false combatready=false
end end
-- Disable this for now as it can cause problems - at least with transport and cargo assets.
--self:I("Attribute is: "..asset.attribute)
if flightgroup:IsArmygroup() then
-- check for fighting assets
if asset.attribute == WAREHOUSE.Attribute.GROUND_ARTILLERY or
asset.attribute == WAREHOUSE.Attribute.GROUND_TANK or
asset.attribute == WAREHOUSE.Attribute.GROUND_INFANTRY or
asset.attribute == WAREHOUSE.Attribute.GROUND_AAA or
asset.attribute == WAREHOUSE.Attribute.GROUND_SAM
then
combatready=true
end
else
combatready=false
end
-- This asset is "combatready". -- This asset is "combatready".
if combatready then if combatready then
self:T(self.lid.."Adding SPAWNED asset to ANOTHER mission as it is COMBATREADY") self:T(self.lid.."Adding SPAWNED asset to ANOTHER mission as it is COMBATREADY")

View File

@ -132,7 +132,7 @@ COMMANDER = {
--- COMMANDER class version. --- COMMANDER class version.
-- @field #string version -- @field #string version
COMMANDER.version="0.1.0" COMMANDER.version="0.1.1"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -1190,7 +1190,7 @@ function COMMANDER:RecruitAssetsForMission(Mission)
local Payloads=Mission.payloads local Payloads=Mission.payloads
-- Recruite assets. -- Recruite assets.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem, nil) local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem)
return recruited, assets, legions return recruited, assets, legions
end end
@ -1292,6 +1292,7 @@ function COMMANDER:CheckTransportQueue()
-- Weight of the heaviest cargo group. Necessary condition that this fits into on carrier unit! -- Weight of the heaviest cargo group. Necessary condition that this fits into on carrier unit!
local weightGroup=0 local weightGroup=0
local TotalWeight=0
-- Calculate the max weight so we know which cohorts can provide carriers. -- Calculate the max weight so we know which cohorts can provide carriers.
if #cargoOpsGroups>0 then if #cargoOpsGroups>0 then
@ -1301,13 +1302,14 @@ function COMMANDER:CheckTransportQueue()
if weight>weightGroup then if weight>weightGroup then
weightGroup=weight weightGroup=weight
end end
TotalWeight=TotalWeight+weight
end end
end end
if weightGroup>0 then if weightGroup>0 then
-- Recruite assets from legions. -- Recruite assets from legions.
local recruited, assets, legions=self:RecruitAssetsForTransport(transport, weightGroup) local recruited, assets, legions=self:RecruitAssetsForTransport(transport, weightGroup, TotalWeight)
if recruited then if recruited then
@ -1344,10 +1346,12 @@ end
--- Recruit assets for a given OPS transport. --- Recruit assets for a given OPS transport.
-- @param #COMMANDER self -- @param #COMMANDER self
-- @param Ops.OpsTransport#OPSTRANSPORT Transport The OPS transport. -- @param Ops.OpsTransport#OPSTRANSPORT Transport The OPS transport.
-- @param #number CargoWeight Weight of the heaviest cargo group.
-- @param #number TotalWeight Total weight of all cargo groups.
-- @return #boolean If `true`, enough assets could be recruited. -- @return #boolean If `true`, enough assets could be recruited.
-- @return #table Recruited assets. -- @return #table Recruited assets.
-- @return #table Legions that have recruited assets. -- @return #table Legions that have recruited assets.
function COMMANDER:RecruitAssetsForTransport(Transport, CargoWeight) function COMMANDER:RecruitAssetsForTransport(Transport, CargoWeight, TotalWeight)
if CargoWeight==0 then if CargoWeight==0 then
-- No cargo groups! -- No cargo groups!
@ -1381,7 +1385,7 @@ function COMMANDER:RecruitAssetsForTransport(Transport, CargoWeight)
local NreqMin,NreqMax=Transport:GetRequiredCarriers() local NreqMin,NreqMax=Transport:GetRequiredCarriers()
-- Recruit assets and legions. -- Recruit assets and legions.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NreqMin, NreqMax, TargetVec2, nil, nil, nil, CargoWeight) local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NreqMin, NreqMax, TargetVec2, nil, nil, nil, CargoWeight, TotalWeight)
return recruited, assets, legions return recruited, assets, legions
end end

View File

@ -168,7 +168,7 @@ FLIGHTGROUP.Attribute = {
--- FLIGHTGROUP class version. --- FLIGHTGROUP class version.
-- @field #string version -- @field #string version
FLIGHTGROUP.version="0.7.0" FLIGHTGROUP.version="0.7.1"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -730,60 +730,6 @@ end
-- Status -- Status
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---- Update status.
-- @param #FLIGHTGROUP self
function FLIGHTGROUP:onbeforeStatus(From, Event, To)
-- First we check if elements are still alive. Could be that they were despawned without notice, e.g. when landing on a too small airbase.
for i,_element in pairs(self.elements) do
local element=_element --Ops.OpsGroup#OPSGROUP.Element
-- Check that element is not already dead or not yet alive.
if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
-- Unit shortcut.
local unit=element.unit
local isdead=false
if unit and unit:IsAlive() then
-- Get life points.
local life=unit:GetLife() or 0
-- Units with life <=1 are dead.
if life<=1 then
--env.info(string.format("FF unit %s: live<=1 in status at T=%.3f", unit:GetName(), timer.getTime()))
isdead=true
end
else
-- Not alive any more.
--env.info(string.format("FF unit %s: NOT alive in status at T=%.3f", unit:GetName(), timer.getTime()))
isdead=true
end
-- This one is dead.
if isdead then
local text=string.format("Element %s is dead at t=%.3f but has status %s! Maybe despawned without notice or landed at a too small airbase. Calling ElementDead in 60 sec to give other events a chance",
tostring(element.name), timer.getTime(), tostring(element.status))
self:T(self.lid..text)
self:__ElementDead(60, element)
end
end
end
if self:IsDead() then
self:T(self.lid..string.format("Onbefore Status DEAD ==> false"))
return false
elseif self:IsStopped() then
self:T(self.lid..string.format("Onbefore Status STOPPED ==> false"))
return false
end
return true
end
--- Status update. --- Status update.
-- @param #FLIGHTGROUP self -- @param #FLIGHTGROUP self
function FLIGHTGROUP:Status() function FLIGHTGROUP:Status()
@ -845,7 +791,10 @@ function FLIGHTGROUP:Status()
end end
end end
end end
else
-- Check damage.
self:_CheckDamage()
end end
--- ---

File diff suppressed because it is too large Load Diff

View File

@ -45,13 +45,14 @@ LEGION = {
--- LEGION class version. --- LEGION class version.
-- @field #string version -- @field #string version
LEGION.version="0.2.0" LEGION.version="0.2.1"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Create FLEED class. -- TODO: Create FLEED class.
-- DONE: Aircraft will not start hot on Alert5.
-- DONE: OPS transport. -- DONE: OPS transport.
-- DONE: Make general so it can be inherited by AIRWING and BRIGADE classes. -- DONE: Make general so it can be inherited by AIRWING and BRIGADE classes.
@ -81,6 +82,10 @@ function LEGION:New(WarehouseName, LegionName)
-- Defaults: -- Defaults:
-- TODO: What? -- TODO: What?
self:SetMarker(false) self:SetMarker(false)
-- Dead and crash events are handled via opsgroups.
self:UnHandleEvent(EVENTS.Crash)
self:UnHandleEvent(EVENTS.Dead)
-- Add FSM transitions. -- Add FSM transitions.
-- From State --> Event --> To State -- From State --> Event --> To State
@ -530,6 +535,7 @@ function LEGION:CheckMissionQueue()
local Transport=nil local Transport=nil
if mission.NcarriersMin then if mission.NcarriersMin then
local Legions=mission.transportLegions or {self} local Legions=mission.transportLegions or {self}
TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone) TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone)
end end
@ -743,6 +749,10 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
if Mission.missionTask then if Mission.missionTask then
asset.missionTask=Mission.missionTask asset.missionTask=Mission.missionTask
end end
if Mission.type==AUFTRAG.Type.ALERT5 then
asset.takeoffType=COORDINATE.WaypointType.TakeOffParking
end
end end
@ -882,7 +892,18 @@ function LEGION:onafterTransportCancel(From, Event, To, Transport)
local cargos=Transport:GetCargoOpsGroups(false) local cargos=Transport:GetCargoOpsGroups(false)
for _,_cargo in pairs(cargos) do for _,_cargo in pairs(cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP local cargo=_cargo --Ops.OpsGroup#OPSGROUP
-- Remover my lift.
cargo:_DelMyLift(Transport) cargo:_DelMyLift(Transport)
-- Legion of cargo group
local legion=cargo.legion
-- Add asset back to legion.
if legion then
legion:T(self.lid..string.format("Adding cargo group %s back to legion", cargo:GetName()))
legion:__AddAsset(0.1, cargo.group, 1)
end
end end
-- Remove asset from mission. -- Remove asset from mission.
@ -993,7 +1014,7 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
-- Debug text. -- Debug text.
local text=string.format("New asset %s with assignment %s and request assignment %s", asset.spawngroupname, tostring(asset.assignment), tostring(assignment)) local text=string.format("New asset %s with assignment %s and request assignment %s", asset.spawngroupname, tostring(asset.assignment), tostring(assignment))
self:T3(self.lid..text) self:T(self.lid..text)
-- Get cohort. -- Get cohort.
local cohort=self:_GetCohort(asset.assignment) local cohort=self:_GetCohort(asset.assignment)
@ -1010,7 +1031,7 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
local nunits=#asset.template.units local nunits=#asset.template.units
-- Debug text. -- Debug text.
local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s, nunits=%d %s", cohort.name, assignment, asset.unittype, asset.attribute, nunits, tostring(cohort.ngrouping)) local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s, nunits=%d ngroup=%s", cohort.name, assignment, asset.unittype, asset.attribute, nunits, tostring(cohort.ngrouping))
self:T(self.lid..text) self:T(self.lid..text)
-- Adjust number of elements in the group. -- Adjust number of elements in the group.
@ -1018,6 +1039,10 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
local template=asset.template local template=asset.template
local N=math.max(#template.units, cohort.ngrouping) local N=math.max(#template.units, cohort.ngrouping)
-- We need to recalc the total weight and cargo bay.
asset.weight=0
asset.cargobaytot=0
-- Handle units. -- Handle units.
for i=1,N do for i=1,N do
@ -1028,15 +1053,28 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
-- If grouping is larger than units present, copy first unit. -- If grouping is larger than units present, copy first unit.
if i>nunits then if i>nunits then
table.insert(template.units, UTILS.DeepCopy(template.units[1])) table.insert(template.units, UTILS.DeepCopy(template.units[1]))
asset.cargobaytot=asset.cargobaytot+asset.cargobay[1]
asset.weight=asset.weight+asset.weights[1]
template.units[i].x=template.units[1].x+5*(i-nunits)
template.units[i].y=template.units[1].y+5*(i-nunits)
else
if i<=cohort.ngrouping then
asset.weight=asset.weight+asset.weights[i]
asset.cargobaytot=asset.cargobaytot+asset.cargobay[i]
end
end end
-- Remove units if original template contains more than in grouping. -- Remove units if original template contains more than in grouping.
if cohort.ngrouping<nunits and i>nunits then if i>cohort.ngrouping then
unit=nil template.units[i]=nil
end end
end end
-- Set number of units.
asset.nunits=cohort.ngrouping asset.nunits=cohort.ngrouping
-- Debug info.
self:T(self.lid..string.format("After regrouping: Nunits=%d, weight=%.1f cargobaytot=%.1f kg", #asset.template.units, asset.weight, asset.cargobaytot))
end end
-- Set takeoff type. -- Set takeoff type.
@ -1061,6 +1099,11 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
-- Asset is returned to the COHORT -- Asset is returned to the COHORT
--- ---
self:T(self.lid..string.format("Asset returned to legion ==> calling LegionAssetReturned event"))
-- Set takeoff type in case it was overwritten for an ALERT5 mission.
asset.takeoffType=cohort.takeoffType
-- Trigger event. -- Trigger event.
self:LegionAssetReturned(cohort, asset) self:LegionAssetReturned(cohort, asset)
@ -1078,7 +1121,7 @@ end
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned. -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned.
function LEGION:onafterLegionAssetReturned(From, Event, To, Cohort, Asset) function LEGION:onafterLegionAssetReturned(From, Event, To, Cohort, Asset)
-- Debug message. -- Debug message.
self:T(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment))) self:I(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment)))
-- Stop flightgroup. -- Stop flightgroup.
if Asset.flightgroup and not Asset.flightgroup:IsStopped() then if Asset.flightgroup and not Asset.flightgroup:IsStopped() then
@ -1246,7 +1289,7 @@ function LEGION:onafterAssetDead(From, Event, To, asset, request)
-- Remove group from the detection set of the CHIEF (INTEL). -- Remove group from the detection set of the CHIEF (INTEL).
if self.commander and self.commander.chief then if self.commander and self.commander.chief then
self.commander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname}) self.commander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname})
end end
-- Remove asset from mission is done via Mission:AssetDead() call from flightgroup onafterFlightDead function -- Remove asset from mission is done via Mission:AssetDead() call from flightgroup onafterFlightDead function
-- Remove asset from squadron same -- Remove asset from squadron same
@ -1809,7 +1852,7 @@ function LEGION:RecruitAssetsForMission(Mission)
end end
-- Recuit assets. -- Recuit assets.
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem, nil) local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads, Mission.engageRange, Mission.refuelSystem)
return recruited, assets, legions return recruited, assets, legions
end end
@ -1826,17 +1869,20 @@ function LEGION:RecruitAssetsForTransport(Transport)
local cargoOpsGroups=Transport:GetCargoOpsGroups(false) local cargoOpsGroups=Transport:GetCargoOpsGroups(false)
local weightGroup=0 local weightGroup=0
local TotalWeight=nil
-- At least one group should be spawned. -- At least one group should be spawned.
if #cargoOpsGroups>0 then if #cargoOpsGroups>0 then
-- Calculate the max weight so we know which cohorts can provide carriers. -- Calculate the max weight so we know which cohorts can provide carriers.
TotalWeight=0
for _,_opsgroup in pairs(cargoOpsGroups) do for _,_opsgroup in pairs(cargoOpsGroups) do
local opsgroup=_opsgroup --Ops.OpsGroup#OPSGROUP local opsgroup=_opsgroup --Ops.OpsGroup#OPSGROUP
local weight=opsgroup:GetWeightTotal() local weight=opsgroup:GetWeightTotal()
if weight>weightGroup then if weight>weightGroup then
weightGroup=weight weightGroup=weight
end end
TotalWeight=TotalWeight+weight
end end
else else
-- No cargo groups! -- No cargo groups!
@ -1854,7 +1900,7 @@ function LEGION:RecruitAssetsForTransport(Transport)
-- Recruit assets and legions. -- Recruit assets and legions.
local recruited, assets, legions=LEGION.RecruitCohortAssets(self.cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NreqMin, NreqMax, TargetVec2, nil, nil, nil, weightGroup) local recruited, assets, legions=LEGION.RecruitCohortAssets(self.cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NreqMin, NreqMax, TargetVec2, nil, nil, nil, weightGroup, TotalWeight)
return recruited, assets, legions return recruited, assets, legions
end end
@ -1915,12 +1961,13 @@ end
-- @param #number RangeMax Max range in meters. -- @param #number RangeMax Max range in meters.
-- @param #number RefuelSystem Refuelsystem. -- @param #number RefuelSystem Refuelsystem.
-- @param #number CargoWeight Cargo weight for recruiting transport carriers. -- @param #number CargoWeight Cargo weight for recruiting transport carriers.
-- @param #number TotalWeight Total cargo weight in kg.
-- @param #table Categories Group categories. -- @param #table Categories Group categories.
-- @param #table Attributes Group attributes. See `GROUP.Attribute.` -- @param #table Attributes Group attributes. See `GROUP.Attribute.`
-- @return #boolean If `true` enough assets could be recruited. -- @return #boolean If `true` enough assets could be recruited.
-- @return #table Recruited assets. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else. -- @return #table Recruited assets. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
-- @return #table Legions of recruited assets. -- @return #table Legions of recruited assets.
function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, Categories, Attributes) function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, NreqMin, NreqMax, TargetVec2, Payloads, RangeMax, RefuelSystem, CargoWeight, TotalWeight, Categories, Attributes)
-- The recruited assets. -- The recruited assets.
local Assets={} local Assets={}
@ -2055,10 +2102,30 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
--- ---
-- Add assets to mission. -- Add assets to mission.
local cargobay=0
for i=1,Nassets do for i=1,Nassets do
local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
asset.isReserved=true asset.isReserved=true
Legions[asset.legion.alias]=asset.legion Legions[asset.legion.alias]=asset.legion
if TotalWeight then
-- Number of
local N=math.floor(asset.cargobaytot/asset.nunits / CargoWeight)*asset.nunits
--env.info(string.format("cargobaytot=%d, cargoweight=%d ==> N=%d", asset.cargobaytot, CargoWeight, N))
cargobay=cargobay + N*CargoWeight
if cargobay>=TotalWeight then
--env.info(string.format("FF found enough assets to transport all cargo! N=%d [%d], cargobay=%.1f >= %.1f kg total weight", i, Nassets, cargobay, TotalWeight))
Nassets=i
break
end
end
end end
-- Return payloads of not needed assets. -- Return payloads of not needed assets.
@ -2153,7 +2220,7 @@ function LEGION:AssignAssetsForEscort(Cohorts, Assets, NescortMin, NescortMax)
end end
-- Recruit escort asset for the mission asset. -- Recruit escort asset for the mission asset.
local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, nil, NescortMin, NescortMax, TargetVec2, nil, nil, nil, nil, Categories) local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, nil, NescortMin, NescortMax, TargetVec2, nil, nil, nil, nil, nil, Categories)
if Erecruited then if Erecruited then
Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets, ecategory=asset.category, TargetTypes=TargetTypes} Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets, ecategory=asset.category, TargetTypes=TargetTypes}
@ -2264,13 +2331,14 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
end end
-- Get all legions and heaviest cargo group weight -- Get all legions and heaviest cargo group weight
local CargoLegions={} ; local CargoWeight=nil local CargoLegions={} ; local CargoWeight=nil ; local TotalWeight=0
for _,_asset in pairs(CargoAssets) do for _,_asset in pairs(CargoAssets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
CargoLegions[asset.legion.alias]=asset.legion CargoLegions[asset.legion.alias]=asset.legion
if CargoWeight==nil or asset.weight>CargoWeight then if CargoWeight==nil or asset.weight>CargoWeight then
CargoWeight=asset.weight CargoWeight=asset.weight
end end
TotalWeight=TotalWeight+asset.weight
end end
-- Target is the deploy zone. -- Target is the deploy zone.
@ -2278,7 +2346,7 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
-- Recruit assets and legions. -- Recruit assets and legions.
local TransportAvail, CarrierAssets, CarrierLegions= local TransportAvail, CarrierAssets, CarrierLegions=
LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NcarriersMin, NcarriersMax, TargetVec2, nil, nil, nil, CargoWeight, Categories, Attributes) LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NcarriersMin, NcarriersMax, TargetVec2, nil, nil, nil, CargoWeight, TotalWeight, Categories, Attributes)
if TransportAvail then if TransportAvail then
@ -2396,12 +2464,34 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
-- Reduce score for legions that are futher away. -- Reduce score for legions that are futher away.
score=score-distance score=score-distance
-- Intercepts need to be carried out quickly. We prefer spawned assets. -- Intercepts need to be carried out quickly. We prefer spawned assets.
--if MissionType==AUFTRAG.Type.INTERCEPT then if asset.spawned and asset.flightgroup and asset.flightgroup:IsAlive() then
if asset.spawned then
score=score+25 local currmission=asset.flightgroup:GetMissionCurrent()
if currmission then
if currmission.type==AUFTRAG.Type.ALERT5 and currmission.alert5MissionType==MissionType then
-- Prefer assets that are on ALERT5 for this mission type.
score=score+25
elseif currmission==AUFTRAG.Type.GCICAP and MissionType==AUFTRAG.Type.INTERCEPT then
-- Prefer assets that are on GCICAP to perform INTERCEPTS
score=score+25
end
end end
--end
if MissionType==AUFTRAG.Type.OPSTRANSPORT or MissionType==AUFTRAG.Type.AMMOSUPPLY or MissionType==AUFTRAG.Type.AWACS or MissionType==AUFTRAG.Type.FUELSUPPLY or MissionType==AUFTRAG.Type.TANKER then
-- TODO: need to check for missions that do not require ammo like transport, recon, awacs, tanker etc.
-- We better take a fresh asset. Sometimes spawned assets to something else, which is difficult to check.
score=score-10
else
-- Combat mission.
if asset.flightgroup:IsOutOfAmmo() then
-- Assets that are out of ammo are not considered.
score=score-1000
end
end
end
-- TRANSPORT specific. -- TRANSPORT specific.
if MissionType==AUFTRAG.Type.OPSTRANSPORT then if MissionType==AUFTRAG.Type.OPSTRANSPORT then

View File

@ -90,7 +90,7 @@ NAVYGROUP = {
--- NavyGroup version. --- NavyGroup version.
-- @field #string version -- @field #string version
NAVYGROUP.version="0.7.0" NAVYGROUP.version="0.7.1"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -760,6 +760,12 @@ function NAVYGROUP:Status(From, Event, To)
-- Check into wind queue. -- Check into wind queue.
self:_CheckTurnsIntoWind() self:_CheckTurnsIntoWind()
-- Check ammo status.
self:_CheckAmmoStatus()
-- Check damage of elements and group.
self:_CheckDamage()
-- Check if group got stuck. -- Check if group got stuck.
self:_CheckStuck() self:_CheckStuck()
@ -775,6 +781,9 @@ function NAVYGROUP:Status(From, Event, To)
end end
end end
else
-- Check damage of elements and group.
self:_CheckDamage()
end end
-- Group exists but can also be inactive. -- Group exists but can also be inactive.
@ -1430,37 +1439,46 @@ function NAVYGROUP:_UpdateEngageTarget()
-- Get current position vector. -- Get current position vector.
local vec3=self.engage.Target:GetVec3() local vec3=self.engage.Target:GetVec3()
if vec3 then
-- Distance to last known position of target. -- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3()) local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
-- Check if target moved more than 100 meters.
if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9) -- Check if target moved more than 100 meters.
if dist>100 then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())
-- Update new position.
self.engage.Coordinate:UpdateFromVec3(vec3)
-- Add waypoint after current. -- ID of current waypoint.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true) local uid=self:GetWaypointCurrent().uid
-- Remove current waypoint
self:RemoveWaypointByID(self.engage.Waypoint.uid)
local intercoord=self:GetCoordinate():GetIntermediateCoordinate(self.engage.Coordinate, 0.9)
-- Set if we want to resume route after reaching the detour waypoint. -- Add waypoint after current.
self.engage.Waypoint.detour=0 self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
-- Set if we want to resume route after reaching the detour waypoint.
self.engage.Waypoint.detour=0
end
else
-- Could not get position of target (not alive any more?) ==> Disengage.
self:Disengage()
end end
else else
-- Target not alive any more == Disengage. -- Target not alive any more ==> Disengage.
self:Disengage() self:Disengage()
end end

View File

@ -466,7 +466,7 @@ OPSGROUP.CargoStatus={
--- OpsGroup version. --- OpsGroup version.
-- @field #string version -- @field #string version
OPSGROUP.version="0.7.5" OPSGROUP.version="0.7.6"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -541,6 +541,9 @@ function OPSGROUP:New(group)
else else
end end
-- Set gen attribute.
self.attribute=self.group:GetAttribute()
local units=self.group:GetUnits() local units=self.group:GetUnits()
@ -864,6 +867,12 @@ function OPSGROUP:GetLifePoints(Element)
return life, life0 return life, life0
end end
--- Get generalized attribute.
-- @param #OPSGROUP self
-- @return #string Generalized attribute.
function OPSGROUP:GetAttribute()
return self.attribute
end
--- Set verbosity level. --- Set verbosity level.
-- @param #OPSGROUP self -- @param #OPSGROUP self
@ -879,6 +888,7 @@ end
-- @param Ops.Legion#LEGION Legion The Legion. -- @param Ops.Legion#LEGION Legion The Legion.
-- @return #OPSGROUP self -- @return #OPSGROUP self
function OPSGROUP:_SetLegion(Legion) function OPSGROUP:_SetLegion(Legion)
self:T2(self.lid..string.format("Adding opsgroup to legion %s", Legion.alias))
self.legion=Legion self.legion=Legion
return self return self
end end
@ -1609,13 +1619,6 @@ function OPSGROUP:Despawn(Delay, NoEventRemoveUnit)
self.scheduleIDDespawn=self:ScheduleOnce(Delay, OPSGROUP.Despawn, self, 0, NoEventRemoveUnit) self.scheduleIDDespawn=self:ScheduleOnce(Delay, OPSGROUP.Despawn, self, 0, NoEventRemoveUnit)
else else
if self.legion and not NoEventRemoveUnit then
-- Add asset back in 10 seconds.
self:T(self.lid..string.format("Despawning Group by adding asset to LEGION!"))
self.legion:AddAsset(self.group, 1)
return
end
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Despawning Group!")) self:T(self.lid..string.format("Despawning Group!"))
@ -1647,6 +1650,30 @@ function OPSGROUP:Despawn(Delay, NoEventRemoveUnit)
return self return self
end end
--- Return group back to the legion it belongs to.
-- Group is despawned and added back to the stock.
-- @param #OPSGROUP self
-- @param #number Delay Delay in seconds before the group will be despawned. Default immediately
-- @return #OPSGROUP self
function OPSGROUP:ReturnToLegion(Delay)
if Delay and Delay>0 then
self.scheduleIDDespawn=self:ScheduleOnce(Delay, OPSGROUP.ReturnToLegion, self)
else
if self.legion then
-- Add asset back.
self:T(self.lid..string.format("Adding asset back to LEGION"))
self.legion:AddAsset(self.group, 1)
else
self:E(self.lid..string.format("ERROR: Group does not belong to a LEGION!"))
end
end
return self
end
--- Destroy a unit of the group. A *Unit Lost* for aircraft or *Dead* event for ground/naval units is generated. --- Destroy a unit of the group. A *Unit Lost* for aircraft or *Dead* event for ground/naval units is generated.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #string UnitName Name of the unit which should be destroyed. -- @param #string UnitName Name of the unit which should be destroyed.
@ -1670,6 +1697,9 @@ function OPSGROUP:DestroyUnit(UnitName, Delay)
else else
self:CreateEventDead(EventTime, unit) self:CreateEventDead(EventTime, unit)
end end
-- Despawn unit.
unit:destroy()
end end
@ -2618,6 +2648,17 @@ function OPSGROUP:GetWaypointCurrent()
return self.waypoints[self.currentwp] return self.waypoints[self.currentwp]
end end
--- Get current waypoint UID.
-- @param #OPSGROUP self
-- @return #number Current waypoint UID.
function OPSGROUP:GetWaypointCurrentUID()
local wp=self:GetWaypointCurrent()
if wp then
return wp.uid
end
return nil
end
--- Get coordinate of next waypoint of the group. --- Get coordinate of next waypoint of the group.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #boolean cyclic If true, return first waypoint if last waypoint was reached. -- @param #boolean cyclic If true, return first waypoint if last waypoint was reached.
@ -3574,8 +3615,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
--Coordinate:MarkToAll("Random Patrol Zone Coordinate") --Coordinate:MarkToAll("Random Patrol Zone Coordinate")
-- Speed and altitude. -- Speed and altitude.
local Speed=UTILS.MpsToKnots(Task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise) local Speed=Task.dcstask.params.speed and UTILS.MpsToKnots(Task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
--local Speed=UTILS.KmphToKnots(Task.dcstask.params.speed or self.speedCruise)
local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude) or nil local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude) or nil
local currUID=self:GetWaypointCurrent().uid local currUID=self:GetWaypointCurrent().uid
@ -3585,7 +3625,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
if self.isFlightgroup then if self.isFlightgroup then
wp=FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude) wp=FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
elseif self.isArmygroup then elseif self.isArmygroup then
wp=ARMYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Formation) wp=ARMYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Task.dcstask.params.formation)
elseif self.isNavygroup then elseif self.isNavygroup then
wp=NAVYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude) wp=NAVYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
end end
@ -3612,7 +3652,6 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- Speed and altitude. -- Speed and altitude.
local Speed=UTILS.MpsToKnots(Task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise) local Speed=UTILS.MpsToKnots(Task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
--local Speed=UTILS.KmphToKnots(Task.dcstask.params.speed or self.speedCruise)
local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude) or nil local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude) or nil
--Coordinate:MarkToAll("Recon Waypoint Execute") --Coordinate:MarkToAll("Recon Waypoint Execute")
@ -3673,7 +3712,6 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- BARRAGE is special! -- BARRAGE is special!
if Task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then if Task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
env.info("FF Barrage")
local vec2=self:GetVec2() local vec2=self:GetVec2()
local param=Task.dcstask.params local param=Task.dcstask.params
local heading=param.heading or math.random(1, 360) local heading=param.heading or math.random(1, 360)
@ -4230,7 +4268,7 @@ function OPSGROUP:onafterMissionStart(From, Event, To, Mission)
-- IMMOBILE Group -- IMMOBILE Group
--- ---
env.info("FF Immobile GROUP") --env.info("FF 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
@ -4580,13 +4618,22 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Refresh DCS task with the known controllable. -- Refresh DCS task with the known controllable.
mission.DCStask=mission:GetDCSMissionTask(self.group) mission.DCStask=mission:GetDCSMissionTask(self.group)
-- Create a pickup zone around the pickup coordinate. The troops will go to a random point inside the zone.
-- This is necessary so the helos do not try to land at the exact same location where the troops wait.
local pradius=mission.transportPickupRadius
local pickupZone=ZONE_RADIUS:New("Pickup Zone", mission.transportPickup:GetVec2(), pradius)
-- Add task to embark for the troops. -- Add task to embark for the troops.
for _,_group in pairs(mission.transportGroupSet.Set) do for _,_group in pairs(mission.transportGroupSet.Set) do
local group=_group --Wrapper.Group#GROUP local group=_group --Wrapper.Group#GROUP
if group and group:IsAlive() then if group and group:IsAlive() then
local DCSTask=group:TaskEmbarkToTransport(mission.transportPickup, 500) -- Get random coordinate inside the zone.
local pcoord=pickupZone:GetRandomCoordinate(20, pradius, {land.SurfaceType.LAND, land.SurfaceType.ROAD})
-- Let the troops embark the transport.
local DCSTask=group:TaskEmbarkToTransport(pcoord, pradius)
group:SetTask(DCSTask, 5) group:SetTask(DCSTask, 5)
end end
@ -4901,7 +4948,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
local Coordinate=zone:GetRandomCoordinate(nil, nil, surfacetypes) local Coordinate=zone:GetRandomCoordinate(nil, nil, surfacetypes)
-- Speed and altitude. -- Speed and altitude.
local Speed=UTILS.MpsToKnots(task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise) local Speed=task.dcstask.params.speed and UTILS.MpsToKnots(task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
-- local Speed=UTILS.KmphToKnots(speed or self.speedCruise) -- local Speed=UTILS.KmphToKnots(speed or self.speedCruise)
local Altitude=UTILS.MetersToFeet(task.dcstask.params.altitude or self.altitudeCruise) local Altitude=UTILS.MetersToFeet(task.dcstask.params.altitude or self.altitudeCruise)
@ -4911,7 +4958,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
if self.isFlightgroup then if self.isFlightgroup then
wp=FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude) wp=FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
elseif self.isArmygroup then elseif self.isArmygroup then
wp=ARMYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Formation) wp=ARMYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, task.dcstask.params.formation)
elseif self.isNavygroup then elseif self.isNavygroup then
wp=NAVYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude) wp=NAVYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
end end
@ -4938,8 +4985,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
local Coordinate=zone:GetRandomCoordinate() local Coordinate=zone:GetRandomCoordinate()
-- Speed and altitude. -- Speed and altitude.
local Speed=UTILS.MpsToKnots(task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise) local Speed=task.dcstask.params.speed and UTILS.MpsToKnots(task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
--local Speed=UTILS.KmphToKnots(task.dcstask.params.speed or self.speedCruise)
local Altitude=task.dcstask.params.altitude and UTILS.MetersToFeet(task.dcstask.params.altitude) or nil local Altitude=task.dcstask.params.altitude and UTILS.MetersToFeet(task.dcstask.params.altitude) or nil
-- Debug. -- Debug.
@ -5759,6 +5805,38 @@ function OPSGROUP:onafterElementInUtero(From, Event, To, Element)
end end
--- On after "ElementDamaged" event.
-- @param #OPSGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #OPSGROUP.Element Element The flight group element.
function OPSGROUP:onafterElementDamaged(From, Event, To, Element)
self:T(self.lid..string.format("Element damaged %s", Element.name))
if Element and (Element.status~=OPSGROUP.ElementStatus.DEAD and Element.status~=OPSGROUP.ElementStatus.INUTERO) then
local lifepoints=0
if Element.DCSunit and Element.DCSunit:isExist() then
-- Get life of unit
lifepoints=Element.DCSunit:getLife()
-- Debug output.
self:T(self.lid..string.format("Element life %s: %.2f/%.2f", Element.name, lifepoints, Element.life0))
end
if lifepoints<=1.0 then
self:T(self.lid..string.format("Element %s life %.2f <= 1.0 ==> Destroyed!", Element.name, lifepoints))
self:ElementDestroyed(Element)
end
end
end
--- On after "ElementDestroyed" event. --- On after "ElementDestroyed" event.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #string From From state. -- @param #string From From state.
@ -5793,7 +5871,7 @@ end
function OPSGROUP:onafterElementDead(From, Event, To, Element) function OPSGROUP:onafterElementDead(From, Event, To, Element)
-- Debug info. -- Debug info.
self:T(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime())) self:I(self.lid..string.format("Element dead %s at t=%.3f", Element.name, timer.getTime()))
-- Set element status. -- Set element status.
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD) self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
@ -5890,6 +5968,80 @@ function OPSGROUP:onafterRespawn(From, Event, To, Template)
end end
--- Teleport the group to a different location.
-- @param #OPSGROUP self
-- @param Core.Point#COORDINATE Coordinate Coordinate where the group is teleported to.
-- @param #number Delay Delay in seconds before respawn happens. Default 0.
-- @return #OPSGROUP self
function OPSGROUP:Teleport(Coordinate, Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, OPSGROUP.Teleport, self, Coordinate)
else
-- Debug message.
self:T(self.lid.."FF Teleporting...")
--Coordinate:MarkToAll("Teleport "..self.groupname)
-- Check if we have a mission running.
if self.currentmission>0 then
self:T(self.lid.."Pausing current mission")
self:PauseMission()
end
-- Get copy of template.
local Template=UTILS.DeepCopy(self.template) --DCS#Template
-- Template units.
local units=Template.units
-- Table with teleported vectors.
local d={}
for i=1,#units do
local unit=units[i]
d[i]={x=Coordinate.x+(units[i].x-units[1].x), y=Coordinate.z+units[i].y-units[1].y}
--COORDINATE:NewFromVec2(d[i]):MarkToAll(unit.name.." teleported")
end
for i=#units,1,-1 do
local unit=units[i]
-- Get element.
local element=self:GetElementByName(unit.name)
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
-- No parking.
unit.parking=nil
unit.parking_id=nil
-- Current position.
local vec3=element.unit:GetVec3()
-- Current heading.
local heading=element.unit:GetHeading()
-- Set new x,y.
unit.x=d[i].x
unit.y=d[i].y
-- Set altitude.
unit.alt=Coordinate.y
-- Set heading.
unit.heading=math.rad(heading)
unit.psi=-unit.heading
else
table.remove(units, i)
end
end
-- Respawn from new template.
self:_Respawn(0, Template, true)
end
end
--- Respawn the group. --- Respawn the group.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #number Delay Delay in seconds before respawn happens. Default 0. -- @param #number Delay Delay in seconds before respawn happens. Default 0.
@ -5905,58 +6057,60 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Debug message. -- Debug message.
self:T2(self.lid.."FF _Respawn") self:T2(self.lid.."FF _Respawn")
-- Given template or get old. -- Given template or get copy of old.
Template=Template or self:_GetTemplate(true) Template=Template or self:_GetTemplate(true)
-- Number of destroyed units.
self.Ndestroyed=0
-- Check if group is currently alive.
if self:IsAlive() then if self:IsAlive() then
--- ---
-- Group is ALIVE -- Group is ALIVE
--- ---
--[[ -- Template units.
-- Get units.
local units=self.group:GetUnits()
-- Loop over template units.
for UnitID, Unit in pairs(Template.units) do
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
if unit:GetName()==Unit.name then
local vec3=unit:GetVec3()
local heading=unit:GetHeading()
Unit.x=vec3.x
Unit.y=vec3.z
Unit.alt=vec3.y
Unit.heading=math.rad(heading)
Unit.psi=-Unit.heading
end
end
end
]]
local units=Template.units local units=Template.units
for i=#units,1,-1 do for i=#units,1,-1 do
local unit=units[i] local unit=units[i]
-- Get the element.
local element=self:GetElementByName(unit.name) local element=self:GetElementByName(unit.name)
if element and element.status~=OPSGROUP.ElementStatus.DEAD then if element and element.status~=OPSGROUP.ElementStatus.DEAD then
unit.parking=element.parking and element.parking.TerminalID or unit.parking
unit.parking_id=nil if not Reset then
local vec3=element.unit:GetVec3()
local heading=element.unit:GetHeading() -- Parking ID.
unit.x=vec3.x unit.parking=element.parking and element.parking.TerminalID or unit.parking
unit.y=vec3.z unit.parking_id=nil
unit.alt=vec3.y
unit.heading=math.rad(heading) -- Get current position vector.
unit.psi=-unit.heading local vec3=element.unit:GetVec3()
-- Get heading.
local heading=element.unit:GetHeading()
-- Set unit position.
unit.x=vec3.x
unit.y=vec3.z
unit.alt=vec3.y
-- Set heading in rad.
unit.heading=math.rad(heading)
unit.psi=-unit.heading
end
else else
-- Element is dead. Remove from template.
table.remove(units, i) table.remove(units, i)
self.Ndestroyed=self.Ndestroyed+1
end end
end end
@ -5967,7 +6121,7 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
else else
--- ---
-- Group is DESPAWNED -- Group is NOT ALIVE
--- ---
-- Ensure elements in utero. -- Ensure elements in utero.
@ -5997,8 +6151,7 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
self.isDestroyed=false self.isDestroyed=false
self.groupinitialized=false self.groupinitialized=false
self.Ndestroyed=0
self.wpcounter=1 self.wpcounter=1
self.currentwp=1 self.currentwp=1
@ -6007,7 +6160,7 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Init Group. -- Init Group.
self:_InitGroup(Template) self:_InitGroup(Template)
-- Reset events. -- Reset events.
--self:ResetEvents() --self:ResetEvents()
@ -6074,6 +6227,19 @@ function OPSGROUP:onbeforeDead(From, Event, To)
end end
end end
--- Cancel all missions in mission queue.
-- @param #OPSGROUP self
function OPSGROUP:CancelAllMissions()
-- Cancel all missions.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
self:T(self.lid.."Cancelling mission "..tostring(mission:GetName()))
self:MissionCancel(mission)
end
end
--- On after "Dead" event. --- On after "Dead" event.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #string From From state. -- @param #string From From state.
@ -6131,12 +6297,31 @@ function OPSGROUP:onafterDead(From, Event, To)
-- All elements were destroyed ==> Asset group is gone. -- All elements were destroyed ==> Asset group is gone.
self.cohort:DelGroup(self.groupname) self.cohort:DelGroup(self.groupname)
end end
if self.legion then
--self.legion:Get
--self.legion:AssetDead()
end
else else
-- Not all assets were destroyed (despawn) ==> Add asset back to legion? -- Not all assets were destroyed (despawn) ==> Add asset back to legion?
end end
-- Stop in a sec. if self.legion then
--self:__Stop(-5) if not self:IsInUtero() then
-- Get asset.
local asset=self.legion:GetAssetByName(self.groupname)
-- Get request.
local request=self.legion:GetRequestByID(asset.rid)
-- Trigger asset dead event.
self.legion:AssetDead(asset, request)
end
-- Stop in 5 sec to give possible respawn attempts a chance.
self:__Stop(-5)
end
end end
--- On before "Stop" event. --- On before "Stop" event.
@ -6178,6 +6363,11 @@ function OPSGROUP:onafterStop(From, Event, To)
self:UnHandleEvent(EVENTS.Crash) self:UnHandleEvent(EVENTS.Crash)
self.currbase=nil self.currbase=nil
end end
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
self:MissionCancel(mission)
end
-- Stop check timers. -- Stop check timers.
self.timerCheckZone:Stop() self.timerCheckZone:Stop()
@ -7993,6 +8183,12 @@ end
-- @param #OPSGROUP OpsGroupCargo Cargo OPSGROUP that was unloaded from a carrier. -- @param #OPSGROUP OpsGroupCargo Cargo OPSGROUP that was unloaded from a carrier.
function OPSGROUP:onafterUnloaded(From, Event, To, OpsGroupCargo) function OPSGROUP:onafterUnloaded(From, Event, To, OpsGroupCargo)
self:T(self.lid..string.format("Unloaded OPSGROUP %s", OpsGroupCargo:GetName())) self:T(self.lid..string.format("Unloaded OPSGROUP %s", OpsGroupCargo:GetName()))
if OpsGroupCargo.legion and OpsGroupCargo:IsInZone(OpsGroupCargo.legion.spawnzone) then
self:T(self.lid..string.format("Unloaded group %s returned to legion", OpsGroupCargo:GetName()))
OpsGroupCargo:Returned()
end
end end
@ -8530,18 +8726,24 @@ function OPSGROUP:_CheckGroupDone(delay)
return return
end end
-- Group is returning -- Group is returning.
if self:IsReturning() then if self:IsReturning() then
self:T(self.lid.."Returning! Group NOT done...") self:T(self.lid.."Returning! Group NOT done...")
return return
end end
-- Group is returning -- Group is rearming.
if self:IsRearming() then if self:IsRearming() then
self:T(self.lid.."Rearming! Group NOT done...") self:T(self.lid.."Rearming! Group NOT done...")
return return
end end
-- Group is retreating.
if self:IsRetreating() then
self:T(self.lid.."Retreating! Group NOT done...")
return
end
-- Group is waiting. We deny all updates. -- Group is waiting. We deny all updates.
if self:IsWaiting() then if self:IsWaiting() then
-- If group is waiting, we assume that is the way it is meant to be. -- If group is waiting, we assume that is the way it is meant to be.

View File

@ -57,6 +57,8 @@
-- @field #table statusLegion Transport status of all assigned LEGIONs. -- @field #table statusLegion Transport status of all assigned LEGIONs.
-- @field #string statusCommander Staus of the COMMANDER. -- @field #string statusCommander Staus of the COMMANDER.
-- @field Ops.Commander#COMMANDER commander Commander of the transport. -- @field Ops.Commander#COMMANDER commander Commander of the transport.
-- @field Ops.Chief#CHIEF chief Chief of the transport.
-- @field Ops.OpsZone#OPSZONE opszone OPS zone.
-- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the transport was cancelled before the request is processed. -- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the transport was cancelled before the request is processed.
-- --
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
@ -1305,7 +1307,7 @@ function OPSTRANSPORT:GetNcarrier()
return self.Ncarrier return self.Ncarrier
end end
--- Add asset to transport. --- Add carrier asset to transport.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be added. -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be added.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
@ -1323,7 +1325,7 @@ function OPSTRANSPORT:AddAsset(Asset, TransportZoneCombo)
return self return self
end end
--- Delete asset from mission. --- Delete carrier asset from transport.
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be removed. -- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be removed.
-- @return #OPSTRANSPORT self -- @return #OPSTRANSPORT self
@ -1736,12 +1738,12 @@ function OPSTRANSPORT:onafterDeadCarrierGroup(From, Event, To, OpsGroup)
-- Increase dead counter. -- Increase dead counter.
self.NcarrierDead=self.NcarrierDead+1 self.NcarrierDead=self.NcarrierDead+1
if #self.carriers==0 then
self:DeadCarrierAll()
end
-- Remove group from carrier list/table. -- Remove group from carrier list/table.
self:_DelCarrier(OpsGroup) self:_DelCarrier(OpsGroup)
if #self.carriers==0 then
self:DeadCarrierAll()
end
end end
--- On after "DeadCarrierAll" event. --- On after "DeadCarrierAll" event.
@ -1750,15 +1752,30 @@ end
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
function OPSTRANSPORT:onafterDeadCarrierAll(From, Event, To) function OPSTRANSPORT:onafterDeadCarrierAll(From, Event, To)
self:I(self.lid..string.format("ALL Carrier OPSGROUPs are dead! Setting stage to PLANNED if not all cargo was delivered.")) self:I(self.lid..string.format("ALL Carrier OPSGROUPs are dead!"))
if self.opszone then
self:I(self.lid..string.format("Cancelling transport on CHIEF level"))
self.chief:TransportCancel(self)
--for _,_legion in pairs(self.legions) do
-- local legion=_legion --Ops.Legion#LEGION
-- legion:TransportCancel(self)
--end
else
-- Check if cargo was delivered. -- Check if cargo was delivered.
self:_CheckDelivered() self:_CheckDelivered()
-- Set state back to PLANNED if not delivered.
if not self:IsDelivered() then
self:Planned()
end
-- Set state back to PLANNED if not delivered.
if not self:IsDelivered() then
self:Planned()
end end
end end
--- On after "Cancel" event. --- On after "Cancel" event.

View File

@ -1097,6 +1097,14 @@ function UTILS.VecSubstract(a, b)
return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z} return {x=a.x-b.x, y=a.y-b.y, z=a.z-b.z}
end end
--- Calculate the difference between two 2D vectors by substracting the x,y components from each other.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param DCS#Vec2 b Vector in 2D with x, y components.
-- @return DCS#Vec2 Vector c=a-b with c(i)=a(i)-b(i), i=x,y.
function UTILS.Vec2Substract(a, b)
return {x=a.x-b.x, y=a.y-b.y}
end
--- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other. --- Calculate the total vector of two 3D vectors by adding the x,y,z components of each other.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components.
@ -1105,6 +1113,14 @@ function UTILS.VecAdd(a, b)
return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z} return {x=a.x+b.x, y=a.y+b.y, z=a.z+b.z}
end end
--- Calculate the total vector of two 2D vectors by adding the x,y components of each other.
-- @param DCS#Vec2 a Vector in 2D with x, y components.
-- @param DCS#Vec2 b Vector in 2D with x, y components.
-- @return DCS#Vec2 Vector c=a+b with c(i)=a(i)+b(i), i=x,y.
function UTILS.Vec2Add(a, b)
return {x=a.x+b.x, y=a.y+b.y}
end
--- Calculate the angle between two 3D vectors. --- Calculate the angle between two 3D vectors.
-- @param DCS#Vec3 a Vector in 3D with x, y, z components. -- @param DCS#Vec3 a Vector in 3D with x, y, z components.
-- @param DCS#Vec3 b Vector in 3D with x, y, z components. -- @param DCS#Vec3 b Vector in 3D with x, y, z components.
@ -1321,26 +1337,6 @@ function UTILS.GetMissionDayOfYear(Time)
end end
--- Returns the current date.
-- @return #string Mission date in yyyy/mm/dd format.
-- @return #number The year anno domini.
-- @return #number The month.
-- @return #number The day.
function UTILS.GetDate()
-- Mission start date
local date, year, month, day=UTILS.GetDCSMissionDate()
local time=timer.getAbsTime()
local clock=UTILS.SecondsToClock(time, false)
local x=tonumber(UTILS.Split(clock, "+")[2])
local day=day+x
end
--- Returns the magnetic declination of the map. --- Returns the magnetic declination of the map.
-- Returned values for the current maps are: -- Returned values for the current maps are:
-- --
@ -1751,6 +1747,11 @@ function UTILS.IsLoadingDoorOpen( unit_name )
ret_val = true ret_val = true
end end
if string.find(type_name, "AH-64D") then
BASE:T(unit_name .. " front door(s) are open")
ret_val = true -- no doors on this one ;)
end
if ret_val == false then if ret_val == false then
BASE:T(unit_name .. " all doors are closed") BASE:T(unit_name .. " all doors are closed")
end end

View File

@ -385,6 +385,16 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Beirut_Rafic_Hariri -- * AIRBASE.Syria.Beirut_Rafic_Hariri
-- * AIRBASE.Syria.An_Nasiriyah -- * AIRBASE.Syria.An_Nasiriyah
-- * AIRBASE.Syria.Abu_al_Duhur -- * AIRBASE.Syria.Abu_al_Duhur
-- * AIRBASE.Syria.At_Tanf
-- * AIRBASE.Syria.H3
-- * AIRBASE.Syria.H3_Northwest
-- * AIRBASE.Syria.H3_Southwest
-- * AIRBASE.Syria.Kharab_Ishk
-- * AIRBASE.Syria.Raj_al_Issa_East
-- * AIRBASE.Syria.Raj_al_Issa_West
-- * AIRBASE.Syria.Ruwayshid
-- * AIRBASE.Syria.Sanliurfa
-- * AIRBASE.Syria.Tal_Siman
-- --
--@field Syria --@field Syria
AIRBASE.Syria={ AIRBASE.Syria={
@ -440,10 +450,18 @@ AIRBASE.Syria={
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri", ["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
["An_Nasiriyah"]="An Nasiriyah", ["An_Nasiriyah"]="An Nasiriyah",
["Abu_al_Duhur"]="Abu al-Duhur", ["Abu_al_Duhur"]="Abu al-Duhur",
["At_Tanf"]="At Tanf",
["H3"]="H3",
["H3_Northwest"]="H3 Northwest",
["H3_Southwest"]="H3 Southwest",
["Kharab_Ishk"]="Kharab Ishk",
["Raj_al_Issa_East"]="Raj al Issa East",
["Raj_al_Issa_West"]="Raj al Issa West",
["Ruwayshid"]="Ruwayshid",
["Sanliurfa"]="Sanliurfa",
["Tal_Siman"]="Tal Siman",
} }
--- Airbases of the Mariana Islands map: --- Airbases of the Mariana Islands map:
-- --
-- * AIRBASE.MarianaIslands.Rota_Intl -- * AIRBASE.MarianaIslands.Rota_Intl

View File

@ -3844,3 +3844,45 @@ function POSITIONABLE:IsSubmarine()
return nil return nil
end end
--- Sets the controlled group to go at the specified speed in meters per second.
-- @param #CONTROLLABLE self
-- @param #number Speed Speed in meters per second
-- @param #boolean Keep (Optional) When set to true, will maintain the speed on passing waypoints. If not present or false, the controlled group will return to the speed as defined by their route.
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetSpeed(Speed, Keep)
self:F2( { self.ControllableName } )
-- Set default if not specified.
local speed = Speed or 5
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
Controller:setSpeed(speed, Keep)
end
end
return self
end
--- [AIR] Sets the controlled aircraft group to fly at the specified altitude in meters.
-- @param #CONTROLLABLE self
-- @param #number Altitude Altitude in meters.
-- @param #boolean Keep (Optional) When set to true, will maintain the altitude on passing waypoints. If not present or false, the controlled group will return to the altitude as defined by their route.
-- @param #string AltType (Optional) Specifies the altitude type used. If nil, the altitude type of the current waypoint will be used. Accepted values are "BARO" and "RADIO".
-- @return #CONTROLLABLE self
function CONTROLLABLE:SetAltitude(Altitude, Keep, AltType)
self:F2( { self.ControllableName } )
-- Set default if not specified.
local altitude = Altitude or 1000
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
if Controller then
if self:IsAir() then
Controller:setAltitude(altitude, Keep, AltType)
end
end
end
return self
end

View File

@ -765,8 +765,7 @@ end
--- Returns the average velocity Vec3 vector. --- Returns the average velocity Vec3 vector.
-- @param Wrapper.Group#GROUP self -- @param Wrapper.Group#GROUP self
-- @return DCS#Vec3 The velocity Vec3 vector -- @return DCS#Vec3 The velocity Vec3 vector or `#nil` if the GROUP is not existing or alive.
-- @return #nil The GROUP is not existing or alive.
function GROUP:GetVelocityVec3() function GROUP:GetVelocityVec3()
self:F2( self.GroupName ) self:F2( self.GroupName )
@ -1009,9 +1008,8 @@ end
--- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. --- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP.
-- @param #GROUP self -- @param #GROUP self
-- @param #number Radius -- @param #number Radius Radius in meters.
-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP. -- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP or #nil The GROUP is invalid or empty.
-- @return #nil The GROUP is invalid or empty
-- @usage -- @usage
-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP -- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP
function GROUP:GetRandomVec3(Radius) function GROUP:GetRandomVec3(Radius)
@ -1032,8 +1030,7 @@ end
--- Returns the mean heading of every UNIT in the GROUP in degrees --- Returns the mean heading of every UNIT in the GROUP in degrees
-- @param #GROUP self -- @param #GROUP self
-- @return #number mean heading of the GROUP -- @return #number Mean heading of the GROUP in degrees or #nil The first UNIT is not existing or alive.
-- @return #nil The first UNIT is not existing or alive.
function GROUP:GetHeading() function GROUP:GetHeading()
self:F2(self.GroupName) self:F2(self.GroupName)
@ -1061,8 +1058,8 @@ end
--- Return the fuel state and unit reference for the unit with the least --- Return the fuel state and unit reference for the unit with the least
-- amount of fuel in the group. -- amount of fuel in the group.
-- @param #GROUP self -- @param #GROUP self
-- @return #number The fuel state of the unit with the least amount of fuel -- @return #number The fuel state of the unit with the least amount of fuel.
-- @return #Unit reference to #Unit object for further processing -- @return #Unit reference to #Unit object for further processing.
function GROUP:GetFuelMin() function GROUP:GetFuelMin()
self:F3(self.ControllableName) self:F3(self.ControllableName)

View File

@ -152,7 +152,7 @@ _MARKERID=0
--- Marker class version. --- Marker class version.
-- @field #string version -- @field #string version
MARKER.version="0.1.0" MARKER.version="0.1.1"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@ -177,7 +177,7 @@ function MARKER:New(Coordinate, Text)
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #MARKER local self=BASE:Inherit(self, FSM:New()) -- #MARKER
self.coordinate=Coordinate self.coordinate=UTILS.DeepCopy(Coordinate)
self.text=Text self.text=Text

View File

@ -1494,7 +1494,7 @@ do -- Cargo
-- Fuel. The descriptor provides the max fuel mass in kg. This needs to be multiplied by the relative fuel amount to calculate the actual fuel mass on board. -- Fuel. The descriptor provides the max fuel mass in kg. This needs to be multiplied by the relative fuel amount to calculate the actual fuel mass on board.
local massFuelMax=Desc.fuelMassMax or 0 local massFuelMax=Desc.fuelMassMax or 0
local relFuel=math.max(self:GetFuel() or 1.0, 1.0) -- We take 1.0 as max in case of external fuel tanks. local relFuel=math.min(self:GetFuel() or 1.0, 1.0) -- We take 1.0 as max in case of external fuel tanks.
local massFuel=massFuelMax*relFuel local massFuel=massFuelMax*relFuel
-- Number of soldiers according to DCS function -- Number of soldiers according to DCS function