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 moving zones around a unit.
-- * 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.
-- * Smoke zones.
-- * Set a zone probability to control zone selection.
@ -20,10 +20,10 @@
-- * 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.
-- * 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:
--
@ -1219,7 +1219,7 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
while gotit==false and N<=Nmax do
gotit=_checkSurface(point)
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
point=_getpoint()
N=N+1
@ -2126,12 +2126,12 @@ end
--
-- ## 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.
-- 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.
-- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object

View File

@ -674,6 +674,7 @@ end
function AUTOLASE:CanLase(Recce,Unit)
local canlase = false
-- cooldown?
if Recce and Recce:IsAlive() == true then
local name = Recce:GetName()
local cooldown = self.RecceUnits[name].cooldown and self.forcecooldown
if cooldown then
@ -694,6 +695,7 @@ function AUTOLASE:CanLase(Recce,Unit)
if distance <= lasedistance and islos then
canlase = true
end
end
return canlase
end

View File

@ -98,6 +98,7 @@
-- * Patriot
-- * Rapier
-- * 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
-- * 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" },
["Chaparrel"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Chaparral" },
["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
["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" },

View File

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

View File

@ -19,7 +19,7 @@
--
-- ### 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.
--
-- 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
-- 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.
@ -79,6 +79,7 @@ SEAD = {
["Kh25"] = "Kh25",
["BGM_109"] = "BGM_109",
["AGM_154"] = "AGM_154",
["HY-2"] = "HY-2",
}
--- Missile enumerators - from DCS ME and Wikipedia
@ -98,6 +99,7 @@ SEAD = {
["Kh25"] = {25, 0.8},
["BGM_109"] = {460, 0.705}, --in-game ~465kn
["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.
@ -141,7 +143,7 @@ function SEAD:New( SEADGroupPrefixes, Padding )
self:AddTransition("*", "ManageEvasion", "*")
self:AddTransition("*", "CalculateHitZone", "*")
self:I("*** SEAD - Started Version 0.4.2")
self:I("*** SEAD - Started Version 0.4.3")
return self
end
@ -267,9 +269,10 @@ end
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
-- @param #number height Height when the missile was fired
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
-- @param #string SEADWeaponName Weapon Name
-- @return #SEAD self
function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup)
self:T("**** Calculating hit zone")
function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup,SEADWeaponName)
self:T("**** Calculating hit zone for " .. (SEADWeaponName or "None"))
if SEADWeapon and SEADWeapon:isExist() then
--local pos = SEADWeapon:getPoint()
@ -285,6 +288,9 @@ function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADG
-- velocity
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 c1 = (2*mheight*9.81)/(mveloc^2)
local c2 = (mveloc^2) / 9.81
@ -459,14 +465,15 @@ function SEAD:HandleEventShot( EventData )
local _targetskill = "Random"
local _targetgroupname = "none"
local _target = EventData.Weapon:getTarget() -- Identify target
if not _target or self.debug then -- AGM-88 w/o target data
if string.find(SEADWeaponName,"AGM_88",1,true) then
self:I("**** Tracking AGM-88 with no target data.")
if not _target or self.debug then -- AGM-88 or 154 w/o target data
self:E("***** SEAD - No target data for " .. (SEADWeaponName or "None"))
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 fheight = SEADPlane:GetHeight()
self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup)
return self
self:__CalculateHitZone(20,SEADWeapon,pos0,fheight,SEADGroup,SEADWeaponName)
end
return self
end
local targetcat = _target:getCategory() -- Identify category
local _targetUnit = nil -- Wrapper.Unit#UNIT

View File

@ -4107,6 +4107,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
local cargobay={}
local cargobaytot=0
local cargobaymax=0
local weights={}
for _i,_unit in pairs(group:GetUnits()) do
local unit=_unit --Wrapper.Unit#UNIT
local Desc=unit:GetDesc()
@ -4115,6 +4116,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
local unitweight=forceweight or Desc.massEmpty
if unitweight then
weight=weight+unitweight
weights[_i]=unitweight
end
local cargomax=0
@ -4165,6 +4167,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay,
asset.speedmax=SpeedMax
asset.size=smax
asset.weight=weight
asset.weights=weights
asset.DCSdesc=Descriptors
asset.attribute=attribute
asset.cargobay=cargobay
@ -5449,9 +5452,59 @@ end
-- @param #WAREHOUSE.Assetitem asset The asset that is dead.
-- @param #WAREHOUSE.Pendingitem request The request of the dead asset.
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)
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
@ -6556,7 +6609,8 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData)
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.
if EventData.IniGroup then
@ -6571,7 +6625,7 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData)
if wid==self.uid then
-- 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.
for _,request in pairs(self.pending) do
@ -6581,7 +6635,7 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData)
if request.uid==rid then
-- 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
@ -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.
-- @param #WAREHOUSE self
-- @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.
function WAREHOUSE:_UnitDead(deadunit, request)
function WAREHOUSE:_UnitDead(deadunit, deadgroup, request)
self:F(self.lid.."FF unit dead "..deadunit:GetName())
-- Flare unit.
if self.Debug then
deadunit:FlareRed()
-- Find opsgroup.
local opsgroup=_DATABASE:FindOpsGroup(deadgroup)
-- Check if we have an opsgroup.
if opsgroup then
-- Handled in OPSGROUP:onafterDead() now.
return nil
end
-- Group the dead unit belongs to.
local group=deadunit:GetGroup()
-- Number of alive units in group.
local nalive=group:CountAliveUnits()
local nalive=deadgroup:CountAliveUnits()
-- Whole group is dead?
local groupdead=true
local groupdead=false
if nalive>0 then
groupdead=false
else
groupdead=true
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!
local unitname=self:_GetNameWithOut(deadunit)
local groupname=self:_GetNameWithOut(group)
local groupname=self:_GetNameWithOut(deadgroup)
-- Group is dead!
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
group:SmokeWhite()
deadgroup:SmokeWhite()
end
-- Trigger AssetDead event.
local asset=self:FindAssetInDB(group)
self:AssetDead(asset, request)
end
@ -6633,19 +6695,7 @@ function WAREHOUSE:_UnitDead(deadunit, request)
-- 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.
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
if not request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then
---
-- Complicated case: Dead unit could be:
@ -6653,10 +6703,7 @@ function WAREHOUSE:_UnitDead(deadunit, request)
-- 2.) A Transport unit which itself holds cargo groups.
---
-- Check if this a cargo or transport group.
local istransport=self:_GroupIsTransport(group,request)
if istransport==true then
if not asset.iscargo then
-- Get the carrier unit table holding the cargo groups inside this carrier.
local cargogroupnames=request.carriercargo[unitname]
@ -6671,25 +6718,8 @@ function WAREHOUSE:_UnitDead(deadunit, request)
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
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
@ -8065,6 +8095,28 @@ end
-- @return #number Request ID.
function WAREHOUSE:_GetIDsFromGroup(group)
if group then
-- Group name
local groupname=group:GetName()
local wid, aid, rid=self:_GetIDsFromGroupName(groupname)
return wid,aid,rid
else
self:E("WARNING: Group not found in GetIDsFromGroup() function!")
end
end
--- Get warehouse id, asset id and request id from group name (alias).
-- @param #WAREHOUSE self
-- @param #string groupname Name of the group from which the info is gathered.
-- @return #number Warehouse ID.
-- @return #number Asset ID.
-- @return #number Request ID.
function WAREHOUSE:_GetIDsFromGroupName(groupname)
---@param #string text The text to analyse.
local function analyse(text)
@ -8094,13 +8146,9 @@ function WAREHOUSE:_GetIDsFromGroup(group)
return _wid,_aid,_rid
end
if group then
-- Group name
local name=group:GetName()
-- Get asset id from group name.
local wid,aid,rid=analyse(name)
local wid,aid,rid=analyse(groupname)
-- Get Asset.
local asset=self:GetAssetByID(aid)
@ -8112,75 +8160,12 @@ function WAREHOUSE:_GetIDsFromGroup(group)
end
-- Debug info
self:T3(self.lid..string.format("Group Name = %s", tostring(name)))
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
else
self:E("WARNING: Group not found in GetIDsFromGroup() function!")
end
end
--- Get warehouse id, asset id and request id from group name (alias).
-- @param #WAREHOUSE self
-- @param Wrapper.Group#GROUP group The group from which the info is gathered.
-- @return #number Warehouse ID.
-- @return #number Asset ID.
-- @return #number Request ID.
function WAREHOUSE:_GetIDsFromGroupOLD(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
-- Group name
local name=group:GetName()
-- Get ids
local wid,aid,rid=analyse(name)
-- 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
else
self:E("WARNING: Group not found in GetIDsFromGroup() function!")
end
end
--- Filter stock assets by descriptor and attribute.

View File

@ -46,7 +46,7 @@
--
-- # The ARMYGROUP Concept
--
-- This class enhances naval groups.
-- This class enhances ground groups.
--
-- @field #ARMYGROUP
ARMYGROUP = {
@ -115,6 +115,7 @@ function ARMYGROUP:New(group)
self:AddTransition("*", "Cruise", "Cruising") -- Cruise along the given route of waypoints.
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("*", "Detour", "OnDetour") -- Make a detour to a coordinate and resume route afterwards.
@ -653,6 +654,9 @@ function ARMYGROUP:Status()
end
end
else
-- Check damage of elements and group.
self:_CheckDamage()
end
-- Check that group EXISTS.
@ -706,7 +710,6 @@ function ARMYGROUP:Status()
local name=element.name
local status=element.status
local unit=element.unit
--local life=unit:GetLifeRelative() or 0
local life,life0=self:GetLifePoints(element)
local life0=element.life0
@ -926,7 +929,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
-- Next waypoint.
local wp=UTILS.DeepCopy(self.waypoints[i]) --Ops.OpsGroup#OPSGROUP.Waypoint
self:T({wp})
-- Speed.
if Speed then
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.
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
local wptable,length,valid=self:GetCoordinate():GetPathOnRoad(wp.coordinate or wp.roadcoord,true,false,false,false) or {}
@ -1003,6 +1013,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, N, Speed, Formation)
current=self:GetClosestRoad():WaypointGround(UTILS.MpsToKmph(self.speedWp), ENUMS.Formation.Vehicle.OnRoad)
table.insert(waypoints, count, current)
end
]]
end
-- Debug output.
@ -1106,16 +1117,6 @@ end
function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
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.
--TODO: IsMobile() check
if self.rearmOnOutOfAmmo then
@ -1139,6 +1140,16 @@ function ARMYGROUP:onafterOutOfAmmo(From, Event, To)
self:__RTZ(-1)
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
@ -1224,9 +1235,6 @@ end
-- @param #number Formation Formation of the group.
function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Zone.
local zone=Zone or self.homezone
@ -1241,6 +1249,9 @@ function ARMYGROUP:onafterRTZ(From, Event, To, Zone, Formation)
local Coordinate=zone:GetRandomCoordinate()
-- ID of current waypoint.
local uid=self:GetWaypointCurrentUID()
-- Add waypoint after current.
local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true)
@ -1352,6 +1363,9 @@ function ARMYGROUP:onafterRetreat(From, Event, To, Zone, Formation)
-- Set if we want to resume route after reaching the detour waypoint.
wp.detour=0
-- Cancel all missions.
self:CancelAllMissions()
end
--- On after "Retreated" event.
@ -1417,6 +1431,7 @@ end
function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
self:T(self.lid.."Engaging Target")
-- Make sure this is a target.
if Target:IsInstanceOf("TARGET") then
self.engage.Target=Target
else
@ -1426,11 +1441,9 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
-- Target coordinate.
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)
-- Backup ROE and alarm state.
self.engage.roe=self:GetROE()
self.engage.alarmstate=self:GetAlarmstate()
@ -1442,6 +1455,10 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
-- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid
-- Set formation.
--TODO: make this input.
local Formation=ENUMS.Formation.Vehicle.Vee
-- Add waypoint after current.
self.engage.Waypoint=self:AddWaypoint(intercoord, nil, uid, Formation, true)
@ -1459,6 +1476,8 @@ function ARMYGROUP:_UpdateEngageTarget()
-- Get current position vector.
local vec3=self.engage.Target:GetVec3()
if vec3 then
-- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
@ -1488,7 +1507,14 @@ function ARMYGROUP:_UpdateEngageTarget()
else
-- Target not alive any more == Disengage.
-- Could not get position of target (not alive any more?) ==> Disengage.
self:Disengage()
end
else
-- Target not alive any more ==> Disengage.
self:Disengage()
end
@ -1587,8 +1613,7 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation
-- Speed in knots.
Speed=Speed or self:GetSpeedCruise()
-- Formation
-- Formation.
if not Formation then
if self.formationPerma then
Formation = self.formationPerma

View File

@ -94,6 +94,7 @@
-- @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 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 #number NcarriersMin Min number of required carrier assets.
@ -383,6 +384,7 @@ _AUFTRAGSNR=0
-- @field #string ARMOREDGUARD On guard - with armored groups.
-- @field #string BARRAGE Barrage.
-- @field #string ARMORATTACK Armor attack.
-- @field #string CASENHANCED Enhanced CAS.
AUFTRAG.Type={
ANTISHIP="Anti Ship",
AWACS="AWACS",
@ -415,6 +417,7 @@ AUFTRAG.Type={
ARMOREDGUARD="Armored Guard",
BARRAGE="Barrage",
ARMORATTACK="Armor Attack",
CASENHANCED="CAS Enhanced",
}
--- Mission status of an assigned group.
@ -557,7 +560,7 @@ AUFTRAG.Category={
--- AUFTRAG class version.
-- @field #string version
AUFTRAG.version="0.8.1"
AUFTRAG.version="0.8.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -1186,6 +1189,45 @@ function AUFTRAG:NewCAS(ZoneCAS, Altitude, Speed, Coordinate, Heading, Leg, Targ
return mission
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.
-- @param #AUFTRAG self
-- @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.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 #number PickupRadius Radius around the pickup coordinate in meters. Default 100 m.
-- @return #AUFTRAG self
function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupCoordinate)
function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupCoordinate, PickupRadius)
local mission=AUFTRAG:New(AUFTRAG.Type.TROOPTRANSPORT)
@ -1512,11 +1555,13 @@ function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupC
mission.transportPickup=PickupCoordinate or mission:GetTargetCoordinate()
mission.transportDropoff=DropoffCoordinate
mission.transportPickupRadius=PickupRadius or 100
mission.missionTask=mission:GetMissionTaskforMissionType(AUFTRAG.Type.TROOPTRANSPORT)
-- Debug.
mission.transportPickup:MarkToAll("Pickup")
mission.transportDropoff:MarkToAll("Drop off")
--mission.transportPickup:MarkToAll("Pickup Transport")
--mission.transportDropoff:MarkToAll("Drop off")
-- TODO: what's the best ROE here?
mission.optionROE=ENUMS.ROE.ReturnFire
@ -1645,8 +1690,9 @@ end
-- @param Core.Zone#ZONE Zone The patrol zone.
-- @param #number Speed Speed in knots.
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
-- @param #string Formation Formation used during patrol.
-- @return #AUFTRAG self
function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude)
function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude, Formation)
local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE)
@ -1671,9 +1717,12 @@ function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude)
mission.DCStask=mission:GetDCSMissionTask()
mission.DCStask.params.formation=Formation
return mission
end
--- **[GROUND]** Create a ARMORATTACK mission. Armoured ground group(s) will go to the zone and attack.
-- @param #AUFTRAG self
-- @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()
-- 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.
if isNotOver and groupsDone then
@ -4959,6 +5008,26 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable)
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
-------------------------
@ -5147,6 +5216,8 @@ function AUFTRAG:GetMissionTaskforMissionType(MissionType)
mtask=ENUMS.MissionTask.CAS
elseif MissionType==AUFTRAG.Type.PATROLZONE then
mtask=ENUMS.MissionTask.CAS
elseif MissionType==AUFTRAG.Type.CASENHANCED then
mtask=ENUMS.MissionTask.CAS
elseif MissionType==AUFTRAG.Type.ESCORT then
mtask=ENUMS.MissionTask.ESCORT
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:
--
-- ### [CSAR - Combat Search & Rescue](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/OPS%20-%20CSAR)
@ -118,6 +126,7 @@
-- self.SRSModulation = radio.modulation.AM -- modulation
-- --
-- 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
--
@ -227,8 +236,9 @@ CSAR = {
-- @field #number frequency Frequency of the NDB.
-- @field #string player Player name if applicable.
-- @field Wrapper.Group#GROUP group Spawned group object.
-- @field #number timestamp Timestamp for approach process
-- @field #boolean alive Group is alive or dead/rescued
-- @field #number timestamp Timestamp for approach process.
-- @field #boolean alive Group is alive or dead/rescued.
-- @field #boolean wetfeet Group is spawned over (deep) water.
--- All slot / Limit settings
-- @type CSAR.AircraftType
@ -245,10 +255,11 @@ CSAR.AircraftType["Mi-24P"] = 8
CSAR.AircraftType["Mi-24V"] = 8
CSAR.AircraftType["Bell-47"] = 2
CSAR.AircraftType["UH-60L"] = 10
CSAR.AircraftType["AH-64D_BLK_II"] = 2
--- CSAR class version.
-- @field #string version
CSAR.version="1.0.3"
CSAR.version="1.0.4d"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@ -391,6 +402,10 @@ function CSAR:New(Coalition, Template, Alias)
-- added 0.1.3
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
-- for this to work you need to de-sanitize your mission environment in <DCS root>\Scripts\MissionScripting.lua
-- needs SRS => 1.9.6 to work (works on the *server* side)
@ -502,8 +517,9 @@ end
-- @param #string Typename Typename of unit.
-- @param #number Frequency Frequency of the NDB in Hz
-- @param #string Playername Name of Player (if applicable)
-- @param #boolean Wetfeet Ejected over water
-- @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})
-- create new entry
@ -519,6 +535,7 @@ function CSAR:_CreateDownedPilotTrack(Group,Groupname,Side,OriginalUnit,Descript
DownedPilot.group = Group
DownedPilot.timestamp = 0
DownedPilot.alive = true
DownedPilot.wetfeet = Wetfeet or false
-- Add Pilot
local PilotTable = self.downedPilots
@ -568,17 +585,23 @@ end
-- @param #number country Country for template.
-- @param Core.Point#COORDINATE point Coordinate to spawn at.
-- @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 #string alias The alias name.
function CSAR:_SpawnPilotInField(country,point,frequency)
self:T({country,point,frequency})
function CSAR:_SpawnPilotInField(country,point,frequency,wetfeet)
self:T({country,point,frequency,tostring(wetfeet)})
local freq = frequency or 1000
local freq = freq / 1000 -- kHz
for i=1,10 do
math.random(i,10000)
end
if point:IsSurfaceTypeWater() then point.y = 0 end
if point:IsSurfaceTypeWater() or wetfeet then
point.y = 0
end
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 coalition = self.coalition
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})
local template = self.template
local wetfeet = false
local surface = _point:GetSurfaceType()
if surface == land.SurfaceType.WATER then
wetfeet = true
end
if not _freq then
_freq = self:_GenerateADFFrequency()
if not _freq then _freq = 333000 end --noob catch
end
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq)
local _spawnedGroup, _alias = self:_SpawnPilotInField(_country,_point,_freq,wetfeet)
local _typeName = _typeName or "Pilot"
@ -688,7 +717,7 @@ function CSAR:_AddCsar(_coalition , _country, _point, _typeName, _unitName, _pla
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.
@ -936,8 +965,18 @@ function CSAR:_EventHandler(EventData)
return
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.
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()
self:_AddCsar(_coalition, _unit:GetCountry(), _unit:GetCoordinate() , _unit:GetTypeName(), _unit:GetName(), _event.IniPlayerName, _freq, false, "none")
return true
@ -1294,7 +1333,8 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG
_time = self.landedStatus[_lookupKeyHeli] - 10
self.landedStatus[_lookupKeyHeli] = _time
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
self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true)
return true
@ -1999,6 +2039,9 @@ function CSAR:onafterStart(From, Event, To)
self.allheligroupset = SET_GROUP:New():FilterCoalitions(self.coalitiontxt):FilterCategoryHelicopter():FilterStart()
end
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)
return self
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.
--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
["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.
-- @field #string version
CTLD.version="1.0.9"
CTLD.version="1.0.10"
--- Instantiate a new CTLD.
-- @param #CTLD self
@ -2095,11 +2096,18 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist, _ignoreweight)
-- cycle
local index = 0
local found = {}
local loadedmass = self:_GetUnitCargoMass(_unit)
local unittype = _unit:GetTypeName()
local capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitCapabilities
local maxmass = capabilities.cargoweightlimit
local maxloadable = maxmass - loadedmass
local loadedmass = 0
local unittype = "none"
local capabilities = {}
local maxmass = 2000
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)
for _,_cargoobject in pairs (existingcrates) do
local cargo = _cargoobject -- #CTLD_CARGO
@ -2256,6 +2264,7 @@ end
-- @return #number mass in kgs
function CTLD:_GetUnitCargoMass(Unit)
self:T(self.lid .. " _GetUnitCargoMass")
if not Unit then return 0 end
local unitname = Unit:GetName()
local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo
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.
--
-- 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.
-- 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.
-- @field #string version
CHIEF.version="0.1.0"
CHIEF.version="0.1.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -1731,7 +1731,7 @@ function CHIEF:CheckOpsZoneQueue()
-- Has a patrol mission?
local hasMissionPatrol=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ONGUARD) or stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARMOREDGUARD)
-- 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?
local hasMissionARTY=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.ARTY)
@ -1775,12 +1775,13 @@ function CHIEF:CheckOpsZoneQueue()
-- 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.
self:T(self.lid..string.format("Zone is NOT empty ==> Recruit CAS assets=%s", tostring(recruited)))
end
if not hasMissionARTY then
-- Debug message.
@ -1811,7 +1812,7 @@ function CHIEF:CheckOpsZoneQueue()
local hasMissionPATROL=stratzone.opszone:_FindMissions(self.coalition,AUFTRAG.Type.PATROLZONE)
-- 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)
if ownercoalition==self.coalition and stratzone.opszone:IsEmpty() and hasMissionCAS then
@ -2208,7 +2209,7 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
end
-- 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
@ -2260,12 +2261,48 @@ function CHIEF:RecruitAssetsForZone(StratZone, MissionType, NassetsMin, NassetsM
-- Attach mission to ops zone.
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
else
LEGION.UnRecruitAssets(assets)
return false
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
-- Create Patrol zone mission.

View File

@ -32,6 +32,7 @@
-- @field #number skill Skill of cohort members.
-- @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 Nkilled Number of destroyed asset groups.
-- @field #number engageRange Mission range in meters.
-- @field #string attribute Generalized attribute of the cohort template group.
-- @field #table tacanChannel List of TACAN channels available to the cohort.
@ -67,6 +68,7 @@ COHORT = {
skill = nil,
legion = nil,
Ngroups = nil,
Ngroups = 0,
engageRange = nil,
tacanChannel = {},
weightAsset = 99999,
@ -275,12 +277,10 @@ end
--- Set number of units in groups.
-- @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
function COHORT:SetGrouping(nunits)
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
end
@ -919,20 +919,6 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
-- ARMY/NAVYGROUP combat ready?
---
if flightgroup:IsRearming() or flightgroup:IsRetreating() or flightgroup:IsReturning() then
combatready=false
end
end
-- Check transport/cargo for combat readyness!
if flightgroup:IsLoading() or flightgroup:IsTransporting() or flightgroup:IsUnloading() or flightgroup:IsPickingup() or flightgroup:IsCarrier() then
combatready=false
end
if flightgroup:IsCargo() or flightgroup:IsBoarding() or flightgroup:IsAwaitingLift() then
combatready=false
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
@ -949,6 +935,22 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
combatready=false
end
-- Not ready when rearming, retreating or returning!
if flightgroup:IsRearming() or flightgroup:IsRetreating() or flightgroup:IsReturning() then
combatready=false
end
end
-- 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
combatready=false
end
-- Not ready when currently acting as ops transport cargo.
if flightgroup:IsCargo() or flightgroup:IsBoarding() or flightgroup:IsAwaitingLift() then
combatready=false
end
-- This asset is "combatready".
if combatready then
self:T(self.lid.."Adding SPAWNED asset to ANOTHER mission as it is COMBATREADY")

View File

@ -132,7 +132,7 @@ COMMANDER = {
--- COMMANDER class version.
-- @field #string version
COMMANDER.version="0.1.0"
COMMANDER.version="0.1.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -1190,7 +1190,7 @@ function COMMANDER:RecruitAssetsForMission(Mission)
local Payloads=Mission.payloads
-- 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
end
@ -1292,6 +1292,7 @@ function COMMANDER:CheckTransportQueue()
-- Weight of the heaviest cargo group. Necessary condition that this fits into on carrier unit!
local weightGroup=0
local TotalWeight=0
-- Calculate the max weight so we know which cohorts can provide carriers.
if #cargoOpsGroups>0 then
@ -1301,13 +1302,14 @@ function COMMANDER:CheckTransportQueue()
if weight>weightGroup then
weightGroup=weight
end
TotalWeight=TotalWeight+weight
end
end
if weightGroup>0 then
-- Recruite assets from legions.
local recruited, assets, legions=self:RecruitAssetsForTransport(transport, weightGroup)
local recruited, assets, legions=self:RecruitAssetsForTransport(transport, weightGroup, TotalWeight)
if recruited then
@ -1344,10 +1346,12 @@ end
--- Recruit assets for a given OPS transport.
-- @param #COMMANDER self
-- @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 #table Recruited assets.
-- @return #table Legions that have recruited assets.
function COMMANDER:RecruitAssetsForTransport(Transport, CargoWeight)
function COMMANDER:RecruitAssetsForTransport(Transport, CargoWeight, TotalWeight)
if CargoWeight==0 then
-- No cargo groups!
@ -1381,7 +1385,7 @@ function COMMANDER:RecruitAssetsForTransport(Transport, CargoWeight)
local NreqMin,NreqMax=Transport:GetRequiredCarriers()
-- 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
end

View File

@ -168,7 +168,7 @@ FLIGHTGROUP.Attribute = {
--- FLIGHTGROUP class version.
-- @field #string version
FLIGHTGROUP.version="0.7.0"
FLIGHTGROUP.version="0.7.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -730,60 +730,6 @@ end
-- 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.
-- @param #FLIGHTGROUP self
function FLIGHTGROUP:Status()
@ -846,6 +792,9 @@ function FLIGHTGROUP:Status()
end
end
else
-- Check damage.
self:_CheckDamage()
end
---

File diff suppressed because it is too large Load Diff

View File

@ -45,13 +45,14 @@ LEGION = {
--- LEGION class version.
-- @field #string version
LEGION.version="0.2.0"
LEGION.version="0.2.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Create FLEED class.
-- DONE: Aircraft will not start hot on Alert5.
-- DONE: OPS transport.
-- DONE: Make general so it can be inherited by AIRWING and BRIGADE classes.
@ -82,6 +83,10 @@ function LEGION:New(WarehouseName, LegionName)
-- TODO: What?
self:SetMarker(false)
-- Dead and crash events are handled via opsgroups.
self:UnHandleEvent(EVENTS.Crash)
self:UnHandleEvent(EVENTS.Dead)
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "MissionRequest", "*") -- Add a (mission) request to the warehouse.
@ -530,6 +535,7 @@ function LEGION:CheckMissionQueue()
local Transport=nil
if mission.NcarriersMin then
local Legions=mission.transportLegions or {self}
TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone)
end
@ -744,6 +750,10 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
asset.missionTask=Mission.missionTask
end
if Mission.type==AUFTRAG.Type.ALERT5 then
asset.takeoffType=COORDINATE.WaypointType.TakeOffParking
end
end
-- Special for reloading brigade units
@ -882,7 +892,18 @@ function LEGION:onafterTransportCancel(From, Event, To, Transport)
local cargos=Transport:GetCargoOpsGroups(false)
for _,_cargo in pairs(cargos) do
local cargo=_cargo --Ops.OpsGroup#OPSGROUP
-- Remover my lift.
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
-- Remove asset from mission.
@ -993,7 +1014,7 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
-- Debug text.
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.
local cohort=self:_GetCohort(asset.assignment)
@ -1010,7 +1031,7 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
local nunits=#asset.template.units
-- 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)
-- Adjust number of elements in the group.
@ -1019,6 +1040,10 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
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.
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 i>nunits then
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
-- Remove units if original template contains more than in grouping.
if cohort.ngrouping<nunits and i>nunits then
unit=nil
if i>cohort.ngrouping then
template.units[i]=nil
end
end
-- Set number of units.
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
-- Set takeoff type.
@ -1061,6 +1099,11 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
-- 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.
self:LegionAssetReturned(cohort, asset)
@ -1078,7 +1121,7 @@ end
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned.
function LEGION:onafterLegionAssetReturned(From, Event, To, Cohort, Asset)
-- 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.
if Asset.flightgroup and not Asset.flightgroup:IsStopped() then
@ -1809,7 +1852,7 @@ function LEGION:RecruitAssetsForMission(Mission)
end
-- 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
end
@ -1826,17 +1869,20 @@ function LEGION:RecruitAssetsForTransport(Transport)
local cargoOpsGroups=Transport:GetCargoOpsGroups(false)
local weightGroup=0
local TotalWeight=nil
-- At least one group should be spawned.
if #cargoOpsGroups>0 then
-- Calculate the max weight so we know which cohorts can provide carriers.
TotalWeight=0
for _,_opsgroup in pairs(cargoOpsGroups) do
local opsgroup=_opsgroup --Ops.OpsGroup#OPSGROUP
local weight=opsgroup:GetWeightTotal()
if weight>weightGroup then
weightGroup=weight
end
TotalWeight=TotalWeight+weight
end
else
-- No cargo groups!
@ -1854,7 +1900,7 @@ function LEGION:RecruitAssetsForTransport(Transport)
-- 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
end
@ -1915,12 +1961,13 @@ end
-- @param #number RangeMax Max range in meters.
-- @param #number RefuelSystem Refuelsystem.
-- @param #number CargoWeight Cargo weight for recruiting transport carriers.
-- @param #number TotalWeight Total cargo weight in kg.
-- @param #table Categories Group categories.
-- @param #table Attributes Group attributes. See `GROUP.Attribute.`
-- @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 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.
local Assets={}
@ -2055,10 +2102,30 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
---
-- Add assets to mission.
local cargobay=0
for i=1,Nassets do
local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
asset.isReserved=true
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
-- Return payloads of not needed assets.
@ -2153,7 +2220,7 @@ function LEGION:AssignAssetsForEscort(Cohorts, Assets, NescortMin, NescortMax)
end
-- 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
Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets, ecategory=asset.category, TargetTypes=TargetTypes}
@ -2264,13 +2331,14 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
end
-- 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
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
CargoLegions[asset.legion.alias]=asset.legion
if CargoWeight==nil or asset.weight>CargoWeight then
CargoWeight=asset.weight
end
TotalWeight=TotalWeight+asset.weight
end
-- Target is the deploy zone.
@ -2278,7 +2346,7 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
-- Recruit assets and legions.
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
@ -2397,11 +2465,33 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
score=score-distance
-- Intercepts need to be carried out quickly. We prefer spawned assets.
--if MissionType==AUFTRAG.Type.INTERCEPT then
if asset.spawned then
if asset.spawned and asset.flightgroup and asset.flightgroup:IsAlive() then
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
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.
if MissionType==AUFTRAG.Type.OPSTRANSPORT then

View File

@ -90,7 +90,7 @@ NAVYGROUP = {
--- NavyGroup version.
-- @field #string version
NAVYGROUP.version="0.7.0"
NAVYGROUP.version="0.7.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -761,6 +761,12 @@ function NAVYGROUP:Status(From, Event, To)
-- Check into wind queue.
self:_CheckTurnsIntoWind()
-- Check ammo status.
self:_CheckAmmoStatus()
-- Check damage of elements and group.
self:_CheckDamage()
-- Check if group got stuck.
self:_CheckStuck()
@ -775,6 +781,9 @@ function NAVYGROUP:Status(From, Event, To)
end
end
else
-- Check damage of elements and group.
self:_CheckDamage()
end
-- Group exists but can also be inactive.
@ -1431,6 +1440,8 @@ function NAVYGROUP:_UpdateEngageTarget()
-- Get current position vector.
local vec3=self.engage.Target:GetVec3()
if vec3 then
-- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
@ -1460,7 +1471,14 @@ function NAVYGROUP:_UpdateEngageTarget()
else
-- Target not alive any more == Disengage.
-- Could not get position of target (not alive any more?) ==> Disengage.
self:Disengage()
end
else
-- Target not alive any more ==> Disengage.
self:Disengage()
end

View File

@ -466,7 +466,7 @@ OPSGROUP.CargoStatus={
--- OpsGroup version.
-- @field #string version
OPSGROUP.version="0.7.5"
OPSGROUP.version="0.7.6"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -542,6 +542,9 @@ function OPSGROUP:New(group)
end
-- Set gen attribute.
self.attribute=self.group:GetAttribute()
local units=self.group:GetUnits()
if units then
@ -864,6 +867,12 @@ function OPSGROUP:GetLifePoints(Element)
return life, life0
end
--- Get generalized attribute.
-- @param #OPSGROUP self
-- @return #string Generalized attribute.
function OPSGROUP:GetAttribute()
return self.attribute
end
--- Set verbosity level.
-- @param #OPSGROUP self
@ -879,6 +888,7 @@ end
-- @param Ops.Legion#LEGION Legion The Legion.
-- @return #OPSGROUP self
function OPSGROUP:_SetLegion(Legion)
self:T2(self.lid..string.format("Adding opsgroup to legion %s", Legion.alias))
self.legion=Legion
return self
end
@ -1609,13 +1619,6 @@ function OPSGROUP:Despawn(Delay, NoEventRemoveUnit)
self.scheduleIDDespawn=self:ScheduleOnce(Delay, OPSGROUP.Despawn, self, 0, NoEventRemoveUnit)
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.
self:T(self.lid..string.format("Despawning Group!"))
@ -1647,6 +1650,30 @@ function OPSGROUP:Despawn(Delay, NoEventRemoveUnit)
return self
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.
-- @param #OPSGROUP self
-- @param #string UnitName Name of the unit which should be destroyed.
@ -1671,6 +1698,9 @@ function OPSGROUP:DestroyUnit(UnitName, Delay)
self:CreateEventDead(EventTime, unit)
end
-- Despawn unit.
unit:destroy()
end
end
@ -2618,6 +2648,17 @@ function OPSGROUP:GetWaypointCurrent()
return self.waypoints[self.currentwp]
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.
-- @param #OPSGROUP self
-- @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")
-- Speed and altitude.
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 Speed=Task.dcstask.params.speed and UTILS.MpsToKnots(Task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
local Altitude=Task.dcstask.params.altitude and UTILS.MetersToFeet(Task.dcstask.params.altitude) or nil
local currUID=self:GetWaypointCurrent().uid
@ -3585,7 +3625,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
if self.isFlightgroup then
wp=FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
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
wp=NAVYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
end
@ -3612,7 +3652,6 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- Speed and altitude.
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
--Coordinate:MarkToAll("Recon Waypoint Execute")
@ -3673,7 +3712,6 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- BARRAGE is special!
if Task.dcstask.id==AUFTRAG.SpecialTask.BARRAGE then
env.info("FF Barrage")
local vec2=self:GetVec2()
local param=Task.dcstask.params
local heading=param.heading or math.random(1, 360)
@ -4230,7 +4268,7 @@ function OPSGROUP:onafterMissionStart(From, Event, To, Mission)
-- IMMOBILE Group
---
env.info("FF Immobile GROUP")
--env.info("FF Immobile GROUP")
-- Add waypoint task. UpdateRoute is called inside.
local Clock=Mission.Tpush and UTILS.SecondsToClock(Mission.Tpush) or 5
@ -4581,12 +4619,21 @@ function OPSGROUP:RouteToMission(mission, delay)
-- Refresh DCS task with the known controllable.
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.
for _,_group in pairs(mission.transportGroupSet.Set) do
local group=_group --Wrapper.Group#GROUP
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)
end
@ -4901,7 +4948,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
local Coordinate=zone:GetRandomCoordinate(nil, nil, surfacetypes)
-- 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 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
wp=FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
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
wp=NAVYGROUP.AddWaypoint(self, Coordinate, Speed, currUID, Altitude)
end
@ -4938,8 +4985,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
local Coordinate=zone:GetRandomCoordinate()
-- Speed and altitude.
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 Speed=task.dcstask.params.speed and UTILS.MpsToKnots(task.dcstask.params.speed) or UTILS.KmphToKnots(self.speedCruise)
local Altitude=task.dcstask.params.altitude and UTILS.MetersToFeet(task.dcstask.params.altitude) or nil
-- Debug.
@ -5759,6 +5805,38 @@ function OPSGROUP:onafterElementInUtero(From, Event, To, Element)
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.
-- @param #OPSGROUP self
-- @param #string From From state.
@ -5793,7 +5871,7 @@ end
function OPSGROUP:onafterElementDead(From, Event, To, Element)
-- 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.
self:_UpdateStatus(Element, OPSGROUP.ElementStatus.DEAD)
@ -5890,6 +5968,80 @@ function OPSGROUP:onafterRespawn(From, Event, To, Template)
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.
-- @param #OPSGROUP self
-- @param #number Delay Delay in seconds before respawn happens. Default 0.
@ -5905,58 +6057,60 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
-- Debug message.
self:T2(self.lid.."FF _Respawn")
-- Given template or get old.
-- Given template or get copy of old.
Template=Template or self:_GetTemplate(true)
-- Number of destroyed units.
self.Ndestroyed=0
-- Check if group is currently alive.
if self:IsAlive() then
---
-- Group is ALIVE
---
--[[
-- Get units.
local units=self.group:GetUnits()
-- Loop over template units.
for UnitID, Unit in pairs(Template.units) do
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
if unit:GetName()==Unit.name then
local vec3=unit:GetVec3()
local heading=unit:GetHeading()
Unit.x=vec3.x
Unit.y=vec3.z
Unit.alt=vec3.y
Unit.heading=math.rad(heading)
Unit.psi=-Unit.heading
end
end
end
]]
-- Template units.
local units=Template.units
for i=#units,1,-1 do
local unit=units[i]
-- Get the element.
local element=self:GetElementByName(unit.name)
if element and element.status~=OPSGROUP.ElementStatus.DEAD then
if not Reset then
-- Parking ID.
unit.parking=element.parking and element.parking.TerminalID or unit.parking
unit.parking_id=nil
-- Get current position vector.
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
-- Element is dead. Remove from template.
table.remove(units, i)
self.Ndestroyed=self.Ndestroyed+1
end
end
@ -5967,7 +6121,7 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
else
---
-- Group is DESPAWNED
-- Group is NOT ALIVE
---
-- Ensure elements in utero.
@ -5998,7 +6152,6 @@ function OPSGROUP:_Respawn(Delay, Template, Reset)
self.groupinitialized=false
self.Ndestroyed=0
self.wpcounter=1
self.currentwp=1
@ -6074,6 +6227,19 @@ function OPSGROUP:onbeforeDead(From, Event, To)
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.
-- @param #OPSGROUP self
-- @param #string From From state.
@ -6131,12 +6297,31 @@ function OPSGROUP:onafterDead(From, Event, To)
-- All elements were destroyed ==> Asset group is gone.
self.cohort:DelGroup(self.groupname)
end
if self.legion then
--self.legion:Get
--self.legion:AssetDead()
end
else
-- Not all assets were destroyed (despawn) ==> Add asset back to legion?
end
-- Stop in a sec.
--self:__Stop(-5)
if self.legion then
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
--- On before "Stop" event.
@ -6179,6 +6364,11 @@ function OPSGROUP:onafterStop(From, Event, To)
self.currbase=nil
end
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
self:MissionCancel(mission)
end
-- Stop check timers.
self.timerCheckZone:Stop()
self.timerQueueUpdate:Stop()
@ -7993,6 +8183,12 @@ end
-- @param #OPSGROUP OpsGroupCargo Cargo OPSGROUP that was unloaded from a carrier.
function OPSGROUP:onafterUnloaded(From, Event, To, OpsGroupCargo)
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
@ -8530,18 +8726,24 @@ function OPSGROUP:_CheckGroupDone(delay)
return
end
-- Group is returning
-- Group is returning.
if self:IsReturning() then
self:T(self.lid.."Returning! Group NOT done...")
return
end
-- Group is returning
-- Group is rearming.
if self:IsRearming() then
self:T(self.lid.."Rearming! Group NOT done...")
return
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.
if self:IsWaiting() then
-- 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 #string statusCommander Staus of the COMMANDER.
-- @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.
--
-- @extends Core.Fsm#FSM
@ -1305,7 +1307,7 @@ function OPSTRANSPORT:GetNcarrier()
return self.Ncarrier
end
--- Add asset to transport.
--- Add carrier asset to transport.
-- @param #OPSTRANSPORT self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be added.
-- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport zone combo.
@ -1323,7 +1325,7 @@ function OPSTRANSPORT:AddAsset(Asset, TransportZoneCombo)
return self
end
--- Delete asset from mission.
--- Delete carrier asset from transport.
-- @param #OPSTRANSPORT self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be removed.
-- @return #OPSTRANSPORT self
@ -1736,12 +1738,12 @@ function OPSTRANSPORT:onafterDeadCarrierGroup(From, Event, To, OpsGroup)
-- Increase dead counter.
self.NcarrierDead=self.NcarrierDead+1
-- Remove group from carrier list/table.
self:_DelCarrier(OpsGroup)
if #self.carriers==0 then
self:DeadCarrierAll()
end
-- Remove group from carrier list/table.
self:_DelCarrier(OpsGroup)
end
--- On after "DeadCarrierAll" event.
@ -1750,7 +1752,19 @@ end
-- @param #string Event Event.
-- @param #string To To state.
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.
self:_CheckDelivered()
@ -1759,6 +1773,9 @@ function OPSTRANSPORT:onafterDeadCarrierAll(From, Event, To)
if not self:IsDelivered() then
self:Planned()
end
end
end
--- 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}
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.
-- @param DCS#Vec3 a 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}
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.
-- @param DCS#Vec3 a 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
--- 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.
-- Returned values for the current maps are:
--
@ -1751,6 +1747,11 @@ function UTILS.IsLoadingDoorOpen( unit_name )
ret_val = true
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
BASE:T(unit_name .. " all doors are closed")
end

View File

@ -385,6 +385,16 @@ AIRBASE.TheChannel = {
-- * AIRBASE.Syria.Beirut_Rafic_Hariri
-- * AIRBASE.Syria.An_Nasiriyah
-- * 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
AIRBASE.Syria={
@ -440,10 +450,18 @@ AIRBASE.Syria={
["Beirut_Rafic_Hariri"]="Beirut-Rafic Hariri",
["An_Nasiriyah"]="An Nasiriyah",
["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:
--
-- * AIRBASE.MarianaIslands.Rota_Intl

View File

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

View File

@ -152,7 +152,7 @@ _MARKERID=0
--- Marker class version.
-- @field #string version
MARKER.version="0.1.0"
MARKER.version="0.1.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@ -177,7 +177,7 @@ function MARKER:New(Coordinate, Text)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #MARKER
self.coordinate=Coordinate
self.coordinate=UTILS.DeepCopy(Coordinate)
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.
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
-- Number of soldiers according to DCS function