* #TIMER
* Added `StartIf()`

* OPS

request

* AUFTRAG CAP

- Fixed mission speed (unit conversion)

* ZONE

Co-authored-by: Frank <frank@inter-zone.de>
This commit is contained in:
Thomas
2023-01-02 13:56:55 +01:00
committed by GitHub
parent 8ed85795c6
commit 4373ee8d13
9 changed files with 472 additions and 210 deletions

View File

@@ -1826,8 +1826,11 @@ function ARMYGROUP:_UpdateEngageTarget()
-- Distance to last known position of target.
local dist=UTILS.VecDist3D(vec3, self.engage.Coordinate:GetVec3())
-- Check line of sight to target.
local los=self:HasLoS(vec3)
-- Check if target moved more than 100 meters or we do not have line of sight.
if dist>100 or not self:HasLoS(self.engage.Target:GetCoordinate()) then
if dist>100 or los==false then
--env.info("FF Update Engage Target Moved "..self.engage.Target:GetName())

View File

@@ -7592,7 +7592,7 @@ function AUFTRAG:NewCAP(ZoneCAP, Altitude, Speed, Coordinate, Heading, Leg, Targ
mission.missionTask=ENUMS.MissionTask.CAP
mission.optionROE=ENUMS.ROE.OpenFire
mission.optionROT=ENUMS.ROT.EvadeFire
mission.missionSpeed = UTILS.KnotsToAltKIAS(Speed or 350,Altitude)
mission.missionSpeed = UTILS.KnotsToKmph(UTILS.KnotsToAltKIAS(Speed or 350, Altitude))
mission.categories={AUFTRAG.Category.AIRCRAFT}
@@ -11749,16 +11749,73 @@ function AUFTRAG:_SetLogID()
end
--- Update DCS task.
--- Get request ID from legion this mission requested assets from
-- @param #AUFTRAG self
-- @return #AUFTRAG self
function AUFTRAG:_UpdateTask()
-- @param Ops.Legion#LEGION Legion The legion from which to get the request ID.
-- @return #number Request ID (if any).
function AUFTRAG:_GetRequestID(Legion)
local requestid=nil
local name=nil
if type(Legion)=="string" then
name=Legion
else
name=Legion.alias
end
if name then
requestid=self.requestID[name]
end
return nil
end
--- Get request from legion this mission requested assets from.
-- @param #AUFTRAG self
-- @param Ops.Legion#LEGION Legion The legion from which to get the request ID.
-- @return Functional.Warehouse#WAREHOUSE.PendingItem Request.
function AUFTRAG:_GetRequest(Legion)
local request=nil
local requestID=self:_GetRequestID(Legion)
if requestID then
request=Legion:GetRequestByID(requestID)
end
return request
end
--- Set request ID from legion this mission requested assets from
-- @param #AUFTRAG self
-- @param Ops.Legion#LEGION Legion The legion from which to get the request ID.
-- @param #number RequestID Request ID.
-- @return #AUFTRAG self
function AUFTRAG:_SetRequestID(Legion, RequestID)
local requestid=nil
local name=nil
if type(Legion)=="string" then
name=Legion
else
name=Legion.alias
end
if name then
if self.requestID[name] then
self:I(self.lid..string.format("WARNING: Mission already has a request ID=%d!", self.requestID[name]))
end
self.requestID[name]=RequestID
end
return self
end
--- Update mission F10 map marker.
-- @param #AUFTRAG self
-- @return #AUFTRAG self

View File

@@ -572,9 +572,9 @@ function BRIGADE:onafterStatus(From, Event, To)
self:I(self.lid..text)
end
-------------------
---------------------
-- Refuelling Info --
-------------------
---------------------
if self.verbose>=4 then
local text="Refuelling Zones:"
for i,_refuellingzone in pairs(self.refuellingZones) do
@@ -583,7 +583,20 @@ function BRIGADE:onafterStatus(From, Event, To)
text=text..string.format("\n* %s: Mission status=%s, suppliers=%d", refuellingzone.zone:GetName(), refuellingzone.mission:GetState(), refuellingzone.mission:CountOpsGroups())
end
self:I(self.lid..text)
end
end
----------------
-- Asset Info --
----------------
if self.verbose>=5 then
local text="Assets in stock:"
for i,_asset in pairs(self.stock) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
-- Info text.
text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned))
end
self:I(self.lid..text)
end
end

View File

@@ -839,6 +839,58 @@ function LEGION:onafterMissionAssign(From, Event, To, Mission, Legions)
end
--- Create a request and add it to the warehouse queue.
-- @param #LEGION self
-- @param Functional.Warehouse#WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested.
-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc.
-- @param #number nAsset Number of groups requested that match the asset specification.
-- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low.
-- @param #string Assignment A keyword or text that can later be used to identify this request and postprocess the assets.
function LEGION:_AddRequest(AssetDescriptor, AssetDescriptorValue, nAsset, Prio, Assignment)
-- Defaults.
nAsset=nAsset or 1
Prio=Prio or 50
-- Increase id.
self.queueid=self.queueid+1
-- Request queue table item.
local request={
uid=self.queueid,
prio=Prio,
warehouse=self,
assetdesc=AssetDescriptor,
assetdescval=AssetDescriptorValue,
nasset=nAsset,
transporttype=WAREHOUSE.TransportType.SELFPROPELLED,
ntransport=0,
assignment=tostring(Assignment),
airbase=self:GetAirbase(),
category=self:GetAirbaseCategory(),
ndelivered=0,
ntransporthome=0,
assets={},
toself=true,
} --Functional.Warehouse#WAREHOUSE.Queueitem
-- Add request to queue.
table.insert(self.queue, request)
local descval="assetlist"
if request.assetdesc==WAREHOUSE.Descriptor.ASSETLIST then
else
descval=tostring(request.assetdescval)
end
local text=string.format("Warehouse %s: New request from warehouse %s.\nDescriptor %s=%s, #assets=%s; Transport=%s, #transports=%s.",
self.alias, self.alias, request.assetdesc, descval, tostring(request.nasset), request.transporttype, tostring(request.ntransport))
self:_DebugMessage(text, 5)
end
--- On after "MissionRequest" event. Performs a self request to the warehouse for the mission assets. Sets mission status to REQUESTED.
-- @param #LEGION self
@@ -914,11 +966,8 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
if Mission.type==AUFTRAG.Type.RELOCATECOHORT then
cancel=true
-- Get request ID.
local requestID=currM.requestID[self.alias]
-- Get request.
local request=self:GetRequestByID(requestID)
local request=currM:_GetRequest(self)
if request then
self:T2(self.lid.."Removing group from cargoset")
@@ -999,7 +1048,8 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
local assignment=string.format("Mission-%d", Mission.auftragsnummer)
-- Add request to legion warehouse.
self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, assignment)
--self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, assignment)
self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, Mission.prio, assignment)
-- The queueid has been increased in the onafterAddRequest function. So we can simply use it here.
Mission.requestID[self.alias]=self.queueid
@@ -1091,7 +1141,8 @@ function LEGION:onafterTransportRequest(From, Event, To, OpsTransport)
local assignment=string.format("Transport-%d", OpsTransport.uid)
-- Add request to legion warehouse.
self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, AssetList, #AssetList, nil, nil, OpsTransport.prio, assignment)
--self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, AssetList, #AssetList, nil, nil, OpsTransport.prio, assignment)
self:_AddRequest(WAREHOUSE.Descriptor.ASSETLIST, AssetList, #AssetList, OpsTransport.prio, assignment)
-- The queueid has been increased in the onafterAddRequest function. So we can simply use it here.
OpsTransport.requestID[self.alias]=self.queueid
@@ -1198,8 +1249,9 @@ function LEGION:onafterMissionCancel(From, Event, To, Mission)
end
-- Remove queued request (if any).
if Mission.requestID[self.alias] then
self:_DeleteQueueItemByID(Mission.requestID[self.alias], self.queue)
local requestID=Mission:_GetRequestID(self)
if requestID then
self:_DeleteQueueItemByID(requestID, self.queue)
end
end

View File

@@ -1539,49 +1539,67 @@ function OPSGROUP:SetReturnOnOutOfAmmo()
return self
end
--- Check if an element of the group has line of sight to a coordinate.
-- @param #OPSGROUP self
-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS.
-- @param Core.Point#COORDINATE Coordinate The position to which we check the LoS. Can also be a DCS#Vec3.
-- @param #OPSGROUP.Element Element The (optinal) element. If not given, all elements are checked.
-- @param DCS#Vec3 OffsetElement Offset vector of the element.
-- @param DCS#Vec3 OffsetCoordinate Offset vector of the coordinate.
-- @return #boolean If `true`, there is line of sight to the specified coordinate.
function OPSGROUP:HasLoS(Coordinate, Element, OffsetElement, OffsetCoordinate)
-- Target vector.
local Vec3=Coordinate:GetVec3()
if Coordinate then
-- Optional offset.
if OffsetCoordinate then
Vec3=UTILS.VecAdd(Vec3, OffsetCoordinate)
end
--- Function to check LoS for an element of the group.
local function checklos(element)
local vec3=element.unit:GetVec3()
if OffsetElement then
vec3=UTILS.VecAdd(vec3, OffsetElement)
-- Target vector.
local Vec3={x=Coordinate.x, y=Coordinate.y, z=Coordinate.z} --Coordinate:GetVec3()
-- Optional offset.
if OffsetCoordinate then
Vec3=UTILS.VecAdd(Vec3, OffsetCoordinate)
end
local _los=land.isVisible(vec3, Vec3)
--self:I({los=_los, source=vec3, target=Vec3})
return _los
end
if Element then
local los=checklos(Element)
return los
else
for _,element in pairs(self.elements) do
-- Get LoS of this element.
local los=checklos(element)
if los then
return true
--- Function to check LoS for an element of the group.
local function checklos(vec3)
if vec3 then
if OffsetElement then
vec3=UTILS.VecAdd(vec3, OffsetElement)
end
local _los=land.isVisible(vec3, Vec3)
--self:I({los=_los, source=vec3, target=Vec3})
return _los
end
return nil
end
if Element then
-- Check los for the given element.
if Element.unit and Element.unit:IsAlive() then
local vec3=Element.unit:GetVec3()
local los=checklos(Element)
return los
end
else
-- Check if any element has los.
local gotit=false
for _,_element in pairs(self.elements) do
local element=_element --#OPSGROUP.Element
if element and element.unit and element.unit:IsAlive() then
gotit=true
local vec3=element.unit:GetVec3()
-- Get LoS of this element.
local los=checklos(vec3)
if los then
return true
end
end
end
if gotit then
return false
end
end
return false
end
return nil

View File

@@ -5,6 +5,7 @@
-- * Monitor if a zone is captured
-- * Monitor if an airbase is captured
-- * Define conditions under which zones are captured/held
-- * Supports circular and polygon zone shapes
--
-- ===
--
@@ -20,6 +21,7 @@
-- @field #string lid DCS log ID string.
-- @field #number verbose Verbosity of output.
-- @field Core.Zone#ZONE zone The zone.
-- @field Core.Zone#ZONE_RADIUS zoneCircular The circular zone.
-- @field Wrapper.Airbase#AIRBASE airbase The airbase that is monitored.
-- @field #string airbaseName Name of the airbase that is monitored.
-- @field #string zoneName Name of the zone.
@@ -60,9 +62,6 @@
--
-- An OPSZONE is a strategically important area.
--
-- **Restrictions**
--
-- * Since we are using a DCS routine that scans a zone for units or other objects present in the zone and this DCS routine is limited to cicular zones, only those can be used.
--
-- @field #OPSZONE
OPSZONE = {
@@ -84,9 +83,19 @@ OPSZONE = {
-- @field #string Type Type of mission
-- @field Ops.Auftrag#AUFTRAG Mission The actual attached mission
--- Type of zone we are dealing with.
-- @type OPSZONE.ZoneType
-- @field #string Circular Zone is circular.
-- @field #string Polygon Zone is a polygon.
OPSZONE.ZoneType={
Circular="Circular",
Polygon="Polygon",
}
--- OPSZONE class version.
-- @field #string version
OPSZONE.version="0.4.0"
OPSZONE.version="0.5.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -94,6 +103,7 @@ OPSZONE.version="0.4.0"
-- TODO: Pause/unpause evaluations.
-- TODO: Differentiate between ground attack and boming by air or arty.
-- DONE: Polygon zones.
-- DONE: Capture time, i.e. time how long a single coalition has to be inside the zone to capture it.
-- DONE: Capturing based on (total) threat level threshold. Unarmed units do not pose a threat and should not be able to hold a zone.
-- DONE: Can neutrals capture? No, since they are _neutral_!
@@ -125,7 +135,7 @@ function OPSZONE:New(Zone, CoalitionOwner)
if type(Zone)=="string" then
-- Convert string into a ZONE or ZONE_AIRBASE
local Name=Zone
Zone=ZONE:New(Name)
Zone=ZONE:FindByName(Name)
if not Zone then
local airbase=AIRBASE:FindByName(Name)
if airbase then
@@ -146,8 +156,17 @@ function OPSZONE:New(Zone, CoalitionOwner)
if Zone:IsInstanceOf("ZONE_AIRBASE") then
self.airbase=Zone._.ZoneAirbase
self.airbaseName=self.airbase:GetName()
self.zoneType=OPSZONE.ZoneType.Circular
self.zoneCircular=Zone
elseif Zone:IsInstanceOf("ZONE_RADIUS") then
-- Nothing to do.
self.zoneType=OPSZONE.ZoneType.Circular
self.zoneCircular=Zone
elseif Zone:IsInstanceOf("ZONE_POLYGON_BASE") then
-- Nothing to do.
self.zoneType=OPSZONE.ZoneType.Polygon
local zone=Zone --Core.Zone#ZONE_POLYGON
self.zoneCircular=zone:GetZoneRadius(nil, true)
else
self:E("ERROR: OPSZONE must be a SPHERICAL zone due to DCS restrictions!")
return nil
@@ -156,10 +175,10 @@ function OPSZONE:New(Zone, CoalitionOwner)
-- Set some string id for output to DCS.log file.
self.lid=string.format("OPSZONE %s | ", Zone:GetName())
-- Set some values.
-- Set some values.
self.zone=Zone
self.zoneName=Zone:GetName()
self.zoneRadius=Zone:GetRadius()
self.zoneRadius=self.zoneCircular:GetRadius()
self.Missions = {}
self.ScanUnitSet=SET_UNIT:New():FilterZones({Zone})
self.ScanGroupSet=SET_GROUP:New():FilterZones({Zone})
@@ -820,8 +839,6 @@ function OPSZONE:onafterEmpty(From, Event, To)
-- Debug info.
self:T(self.lid..string.format("Zone is empty EVENT"))
end
--- On after "Attacked" event.
@@ -1034,42 +1051,55 @@ function OPSZONE:Scan()
local tl=0
local unit=UNIT:Find(DCSUnit)
if unit then
-- Threat level of unit.
tl=unit:GetThreatLevel()
-- Inside zone.
local inzone=true
if self.zoneType==OPSZONE.ZoneType.Polygon then
-- Add unit to set.
self.ScanUnitSet:AddUnit(unit)
-- Check if unit is really inside the zone.
inzone=unit:IsInZone(self.zone)
-- Debug marker.
-- Debug: Had cases where a (red) unit was clearly not inside the zone but the scan did find it!
unit:GetCoordinate():MarkToAll(string.format("Unit %s inzone=%s", unit:GetName(), tostring(inzone)))
end
-- Debug: Had cases where a (red) unit was clearly not inside the zone but the scan did find it!
--local inzone=unit:IsInZone(self.zone)
--unit:GetCoordinate():MarkToAll(string.format("Unit %s inzone=%s", unit:GetName(), tostring(inzone)))
if inzone then
-- Threat level of unit.
tl=unit:GetThreatLevel()
-- Add unit to set.
self.ScanUnitSet:AddUnit(unit)
-- Get group of unit.
local group=unit:GetGroup()
-- Get group of unit.
local group=unit:GetGroup()
-- Add group to scanned set.
if group then
self.ScanGroupSet:AddGroup(group, true)
end
-- Increase counter.
if Coalition==coalition.side.RED then
Nred=Nred+1
Tred=Tred+tl
elseif Coalition==coalition.side.BLUE then
Nblu=Nblu+1
Tblu=Tblu+tl
elseif Coalition==coalition.side.NEUTRAL then
Nnut=Nnut+1
Tnut=Tnut+tl
end
-- Debug info.
if self.verbose>=4 then
self:I(self.lid..string.format("Found unit %s (coalition=%d)", DCSUnit:getName(), Coalition))
end
if group then
self.ScanGroupSet:AddGroup(group, true)
end
end
-- Increase counter.
if Coalition==coalition.side.RED then
Nred=Nred+1
Tred=Tred+tl
elseif Coalition==coalition.side.BLUE then
Nblu=Nblu+1
Tblu=Tblu+tl
elseif Coalition==coalition.side.NEUTRAL then
Nnut=Nnut+1
Tnut=Tnut+tl
end
-- Debug info.
if self.verbose>=4 then
self:I(self.lid..string.format("Found unit %s (coalition=%d)", DCSUnit:getName(), Coalition))
end
end
elseif ObjectCategory==Object.Category.STATIC and ZoneObject:isExist() then
@@ -1079,26 +1109,40 @@ function OPSZONE:Scan()
---
-- This is a DCS static object.
local DCSStatic=ZoneObject --DCS#Static
local DCSStatic=ZoneObject --DCS#StaticObject
-- Get coalition.
local Coalition=DCSStatic:getCoalition()
-- CAREFUL! Downed pilots break routine here without any error thrown.
--local unit=STATIC:Find(DCSStatic)
-- Increase counter.
if Coalition==coalition.side.RED then
Nred=Nred+1
elseif Coalition==coalition.side.BLUE then
Nblu=Nblu+1
elseif Coalition==coalition.side.NEUTRAL then
Nnut=Nnut+1
-- Inside zone.
local inzone=true
if self.zoneType==OPSZONE.ZoneType.Polygon then
local Vec3=DCSStatic:getPoint()
inzone=self.zone:IsVec3InZone(Vec3)
end
-- Debug info
if self.verbose>=4 then
self:I(self.lid..string.format("Found static %s (coalition=%d)", DCSStatic:getName(), Coalition))
if inzone then
-- Increase counter.
if Coalition==coalition.side.RED then
Nred=Nred+1
elseif Coalition==coalition.side.BLUE then
Nblu=Nblu+1
elseif Coalition==coalition.side.NEUTRAL then
Nnut=Nnut+1
end
-- Debug info
if self.verbose>=4 then
self:I(self.lid..string.format("Found static %s (coalition=%d)", DCSStatic:getName(), Coalition))
end
end
elseif ObjectCategory==Object.Category.SCENERY then