mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
OPS
This commit is contained in:
@@ -2607,6 +2607,15 @@ function AUFTRAG:SetRepeatOnSuccess(Nrepeat)
|
||||
return self
|
||||
end
|
||||
|
||||
--- **[LEGION, COMMANDER, CHIEF]** Set that mission assets get reinforced if their number drops below Nmin.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param #number Nreinforce Number of max asset groups used to reinforce.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetReinforce(Nreinforce)
|
||||
self.reinforce=Nreinforce
|
||||
return self
|
||||
end
|
||||
|
||||
--- **[LEGION, COMMANDER, CHIEF]** Define how many assets are required to do the job. Only used if the mission is handled by a **LEGION** (AIRWING, BRIGADE, ...) or higher level.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param #number NassetsMin Minimum number of asset groups. Default 1.
|
||||
@@ -2628,24 +2637,32 @@ end
|
||||
|
||||
--- **[LEGION, COMMANDER, CHIEF]** Get number of required assets.
|
||||
-- @param #AUFTRAG self
|
||||
-- @param Ops.Legion#Legion Legion (Optional) Only get the required assets for a specific legion. If required assets for this legion are not defined, the total number is returned.
|
||||
-- @return #number Min. number of required assets.
|
||||
-- @return #number Max. number of required assets.
|
||||
function AUFTRAG:GetRequiredAssets(Legion)
|
||||
|
||||
--local N=self.nassets
|
||||
|
||||
--if Legion and self.Nassets[Legion.alias] then
|
||||
-- N=self.Nassets[Legion.alias]
|
||||
--end
|
||||
function AUFTRAG:GetRequiredAssets()
|
||||
|
||||
local Nmin=self.NassetsMin
|
||||
local Nmax=self.NassetsMax
|
||||
|
||||
|
||||
if self.type==AUFTRAG.Type.RELOCATECOHORT then
|
||||
|
||||
-- Relocation gets all the assets.
|
||||
local cohort=self.DCStask.params.cohort --Ops.Cohort#COHORT
|
||||
Nmin=#cohort.assets
|
||||
Nmax=Nmin
|
||||
|
||||
else
|
||||
|
||||
-- Check if this is an reinforcement.
|
||||
if self:IsExecuting() and self.reinforce and self.reinforce>0 then
|
||||
local N=self:CountOpsGroups()
|
||||
if N<Nmin then
|
||||
Nmin=math.min(Nmin-N, self.reinforce)
|
||||
Nmax=Nmin
|
||||
self:T(self.lid..string.format("FF Executing Nmin=%d, N=%d, Nreinfoce=%d ==> Nmin=%d", self.NassetsMin, N, self.reinforce, Nmin))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Nmin, Nmax
|
||||
@@ -2905,6 +2922,7 @@ function AUFTRAG:AddTransportCarriers(Carriers)
|
||||
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- **[LEGION, COMMANDER, CHIEF]** Set required attribute(s) the assets must have.
|
||||
@@ -2912,10 +2930,8 @@ end
|
||||
-- @param #table Attributes Generalized attribute(s).
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetRequiredAttribute(Attributes)
|
||||
if Attributes and type(Attributes)~="table" then
|
||||
Attributes={Attributes}
|
||||
end
|
||||
self.attributes=Attributes
|
||||
self.attributes=UTILS.EnsureTable(Attributes, true)
|
||||
return self
|
||||
end
|
||||
|
||||
--- **[LEGION, COMMANDER, CHIEF]** Set required property or properties the assets must have.
|
||||
@@ -2924,10 +2940,8 @@ end
|
||||
-- @param #table Properties Property or table of properties.
|
||||
-- @return #AUFTRAG self
|
||||
function AUFTRAG:SetRequiredProperty(Properties)
|
||||
if Properties and type(Properties)~="table" then
|
||||
Properties={Properties}
|
||||
end
|
||||
self.properties=Properties
|
||||
self.properties=UTILS.EnsureTable(Properties, true)
|
||||
return self
|
||||
end
|
||||
|
||||
--- **[LEGION, COMMANDER, CHIEF]** Set number of required carrier groups if an OPSTRANSPORT assignment is required.
|
||||
@@ -3798,7 +3812,7 @@ function AUFTRAG:onafterStatus(From, Event, To)
|
||||
self:T(self.lid.."No targets left cancelling mission!")
|
||||
self:Cancel()
|
||||
|
||||
elseif self:IsExecuting() then
|
||||
elseif self:IsExecuting() and ((not self.reinforce) or self.reinforce==0) then
|
||||
|
||||
-- Had the case that mission was in state Executing but all assigned groups were dead.
|
||||
-- TODO: might need to loop over all assigned groups
|
||||
@@ -4327,6 +4341,12 @@ function AUFTRAG:CheckGroupsDone()
|
||||
self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] (PLANNED or QUEUED or REQUESTED). Mission NOT DONE!", self.status, self:GetState()))
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check if there is still reinforcement to be expected.
|
||||
if self:IsExecuting() and self.reinforce and self.reinforce>0 then
|
||||
self:T2(self.lid..string.format("CheckGroupsDone: Mission is still in state %s [FSM=%s] and reinfoce=%d. Mission NOT DONE!", self.status, self:GetState(), self.reinforce))
|
||||
return false
|
||||
end
|
||||
|
||||
-- It could be that all flights were destroyed on the way to the mission execution waypoint.
|
||||
-- TODO: would be better to check if everybody is dead by now.
|
||||
@@ -4486,7 +4506,7 @@ function AUFTRAG:onafterAssetDead(From, Event, To, Asset)
|
||||
self:T(self.lid..string.format("Asset %s dead! Number of ops groups remaining %d", tostring(Asset.spawngroupname), N))
|
||||
|
||||
-- All assets dead?
|
||||
if N==0 then
|
||||
if N==0 and (self.reinforce==nil or self.reinforce==0) then
|
||||
|
||||
if self:IsNotOver() then
|
||||
|
||||
|
||||
@@ -176,7 +176,11 @@ function COHORT:New(TemplateGroupName, Ngroups, CohortName)
|
||||
for i,_unit in pairs(units) do
|
||||
local unit=_unit --Wrapper.Unit#UNIT
|
||||
local desc=unit:GetDesc()
|
||||
self.weightAsset=self.weightAsset + (desc.massMax or 666)
|
||||
local mass=666
|
||||
if desc then
|
||||
mass=desc.massMax or desc.massEmpty
|
||||
end
|
||||
self.weightAsset=self.weightAsset + (mass or 666)
|
||||
if i==1 then
|
||||
self.cargobayLimit=unit:GetCargoBayFreeWeight()
|
||||
end
|
||||
|
||||
@@ -1671,25 +1671,51 @@ function COMMANDER:RecruitAssetsForMission(Mission)
|
||||
|
||||
-- Debug info.
|
||||
self:T2(self.lid..string.format("Recruiting assets for mission \"%s\" [%s]", Mission:GetName(), Mission:GetType()))
|
||||
|
||||
-- Cohorts.
|
||||
local Cohorts=self:_GetCohorts(Mission.specialLegions, Mission.specialCohorts, Mission.operation)
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Found %d cohort candidates for mission", #Cohorts))
|
||||
|
||||
-- Number of required assets.
|
||||
local NreqMin, NreqMax=Mission:GetRequiredAssets()
|
||||
|
||||
|
||||
-- Target position.
|
||||
local TargetVec2=Mission:GetTargetVec2()
|
||||
|
||||
-- Special payloads.
|
||||
local Payloads=Mission.payloads
|
||||
local Payloads=Mission.payloads
|
||||
|
||||
-- Largest cargo bay available of available carrier assets if mission assets need to be transported.
|
||||
local MaxWeight=nil
|
||||
|
||||
if Mission.NcarriersMin then
|
||||
|
||||
-- Get transport cohorts.
|
||||
local Cohorts=LEGION._GetCohorts(Mission.transportLegions or self.legions, Mission.transportCohorts)
|
||||
|
||||
-- Filter cohorts that can actually perform transport missions.
|
||||
local transportcohorts={}
|
||||
for _,_cohort in pairs(Cohorts) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
|
||||
-- Check if cohort can perform transport to target.
|
||||
--TODO: Option to filter transport carrier asset categories, attributes and/or properties.
|
||||
local can=LEGION._CohortCan(cohort, AUFTRAG.Type.OPSTRANSPORT, Categories, Attributes, Properties, nil, TargetVec2)
|
||||
|
||||
-- MaxWeight of cargo assets is limited by the largets available cargo bay. We don't want to select, e.g., tanks that cannot be transported by APCs or helos.
|
||||
if can and (MaxWeight==nil or cohort.cargobayLimit>MaxWeight) then
|
||||
MaxWeight=cohort.cargobayLimit
|
||||
end
|
||||
end
|
||||
|
||||
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight))
|
||||
end
|
||||
|
||||
-- Get cohorts.
|
||||
local Cohorts=LEGION._GetCohorts(Mission.specialLegions or self.legions, Mission.specialCohorts, Mission.operation, self.opsqueue)
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Found %d cohort candidates for mission", #Cohorts))
|
||||
|
||||
-- Recruite assets.
|
||||
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads,
|
||||
Mission.engageRange, Mission.refuelSystem, nil, nil, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
|
||||
Mission.engageRange, Mission.refuelSystem, nil, nil, MaxWeight, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
|
||||
|
||||
return recruited, assets, legions
|
||||
end
|
||||
|
||||
@@ -665,9 +665,24 @@ function LEGION:CheckMissionQueue()
|
||||
-- Look for first task that is not accomplished.
|
||||
for _,_mission in pairs(self.missionqueue) do
|
||||
local mission=_mission --Ops.Auftrag#AUFTRAG
|
||||
|
||||
-- Check if reinforcement is necessary.
|
||||
local reinforce=false
|
||||
if mission:IsExecuting() and mission.reinforce and mission.reinforce>0 then
|
||||
|
||||
local N=mission:CountOpsGroups()
|
||||
|
||||
local Nmin, Nmax=mission:GetRequiredAssets()
|
||||
|
||||
if N<Nmin then
|
||||
reinforce=true
|
||||
end
|
||||
end
|
||||
|
||||
mission:CountOpsGroups()
|
||||
|
||||
-- Firstly, check if mission is due?
|
||||
if mission:IsQueued(self) and mission:IsReadyToGo() and (mission.importance==nil or mission.importance<=vip) then
|
||||
if (mission:IsQueued(self) or reinforce) and mission:IsReadyToGo() and (mission.importance==nil or mission.importance<=vip) then
|
||||
|
||||
-- Recruit best assets for the job.
|
||||
local recruited, assets, legions=self:RecruitAssetsForMission(mission)
|
||||
@@ -693,8 +708,11 @@ function LEGION:CheckMissionQueue()
|
||||
-- Recruit carrier assets for transport.
|
||||
local Transport=nil
|
||||
if mission.NcarriersMin then
|
||||
|
||||
-- Transport legions.
|
||||
local Legions=mission.transportLegions or {self}
|
||||
|
||||
-- Assign carrier assets for transport.
|
||||
TransportAvail, Transport=self:AssignAssetsForTransport(Legions, assets, mission.NcarriersMin, mission.NcarriersMax, mission.transportDeployZone, mission.transportDisembarkZone)
|
||||
end
|
||||
|
||||
@@ -706,8 +724,10 @@ function LEGION:CheckMissionQueue()
|
||||
end
|
||||
|
||||
if EscortAvail and TransportAvail then
|
||||
|
||||
-- Got a mission.
|
||||
self:MissionRequest(mission)
|
||||
|
||||
return true
|
||||
else
|
||||
-- Recruited assets but no requested escort available. Unrecruit assets!
|
||||
@@ -2133,29 +2153,39 @@ function LEGION:RecruitAssetsForMission(Mission)
|
||||
|
||||
-- Payloads.
|
||||
local Payloads=Mission.payloads
|
||||
|
||||
-- Get special escort legions and/or cohorts.
|
||||
local Cohorts={}
|
||||
for _,_legion in pairs(Mission.specialLegions or {}) do
|
||||
local legion=_legion --Ops.Legion#LEGION
|
||||
for _,_cohort in pairs(legion.cohorts) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
table.insert(Cohorts, cohort)
|
||||
end
|
||||
end
|
||||
for _,_cohort in pairs(Mission.specialCohorts or {}) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
table.insert(Cohorts, cohort)
|
||||
end
|
||||
|
||||
-- No escort cohorts/legions given ==> take own cohorts.
|
||||
if #Cohorts==0 then
|
||||
Cohorts=self.cohorts
|
||||
end
|
||||
|
||||
-- Largest cargo bay available of available carrier assets if mission assets need to be transported.
|
||||
local MaxWeight=nil
|
||||
|
||||
if Mission.NcarriersMin then
|
||||
|
||||
-- Get transport cohorts.
|
||||
local Cohorts=LEGION._GetCohorts(Mission.transportLegions or {self}, Mission.transportCohorts or self.cohorts)
|
||||
|
||||
-- Filter cohorts that can actually perform transport missions.
|
||||
local transportcohorts={}
|
||||
for _,_cohort in pairs(Cohorts) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
|
||||
-- Check if cohort can perform transport to target.
|
||||
--TODO: Option to filter transport carrier asset categories, attributes and/or properties.
|
||||
local can=LEGION._CohortCan(cohort, AUFTRAG.Type.OPSTRANSPORT, Categories, Attributes, Properties, nil, TargetVec2)
|
||||
|
||||
-- MaxWeight of cargo assets is limited by the largets available cargo bay. We don't want to select, e.g., tanks that cannot be transported by APCs or helos.
|
||||
if can and (MaxWeight==nil or cohort.cargobayLimit>MaxWeight) then
|
||||
MaxWeight=cohort.cargobayLimit
|
||||
end
|
||||
end
|
||||
|
||||
self:T(self.lid..string.format("Largest cargo bay available=%.1f", MaxWeight))
|
||||
end
|
||||
|
||||
-- Get cohorts.
|
||||
local Cohorts=LEGION._GetCohorts(Mission.specialLegions or {self}, Mission.specialCohorts or self.cohorts, Operation, OpsQueue)
|
||||
|
||||
-- Recuit assets.
|
||||
local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, Mission.type, Mission.alert5MissionType, NreqMin, NreqMax, TargetVec2, Payloads,
|
||||
Mission.engageRange, Mission.refuelSystem, nil, nil, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
|
||||
Mission.engageRange, Mission.refuelSystem, nil, nil, MaxWeight, nil, Mission.attributes, Mission.properties, {Mission.engageWeaponType})
|
||||
|
||||
return recruited, assets, legions
|
||||
end
|
||||
@@ -2249,42 +2279,118 @@ function LEGION:RecruitAssetsForEscort(Mission, Assets)
|
||||
return true
|
||||
end
|
||||
|
||||
--- Get cohorts.
|
||||
-- @param #table Legions Special legions.
|
||||
-- @param #table Cohorts Special cohorts.
|
||||
-- @param Ops.Operation#OPERATION Operation Operation.
|
||||
-- @param #table OpsQueue Queue of operations.
|
||||
-- @return #table Cohorts.
|
||||
function LEGION._GetCohorts(Legions, Cohorts, Operation, OpsQueue)
|
||||
|
||||
OpsQueue=OpsQueue or {}
|
||||
|
||||
--- Function that check if a legion or cohort is part of an operation.
|
||||
local function CheckOperation(LegionOrCohort)
|
||||
-- No operations ==> no problem!
|
||||
if #OpsQueue==0 then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Cohort is not dedicated to a running(!) operation. We assume so.
|
||||
local isAvail=true
|
||||
|
||||
-- Only available...
|
||||
if Operation then
|
||||
isAvail=false
|
||||
end
|
||||
|
||||
for _,_operation in pairs(OpsQueue) do
|
||||
local operation=_operation --Ops.Operation#OPERATION
|
||||
|
||||
-- Legion is assigned to this operation.
|
||||
local isOps=operation:IsAssignedCohortOrLegion(LegionOrCohort)
|
||||
|
||||
if isOps and operation:IsRunning() then
|
||||
|
||||
-- Is dedicated.
|
||||
isAvail=false
|
||||
|
||||
if Operation==nil then
|
||||
-- No Operation given and this is dedicated to at least one operation.
|
||||
return false
|
||||
else
|
||||
if Operation.uid==operation.uid then
|
||||
-- Operation given and is part of it.
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return isAvail
|
||||
end
|
||||
|
||||
-- Chosen cohorts.
|
||||
local cohorts={}
|
||||
|
||||
-- Check if there are any special legions and/or cohorts.
|
||||
if (Legions and #Legions>0) or (Cohorts and #Cohorts>0) then
|
||||
|
||||
-- Add cohorts of special legions.
|
||||
for _,_legion in pairs(Legions or {}) do
|
||||
local legion=_legion --Ops.Legion#LEGION
|
||||
|
||||
-- Check that runway is operational.
|
||||
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
|
||||
|
||||
-- Legion has to be running.
|
||||
if legion:IsRunning() and Runway then
|
||||
|
||||
-- Add cohorts of legion.
|
||||
for _,_cohort in pairs(legion.cohorts) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
|
||||
if (CheckOperation(cohort.legion) or CheckOperation(cohort)) and not UTILS.IsInTable(cohorts, cohort, "name") then
|
||||
table.insert(cohorts, cohort)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Add special cohorts.
|
||||
for _,_cohort in pairs(Cohorts or {}) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
|
||||
if CheckOperation(cohort) and not UTILS.IsInTable(cohorts, cohort, "name") then
|
||||
table.insert(cohorts, cohort)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return cohorts
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Recruiting and Optimization Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
|
||||
-- @param #table Cohorts Cohorts included.
|
||||
-- @param #string MissionTypeRecruit Mission type for recruiting the cohort assets.
|
||||
-- @param #string MissionTypeOpt Mission type for which the assets are optimized. Default is the same as `MissionTypeRecruit`.
|
||||
-- @param #number NreqMin Minimum number of required assets.
|
||||
-- @param #number NreqMax Maximum number of required assets.
|
||||
-- @param DCS#Vec2 TargetVec2 Target position as 2D vector.
|
||||
-- @param #table Payloads Special payloads.
|
||||
-- @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 Ops.Cohort#COHORT Cohort The Cohort.
|
||||
-- @param #string MissionType Misson type(s).
|
||||
-- @param #table Categories Group categories.
|
||||
-- @param #table Attributes Group attributes. See `GROUP.Attribute.`
|
||||
-- @param #table Properties DCS attributes.
|
||||
-- @param #table WeaponTypes Bit of weapon types.
|
||||
-- @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, TotalWeight, Categories, Attributes, Properties, WeaponTypes)
|
||||
-- @param DCS#Vec2 TargetVec2 Target position.
|
||||
-- @param RangeMax Max range in meters.
|
||||
-- @param #number RefuelSystem Refueling system (boom or probe).
|
||||
-- @param #number CargoWeight Cargo weight [kg]. This checks the cargo bay of the cohort assets and ensures that it is large enough to carry the given cargo weight.
|
||||
-- @param #number MaxWeight Max weight [kg]. This checks whether the cohort asset group is not too heavy.
|
||||
-- @return #boolean Returns `true` if given cohort can meet all requirements.
|
||||
function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properties, WeaponTypes, TargetVec2, RangeMax, RefuelSystem, CargoWeight, MaxWeight)
|
||||
|
||||
-- The recruited assets.
|
||||
local Assets={}
|
||||
|
||||
-- Legions of recruited assets.
|
||||
local Legions={}
|
||||
|
||||
-- Set MissionTypeOpt to Recruit if nil.
|
||||
if MissionTypeOpt==nil then
|
||||
MissionTypeOpt=MissionTypeRecruit
|
||||
end
|
||||
|
||||
--- Function to check category.
|
||||
local function CheckCategory(_cohort)
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
@@ -2350,9 +2456,9 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- Loops over cohorts.
|
||||
for _,_cohort in pairs(Cohorts) do
|
||||
|
||||
--- Function to check range.
|
||||
local function CheckRange(_cohort)
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
|
||||
-- Distance to target.
|
||||
@@ -2362,50 +2468,175 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
|
||||
local Rmax=cohort:GetMissionRange(WeaponTypes)
|
||||
local InRange=(RangeMax and math.max(RangeMax, Rmax) or Rmax) >= TargetDistance
|
||||
|
||||
return InRange
|
||||
end
|
||||
|
||||
|
||||
--- Function to check weapon type.
|
||||
local function CheckRefueling(_cohort)
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
|
||||
-- Has the requested refuelsystem?
|
||||
local Refuel=RefuelSystem~=nil and (RefuelSystem==cohort.tankerSystem) or true
|
||||
--local Refuel=RefuelSystem~=nil and (RefuelSystem==cohort.tankerSystem) or true
|
||||
|
||||
-- STRANGE: Why did the above line did not give the same result?! Above Refuel is always true!
|
||||
local Refuel=true
|
||||
if RefuelSystem then
|
||||
if cohort.tankerSystem then
|
||||
Refuel=RefuelSystem==cohort.tankerSystem
|
||||
return RefuelSystem==cohort.tankerSystem
|
||||
else
|
||||
Refuel=false
|
||||
return false
|
||||
end
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to check cargo weight.
|
||||
local function CheckCargoWeight(_cohort)
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
if CargoWeight~=nil then
|
||||
return cohort.cargobayLimit>=CargoWeight
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
--- Function to check cargo weight.
|
||||
local function CheckMaxWeight(_cohort)
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
if MaxWeight~=nil then
|
||||
cohort:I(string.format("Cohort weight=%.1f | max weight=%.1f", cohort.weightAsset, MaxWeight))
|
||||
return cohort.weightAsset<=MaxWeight
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Is capable of the mission type?
|
||||
local can=AUFTRAG.CheckMissionCapability(MissionType, Cohort.missiontypes)
|
||||
|
||||
if can then
|
||||
can=CheckCategory(Cohort)
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of mission types", Cohort.name))
|
||||
return false
|
||||
end
|
||||
|
||||
if can then
|
||||
if MissionType==AUFTRAG.Type.RELOCATECOHORT then
|
||||
can=Cohort:IsRelocating()
|
||||
else
|
||||
can=Cohort:IsOnDuty()
|
||||
end
|
||||
|
||||
-- Is capable of the mission type?
|
||||
local Capable=AUFTRAG.CheckMissionCapability({MissionTypeRecruit}, cohort.missiontypes)
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of category", Cohort.name))
|
||||
BASE:I(Categories)
|
||||
BASE:I(Cohort.category)
|
||||
return false
|
||||
end
|
||||
|
||||
if can then
|
||||
can=CheckAttribute(Cohort)
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of readyiness", Cohort.name))
|
||||
return false
|
||||
end
|
||||
|
||||
if can then
|
||||
can=CheckProperty(Cohort)
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of attribute", Cohort.name))
|
||||
return false
|
||||
end
|
||||
|
||||
if can then
|
||||
can=CheckWeapon(Cohort)
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of property", Cohort.name))
|
||||
return false
|
||||
end
|
||||
|
||||
if can then
|
||||
can=CheckRange(Cohort)
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of weapon type", Cohort.name))
|
||||
return false
|
||||
end
|
||||
|
||||
if can then
|
||||
can=CheckRefueling(Cohort)
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of range", Cohort.name))
|
||||
return false
|
||||
end
|
||||
|
||||
if can then
|
||||
can=CheckCargoWeight(Cohort)
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of refueling system", Cohort.name))
|
||||
return false
|
||||
end
|
||||
|
||||
if can then
|
||||
can=CheckMaxWeight(Cohort)
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of cargo weight", Cohort.name))
|
||||
return false
|
||||
end
|
||||
|
||||
if can then
|
||||
return true
|
||||
else
|
||||
env.info(string.format("Cohort %s cannot because of max weight", Cohort.name))
|
||||
return false
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
|
||||
-- @param #table Cohorts Cohorts included.
|
||||
-- @param #string MissionTypeRecruit Mission type for recruiting the cohort assets.
|
||||
-- @param #string MissionTypeOpt Mission type for which the assets are optimized. Default is the same as `MissionTypeRecruit`.
|
||||
-- @param #number NreqMin Minimum number of required assets.
|
||||
-- @param #number NreqMax Maximum number of required assets.
|
||||
-- @param DCS#Vec2 TargetVec2 Target position as 2D vector.
|
||||
-- @param #table Payloads Special payloads.
|
||||
-- @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 #number MaxWeight Max weight [kg] of the asset group.
|
||||
-- @param #table Categories Group categories.
|
||||
-- @param #table Attributes Group attributes. See `GROUP.Attribute.`
|
||||
-- @param #table Properties DCS attributes.
|
||||
-- @param #table WeaponTypes Bit of weapon types.
|
||||
-- @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, TotalWeight, MaxWeight, Categories, Attributes, Properties, WeaponTypes)
|
||||
|
||||
-- The recruited assets.
|
||||
local Assets={}
|
||||
|
||||
-- Legions of recruited assets.
|
||||
local Legions={}
|
||||
|
||||
-- Set MissionTypeOpt to Recruit if nil.
|
||||
if MissionTypeOpt==nil then
|
||||
MissionTypeOpt=MissionTypeRecruit
|
||||
end
|
||||
|
||||
-- Can carry the cargo?
|
||||
local CanCarry=CargoWeight and cohort.cargobayLimit>=CargoWeight or true
|
||||
-- Loops over cohorts.
|
||||
for _,_cohort in pairs(Cohorts) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
|
||||
-- Right category.
|
||||
local RightCategory=CheckCategory(cohort)
|
||||
|
||||
-- Right attribute.
|
||||
local RightAttribute=CheckAttribute(cohort)
|
||||
|
||||
-- Right property (DCS attribute).
|
||||
local RightProperty=CheckProperty(cohort)
|
||||
|
||||
-- Right weapon type.
|
||||
local RightWeapon=CheckWeapon(cohort)
|
||||
|
||||
-- Cohort ready to execute mission.
|
||||
local Ready=cohort:IsOnDuty()
|
||||
if MissionTypeRecruit==AUFTRAG.Type.RELOCATECOHORT then
|
||||
Ready=cohort:IsRelocating()
|
||||
Capable=true
|
||||
end
|
||||
|
||||
-- Debug info.
|
||||
cohort:T(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, Category=%s, Attribute=%s, Property=%s, Weapon=%s",
|
||||
cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute), tostring(RightProperty), tostring(RightWeapon)))
|
||||
-- Check if cohort can do the mission.
|
||||
local can=LEGION._CohortCan(cohort, MissionTypeRecruit, Categories, Attributes, Properties, WeaponTypes, TargetVec2, RangeMax, RefuelSystem, CargoWeight, MaxWeight)
|
||||
|
||||
-- Check OnDuty, capable, in range and refueling type (if TANKER).
|
||||
if Ready and Capable and InRange and Refuel and CanCarry and RightCategory and RightAttribute and RightProperty and RightWeapon then
|
||||
if can then
|
||||
|
||||
-- Recruit assets from cohort.
|
||||
local assets, npayloads=cohort:RecruitAssets(MissionTypeRecruit, 999)
|
||||
@@ -2456,23 +2687,30 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt,
|
||||
-- Found enough assets
|
||||
---
|
||||
|
||||
-- Add assets to mission.
|
||||
-- Total cargo bay of all carrier assets.
|
||||
local cargobay=0
|
||||
|
||||
-- Add assets to mission.
|
||||
for i=1,Nassets do
|
||||
local asset=Assets[i] --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
|
||||
-- Asset is reserved and will not be picked for other missions.
|
||||
asset.isReserved=true
|
||||
|
||||
-- Add legion.
|
||||
Legions[asset.legion.alias]=asset.legion
|
||||
|
||||
-- Check if total cargo weight was given.
|
||||
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))
|
||||
|
||||
-- Sum up total cargo bay of all carrier assets.
|
||||
cargobay=cargobay + N*CargoWeight
|
||||
|
||||
-- Check if enough carrier assets were found to transport all cargo.
|
||||
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
|
||||
@@ -2580,7 +2818,7 @@ function LEGION:AssignAssetsForEscort(Cohorts, Assets, NescortMin, NescortMax, M
|
||||
TargetTypes=TargetTypes or targetTypes
|
||||
|
||||
-- Recruit escort asset for the mission asset.
|
||||
local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, MissionType, NescortMin, NescortMax, TargetVec2, nil, nil, nil, nil, nil, Categories)
|
||||
local Erecruited, eassets, elegions=LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.ESCORT, MissionType, NescortMin, NescortMax, TargetVec2, nil, nil, nil, nil, nil, nil, Categories)
|
||||
|
||||
if Erecruited then
|
||||
Escorts[asset.spawngroupname]={EscortLegions=elegions, EscortAssets=eassets, ecategory=asset.category}
|
||||
@@ -2685,24 +2923,8 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
|
||||
-- Is an escort requested in the first place?
|
||||
if NcarriersMin and NcarriersMax and (NcarriersMin>0 or NcarriersMax>0) then
|
||||
|
||||
-- Cohorts.
|
||||
local Cohorts={}
|
||||
for _,_legion in pairs(Legions) do
|
||||
local legion=_legion --Ops.Legion#LEGION
|
||||
|
||||
-- Check that runway is operational.
|
||||
local Runway=legion:IsAirwing() and legion:IsRunwayOperational() or true
|
||||
|
||||
if legion:IsRunning() and Runway then
|
||||
|
||||
-- Loops over cohorts.
|
||||
for _,_cohort in pairs(legion.cohorts) do
|
||||
local cohort=_cohort --Ops.Cohort#COHORT
|
||||
table.insert(Cohorts, cohort)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
-- Get cohorts.
|
||||
local Cohorts=LEGION._GetCohorts(Legions)
|
||||
|
||||
-- Get all legions and heaviest cargo group weight
|
||||
local CargoLegions={} ; local CargoWeight=nil ; local TotalWeight=0
|
||||
@@ -2714,13 +2936,17 @@ function LEGION:AssignAssetsForTransport(Legions, CargoAssets, NcarriersMin, Nca
|
||||
end
|
||||
TotalWeight=TotalWeight+asset.weight
|
||||
end
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Cargo weight=%.1f", CargoWeight))
|
||||
self:T(self.lid..string.format("Total weight=%.1f", TotalWeight))
|
||||
|
||||
-- Target is the deploy zone.
|
||||
local TargetVec2=DeployZone:GetVec2()
|
||||
|
||||
-- Recruit assets and legions.
|
||||
local TransportAvail, CarrierAssets, CarrierLegions=
|
||||
LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NcarriersMin, NcarriersMax, TargetVec2, nil, nil, nil, CargoWeight, TotalWeight, Categories, Attributes, Properties)
|
||||
LEGION.RecruitCohortAssets(Cohorts, AUFTRAG.Type.OPSTRANSPORT, nil, NcarriersMin, NcarriersMax, TargetVec2, nil, nil, nil, CargoWeight, TotalWeight, nil, Categories, Attributes, Properties)
|
||||
|
||||
if TransportAvail then
|
||||
|
||||
@@ -2922,7 +3148,7 @@ function LEGION._OptimizeAssetSelection(assets, MissionType, TargetVec2, Include
|
||||
local text=string.format("Optimized %d assets for %s mission/transport (payload=%s):", #assets, MissionType, tostring(IncludePayload))
|
||||
for i,Asset in pairs(assets) do
|
||||
local asset=Asset --Functional.Warehouse#WAREHOUSE.Assetitem
|
||||
text=text..string.format("\n%s %s: score=%d", asset.squadname, asset.spawngroupname, asset.score)
|
||||
text=text..string.format("\n%s %s: score=%d", asset.squadname, asset.spawngroupname, asset.score or -1)
|
||||
asset.score=nil
|
||||
end
|
||||
env.info(text)
|
||||
|
||||
@@ -885,10 +885,10 @@ function OPSGROUP:GetCoalition()
|
||||
return self.group:GetCoalition()
|
||||
end
|
||||
|
||||
--- Returns the absolute (average) life points of the group.
|
||||
--- Returns the absolute total life points of the group.
|
||||
-- @param #OPSGROUP self
|
||||
-- @param #OPSGROUP.Element Element (Optional) Only get life points of this element.
|
||||
-- @return #number Life points. If group contains more than one element, the average is given.
|
||||
-- @return #number Life points, *i.e.* the sum of life points over all units in the group (unless a specific element was passed).
|
||||
-- @return #number Initial life points.
|
||||
function OPSGROUP:GetLifePoints(Element)
|
||||
|
||||
@@ -3315,7 +3315,13 @@ function OPSGROUP:RemoveWaypoint(wpindex)
|
||||
else
|
||||
self.currentwp=self.currentwp-1
|
||||
end
|
||||
|
||||
|
||||
-- Could be that the waypoint we are currently moving to was the LAST waypoint. Then we now passed the final waypoint.
|
||||
if (self.adinfinitum or istemp) then
|
||||
self:_PassedFinalWaypoint(false, "Removed PASSED temporary waypoint ")
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -5689,6 +5695,7 @@ function OPSGROUP:RouteToMission(mission, delay)
|
||||
end
|
||||
|
||||
waypoint=ARMYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, formation, false)
|
||||
|
||||
elseif self:IsNavygroup() then
|
||||
|
||||
waypoint=NAVYGROUP.AddWaypoint(self, waypointcoord, SpeedToMission, uid, UTILS.MetersToFeet(mission.missionAltitude or self.altitudeCruise), false)
|
||||
@@ -5725,6 +5732,8 @@ function OPSGROUP:RouteToMission(mission, delay)
|
||||
if targetzone and self:IsInZone(targetzone) then
|
||||
self:T(self.lid.."Already in mission zone ==> TaskExecute()")
|
||||
self:TaskExecute(waypointtask)
|
||||
-- TODO: Calling PassingWaypoint here is probably better as it marks the mission waypoint as passed!
|
||||
--self:PassingWaypoint(waypoint)
|
||||
return
|
||||
elseif d<25 then
|
||||
self:T(self.lid.."Already within 25 meters of mission waypoint ==> TaskExecute()")
|
||||
@@ -6923,7 +6932,7 @@ function OPSGROUP:onafterElementDamaged(From, Event, To, Element)
|
||||
|
||||
local lifepoints=0
|
||||
|
||||
if Element.DCSunit and Element.DCSunit:isExist() then
|
||||
if Element.DCSunit then --and Element.DCSunit:isExist() then
|
||||
|
||||
-- Get life of unit
|
||||
lifepoints=Element.DCSunit:getLife()
|
||||
@@ -10145,28 +10154,32 @@ end
|
||||
-- @return #OPSGROUP self
|
||||
function OPSGROUP:_CheckDamage()
|
||||
|
||||
self:T(self.lid..string.format("Checking damage..."))
|
||||
|
||||
self.life=0
|
||||
local damaged=false
|
||||
|
||||
for _,_element in pairs(self.elements) do
|
||||
local element=_element --Ops.OpsGroup#OPSGROUP.Element
|
||||
|
||||
if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
|
||||
|
||||
-- Current life points.
|
||||
local life=element.unit:GetLife()
|
||||
|
||||
self.life=self.life+life
|
||||
|
||||
if life<element.life then
|
||||
element.life=life
|
||||
self:ElementDamaged(element)
|
||||
damaged=true
|
||||
end
|
||||
if element.status~=OPSGROUP.ElementStatus.DEAD and element.status~=OPSGROUP.ElementStatus.INUTERO then
|
||||
|
||||
end
|
||||
-- Current life points.
|
||||
local life=element.unit:GetLife()
|
||||
|
||||
self.life=self.life+life
|
||||
|
||||
if life<element.life then
|
||||
element.life=life
|
||||
self:ElementDamaged(element)
|
||||
damaged=true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- If anyone in the group was damaged, trigger event.
|
||||
if damaged then
|
||||
self:Damaged()
|
||||
end
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
-- @field #number Nred Number of red units in the zone.
|
||||
-- @field #number Nblu Number of blue units in the zone.
|
||||
-- @field #number Nnut Number of neutral units in the zone.
|
||||
-- @field #number Tred Threat level of red units in the zone.
|
||||
-- @field #number Tblu Threat level of blue units in the zone.
|
||||
-- @field #number Tnut Threat level of neutral units in the zone.
|
||||
-- @field #number TminCaptured Time interval in seconds how long an attacker must have troops inside the zone to capture.
|
||||
-- @field #number Tcaptured Time stamp (abs.) when the attacker destroyed all owning troops.
|
||||
-- @field #table ObjectCategories Object categories for the scan.
|
||||
@@ -43,6 +46,10 @@
|
||||
-- @field #string markerText Text shown in the maker.
|
||||
-- @field #table chiefs Chiefs that monitor this zone.
|
||||
-- @field #table Missions Missions that are attached to this OpsZone.
|
||||
-- @field #number nunitsCapture Number of units necessary to capture a zone.
|
||||
-- @field #number threatlevelCapture Threat level necessary to capture a zone.
|
||||
-- @field Core.Set#SET_UNIT ScanUnitSet Set of scanned units.
|
||||
-- @field Core.Set#SET_GROUP ScanGroupSet Set of scanned groups.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *Gentlemen, when the enemy is committed to a mistake we must not interrupt him too soon.* --- Horation Nelson
|
||||
@@ -64,6 +71,9 @@ OPSZONE = {
|
||||
Nred = 0,
|
||||
Nblu = 0,
|
||||
Nnut = 0,
|
||||
Tred = 0,
|
||||
Tblu = 0,
|
||||
Tnut = 0,
|
||||
chiefs = {},
|
||||
Missions = {},
|
||||
}
|
||||
@@ -76,7 +86,7 @@ OPSZONE = {
|
||||
|
||||
--- OPSZONE class version.
|
||||
-- @field #string version
|
||||
OPSZONE.version="0.3.1"
|
||||
OPSZONE.version="0.3.2"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
@@ -100,10 +110,10 @@ OPSZONE.version="0.3.1"
|
||||
-- @param #number CoalitionOwner Initial owner of the coaliton. Default `coalition.side.NEUTRAL`.
|
||||
-- @return #OPSZONE self
|
||||
-- @usage
|
||||
-- myopszone = OPSZONE:New(ZONE:FindByName("OpsZoneOne"),coalition.side.RED) -- base zone from the mission editor
|
||||
-- myopszone = OPSZONE:New(ZONE_RADIUS:New("OpsZoneTwo",mycoordinate:GetVec2(),5000),coalition.side.BLUE) -- radius zone of 5km at a coordinate
|
||||
-- myopszone = OPSZONE:New(ZONE_RADIUS:New("Batumi")) -- airbase zone from Batumi Airbase, ca 2500m radius
|
||||
-- myopszone = OPSZONE:New(ZONE_AIRBASE:New("Batumi",6000),coalition.side.BLUE) -- airbase zone from Batumi Airbase, but with a specific radius of 6km
|
||||
-- myopszone = OPSZONE:New(ZONE:FindByName("OpsZoneOne"), coalition.side.RED) -- base zone from the mission editor
|
||||
-- myopszone = OPSZONE:New(ZONE_RADIUS:New("OpsZoneTwo", mycoordinate:GetVec2(),5000),coalition.side.BLUE) -- radius zone of 5km at a coordinate
|
||||
-- myopszone = OPSZONE:New(ZONE_RADIUS:New("Batumi")) -- airbase zone from Batumi Airbase, ca 2500m radius
|
||||
-- myopszone = OPSZONE:New(ZONE_AIRBASE:New("Batumi",6000),coalition.side.BLUE) -- airbase zone from Batumi Airbase, but with a specific radius of 6km
|
||||
--
|
||||
function OPSZONE:New(Zone, CoalitionOwner)
|
||||
|
||||
@@ -151,6 +161,11 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
||||
self.zoneName=Zone:GetName()
|
||||
self.zoneRadius=Zone:GetRadius()
|
||||
self.Missions = {}
|
||||
self.ScanUnitSet=SET_UNIT:New():FilterZones({Zone})
|
||||
self.ScanGroupSet=SET_GROUP:New():FilterZones({Zone})
|
||||
|
||||
-- Add to database.
|
||||
_DATABASE:AddOpsZone(self)
|
||||
|
||||
-- Current and previous owners.
|
||||
self.ownerCurrent=CoalitionOwner or coalition.side.NEUTRAL
|
||||
@@ -165,9 +180,6 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
||||
self.ownerPrevious=self.airbase:GetCoalition()
|
||||
end
|
||||
|
||||
-- Set time to capture.
|
||||
self:SetTimeCapture()
|
||||
|
||||
-- Set object categories.
|
||||
self:SetObjectCategories()
|
||||
self:SetUnitCategories()
|
||||
@@ -176,6 +188,11 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
||||
self:SetDrawZone()
|
||||
self:SetMarkZone(true)
|
||||
|
||||
-- Default capture parameters.
|
||||
self:SetCaptureTime()
|
||||
self:SetCaptureNunits()
|
||||
self:SetCaptureThreatlevel()
|
||||
|
||||
-- Status timer.
|
||||
self.timerStatus=TIMER:New(OPSZONE.Status, self)
|
||||
|
||||
@@ -186,6 +203,8 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
||||
-- From State --> Event --> To State
|
||||
self:AddTransition("Stopped", "Start", "Empty") -- Start FSM.
|
||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||
|
||||
self:AddTransition("*", "Evaluated", "*") -- Evaluation done.
|
||||
|
||||
self:AddTransition("*", "Captured", "Guarded") -- Zone was captured.
|
||||
|
||||
@@ -220,6 +239,23 @@ function OPSZONE:New(Zone, CoalitionOwner)
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Evaluated".
|
||||
-- @function [parent=#OPSZONE] Evaluated
|
||||
-- @param #OPSZONE self
|
||||
|
||||
--- Triggers the FSM event "Evaluated" after a delay.
|
||||
-- @function [parent=#OPSZONE] __Evaluated
|
||||
-- @param #OPSZONE self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
--- On after "Evaluated" event.
|
||||
-- @function [parent=#OPSZONE] OnAfterEvaluated
|
||||
-- @param #OPSZONE self
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
|
||||
|
||||
--- Triggers the FSM event "Captured".
|
||||
-- @function [parent=#OPSZONE] Captured
|
||||
-- @param #OPSZONE self
|
||||
@@ -369,27 +405,27 @@ function OPSZONE:SetUnitCategories(Categories)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set threat level threshold that the defending units must have to hold a zone.
|
||||
-- The reason why you might want to set this is that unarmed units (*e.g.* fuel trucks) should not be able to hold a zone as they do not pose a threat.
|
||||
--- Set threat level threshold that the offending units must have to capture a zone.
|
||||
-- The reason why you might want to set this is that unarmed units (*e.g.* fuel trucks) should not be able to capture a zone as they do not pose a threat.
|
||||
-- @param #OPSZONE self
|
||||
-- @param #number Threatlevel Threat level threshod. Default 0.
|
||||
-- @param #number Threatlevel Threat level threshold. Default 0.
|
||||
-- @return #OPSZONE self
|
||||
function OPSZONE:SetThreatlevelDefinding(Threatlevel)
|
||||
function OPSZONE:SetCaptureThreatlevel(Threatlevel)
|
||||
|
||||
self.threatlevelDefending=Threatlevel or 0
|
||||
self.threatlevelCapture=Threatlevel or 0
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set threat level threshold that the offending units must have to capture a zone.
|
||||
-- The reason why you might want to set this is that unarmed units (*e.g.* fuel trucks) should not be able to capture a zone as they do not pose a threat.
|
||||
--- Set how many units must be present in a zone to capture it. By default, one unit is enough.
|
||||
-- @param #OPSZONE self
|
||||
-- @param #number Threatlevel Threat level threshod. Default 0.
|
||||
-- @param #number Nunits Number of units. Default 1.
|
||||
-- @return #OPSZONE self
|
||||
function OPSZONE:SetThreatlevelOffending(Threatlevel)
|
||||
function OPSZONE:SetCaptureNunits(Nunits)
|
||||
|
||||
self.threatlevelOffending=Threatlevel or 0
|
||||
Nunits=Nunits or 1
|
||||
|
||||
self.nunitsCapture=Nunits
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -399,7 +435,7 @@ end
|
||||
-- @param #OPSZONE self
|
||||
-- @param #number Tcapture Time in seconds. Default 0.
|
||||
-- @return #OPSZONE self
|
||||
function OPSZONE:SetTimeCapture(Tcapture)
|
||||
function OPSZONE:SetCaptureTime(Tcapture)
|
||||
|
||||
self.TminCaptured=Tcapture or 0
|
||||
|
||||
@@ -479,6 +515,21 @@ function OPSZONE:GetCoordinate()
|
||||
return coordinate
|
||||
end
|
||||
|
||||
--- Get scanned units inside the zone.
|
||||
-- @param #OPSZONE self
|
||||
-- @return Core.Set#SET_UNIT Set of units inside the zone.
|
||||
function OPSZONE:GetScannedUnitSet()
|
||||
return self.ScanUnitSet
|
||||
end
|
||||
|
||||
|
||||
--- Get scanned groups inside the zone.
|
||||
-- @param #OPSZONE self
|
||||
-- @return Core.Set#SET_GROUP Set of groups inside the zone.
|
||||
function OPSZONE:GetScannedGroupSet()
|
||||
return self.ScanGroupSet
|
||||
end
|
||||
|
||||
--- Returns a random coordinate in the zone.
|
||||
-- @param #OPSZONE self
|
||||
-- @param #number inner (Optional) Minimal distance from the center of the zone in meters. Default is 0 m.
|
||||
@@ -753,11 +804,7 @@ function OPSZONE:onafterEmpty(From, Event, To)
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Zone is empty EVENT"))
|
||||
|
||||
-- Inform chief.
|
||||
for _,_chief in pairs(self.chiefs) do
|
||||
local chief=_chief --Ops.Chief#CHIEF
|
||||
chief:ZoneEmpty(self)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
@@ -771,17 +818,7 @@ function OPSZONE:onafterAttacked(From, Event, To, AttackerCoalition)
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Zone is being attacked by coalition=%s!", tostring(AttackerCoalition)))
|
||||
|
||||
-- Inform chief.
|
||||
if AttackerCoalition then
|
||||
for _,_chief in pairs(self.chiefs) do
|
||||
local chief=_chief --Ops.Chief#CHIEF
|
||||
if chief.coalition~=AttackerCoalition then
|
||||
chief:ZoneAttacked(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- On after "Defeated" event.
|
||||
@@ -806,19 +843,24 @@ end
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
function OPSZONE:onenterGuarded(From, Event, To)
|
||||
|
||||
if From~=To then
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Zone is guarded"))
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Zone is guarded"))
|
||||
|
||||
-- Not attacked any more.
|
||||
self.Tattacked=nil
|
||||
|
||||
if self.drawZone then
|
||||
self.zone:UndrawZone()
|
||||
-- Not attacked any more.
|
||||
self.Tattacked=nil
|
||||
|
||||
if self.drawZone then
|
||||
|
||||
local color=self:_GetZoneColor()
|
||||
self.zone:UndrawZone()
|
||||
|
||||
local color=self:_GetZoneColor()
|
||||
|
||||
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
|
||||
end
|
||||
|
||||
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
|
||||
end
|
||||
|
||||
end
|
||||
@@ -828,26 +870,43 @@ end
|
||||
-- @param #string From From state.
|
||||
-- @param #string Event Event.
|
||||
-- @param #string To To state.
|
||||
function OPSZONE:onenterAttacked(From, Event, To)
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Zone is Attacked"))
|
||||
-- @param #number AttackerCoalition Coalition of the attacking ground troops.
|
||||
function OPSZONE:onenterAttacked(From, Event, To, AttackerCoalition)
|
||||
|
||||
-- Time stamp when the attack started.
|
||||
self.Tattacked=timer.getAbsTime()
|
||||
if From~="Attacked" then
|
||||
|
||||
-- Draw zone?
|
||||
if self.drawZone then
|
||||
self.zone:UndrawZone()
|
||||
|
||||
-- Color.
|
||||
local color={1, 204/255, 204/255}
|
||||
|
||||
-- Draw zone.
|
||||
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
|
||||
end
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Zone is Attacked"))
|
||||
|
||||
self:_CleanMissionTable()
|
||||
-- Set time stamp.
|
||||
self.Tattacked=timer.getAbsTime()
|
||||
|
||||
-- Inform chief.
|
||||
if AttackerCoalition then
|
||||
for _,_chief in pairs(self.chiefs) do
|
||||
local chief=_chief --Ops.Chief#CHIEF
|
||||
if chief.coalition~=AttackerCoalition then
|
||||
chief:ZoneAttacked(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Draw zone?
|
||||
if self.drawZone then
|
||||
self.zone:UndrawZone()
|
||||
|
||||
-- Color.
|
||||
local color={1, 204/255, 204/255}
|
||||
|
||||
-- Draw zone.
|
||||
self.zone:DrawZone(nil, color, 1.0, color, 0.5)
|
||||
end
|
||||
|
||||
self:_CleanMissionTable()
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- On enter "Empty" event.
|
||||
@@ -857,17 +916,27 @@ end
|
||||
-- @param #string To To state.
|
||||
function OPSZONE:onenterEmpty(From, Event, To)
|
||||
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Zone is empty now"))
|
||||
if From~=To then
|
||||
|
||||
if self.drawZone then
|
||||
self.zone:UndrawZone()
|
||||
|
||||
local color=self:_GetZoneColor()
|
||||
|
||||
self.zone:DrawZone(nil, color, 1.0, color, 0.2)
|
||||
end
|
||||
-- Debug info.
|
||||
self:T(self.lid..string.format("Zone is empty now"))
|
||||
|
||||
-- Inform chief.
|
||||
for _,_chief in pairs(self.chiefs) do
|
||||
local chief=_chief --Ops.Chief#CHIEF
|
||||
chief:ZoneEmpty(self)
|
||||
end
|
||||
|
||||
if self.drawZone then
|
||||
self.zone:UndrawZone()
|
||||
|
||||
local color=self:_GetZoneColor()
|
||||
|
||||
self.zone:DrawZone(nil, color, 1.0, color, 0.2)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -892,6 +961,13 @@ function OPSZONE:Scan()
|
||||
local Nred=0
|
||||
local Nblu=0
|
||||
local Nnut=0
|
||||
|
||||
local Tred=0
|
||||
local Tblu=0
|
||||
local Tnut=0
|
||||
|
||||
self.ScanGroupSet:Clear(false)
|
||||
self.ScanUnitSet:Clear(false)
|
||||
|
||||
--- Function to evaluate the world search
|
||||
local function EvaluateZone(_ZoneObject)
|
||||
@@ -939,13 +1015,35 @@ function OPSZONE:Scan()
|
||||
-- Get Coalition.
|
||||
local Coalition=DCSUnit:getCoalition()
|
||||
|
||||
local tl=0
|
||||
local unit=UNIT:Find(DCSUnit)
|
||||
if unit then
|
||||
|
||||
-- Threat level of unit.
|
||||
tl=unit:GetThreatLevel()
|
||||
|
||||
-- Add unit to set.
|
||||
self.ScanUnitSet:AddUnit(unit)
|
||||
|
||||
-- Get group of unit.
|
||||
local group=unit:GetGroup()
|
||||
|
||||
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.
|
||||
@@ -1014,6 +1112,10 @@ function OPSZONE:Scan()
|
||||
self.Nred=Nred
|
||||
self.Nblu=Nblu
|
||||
self.Nnut=Nnut
|
||||
|
||||
self.Tblu=Tblu
|
||||
self.Tred=Tred
|
||||
self.Tnut=Tnut
|
||||
|
||||
return self
|
||||
end
|
||||
@@ -1028,6 +1130,30 @@ function OPSZONE:EvaluateZone()
|
||||
local Nblu=self.Nblu
|
||||
local Nnut=self.Nnut
|
||||
|
||||
local Tnow=timer.getAbsTime()
|
||||
|
||||
--- Capture
|
||||
-- @param #number coal Coaltion capturing.
|
||||
local function captured(coal)
|
||||
|
||||
-- Blue captured red zone.
|
||||
if not self.airbase then
|
||||
|
||||
-- Set time stamp if it does not exist.
|
||||
if not self.Tcaptured then
|
||||
self.Tcaptured=Tnow
|
||||
end
|
||||
|
||||
-- Check if enough time elapsed.
|
||||
if Tnow-self.Tcaptured>=self.TminCaptured then
|
||||
self:Captured(coal)
|
||||
self.Tcaptured=nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if self:IsRed() then
|
||||
|
||||
---
|
||||
@@ -1038,43 +1164,16 @@ function OPSZONE:EvaluateZone()
|
||||
|
||||
-- No red units in red zone any more.
|
||||
|
||||
if Nblu>0 then
|
||||
-- Blue captured red zone.
|
||||
if not self.airbase then
|
||||
local Tnow=timer.getAbsTime()
|
||||
|
||||
-- Set time stamp if it does not exist.
|
||||
if not self.Tcaptured then
|
||||
self.Tcaptured=Tnow
|
||||
end
|
||||
|
||||
-- Check if enough time elapsed.
|
||||
if Tnow-self.Tcaptured>=self.TminCaptured then
|
||||
self:Captured(coalition.side.BLUE)
|
||||
self.Tcaptured=nil
|
||||
end
|
||||
end
|
||||
elseif Nnut>0 and self.neutralCanCapture then
|
||||
if Nblu>=self.nunitsCapture and self.Tblu>=self.threatlevelCapture then
|
||||
|
||||
-- Blue captued red zone.
|
||||
captured(coalition.side.BLUE)
|
||||
|
||||
elseif Nnut>=self.nunitsCapture and self.Tnut>=self.threatlevelCapture and self.neutralCanCapture then
|
||||
|
||||
-- Neutral captured red zone.
|
||||
if not self.airbase then
|
||||
local Tnow=timer.getAbsTime()
|
||||
|
||||
-- Set time stamp if it does not exist.
|
||||
if not self.Tcaptured then
|
||||
self.Tcaptured=Tnow
|
||||
end
|
||||
|
||||
-- Check if enough time elapsed.
|
||||
if Tnow-self.Tcaptured>=self.TminCaptured then
|
||||
self:Captured(coalition.side.NEUTRAL)
|
||||
self.Tcaptured=nil
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Red zone is now empty (but will remain red).
|
||||
if not self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
captured(coalition.side.NEUTRAL)
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
@@ -1117,21 +1216,16 @@ function OPSZONE:EvaluateZone()
|
||||
|
||||
-- No blue units in blue zone any more.
|
||||
|
||||
if Nred>0 then
|
||||
if Nred>=self.nunitsCapture and self.Tred>=self.threatlevelCapture then
|
||||
|
||||
-- Red captured blue zone.
|
||||
if not self.airbase then
|
||||
self:Captured(coalition.side.RED)
|
||||
end
|
||||
elseif Nnut>0 and self.neutralCanCapture then
|
||||
captured(coalition.side.RED)
|
||||
|
||||
elseif Nnut>=self.nunitsCapture and self.Tnut>=self.threatlevelCapture and self.neutralCanCapture then
|
||||
|
||||
-- Neutral captured blue zone.
|
||||
if not self.airbase then
|
||||
self:Captured(coalition.side.NEUTRAL)
|
||||
end
|
||||
else
|
||||
-- Blue zone is empty now.
|
||||
if not self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
captured(coalition.side.NEUTRAL)
|
||||
|
||||
end
|
||||
|
||||
else
|
||||
@@ -1152,7 +1246,7 @@ function OPSZONE:EvaluateZone()
|
||||
self:Defeated(coalition.side.RED)
|
||||
elseif self:IsEmpty() then
|
||||
-- Blue units left zone and returned (or from initial Empty state).
|
||||
self:Guarded()
|
||||
self:Guarded()
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1183,21 +1277,12 @@ function OPSZONE:EvaluateZone()
|
||||
self:Attacked()
|
||||
end
|
||||
self.isContested=true
|
||||
elseif Nred>0 then
|
||||
elseif Nred>=self.nunitsCapture and self.Tred>=self.threatlevelCapture then
|
||||
-- Red captured neutral zone.
|
||||
if not self.airbase then
|
||||
self:Captured(coalition.side.RED)
|
||||
end
|
||||
elseif Nblu>0 then
|
||||
captured(coalition.side.RED)
|
||||
elseif Nblu>=self.nunitsCapture and self.Tblu>=self.threatlevelCapture then
|
||||
-- Blue captured neutral zone.
|
||||
if not self.airbase then
|
||||
self:Captured(coalition.side.BLUE)
|
||||
end
|
||||
else
|
||||
-- Neutral zone is empty now.
|
||||
if not self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
captured(coalition.side.BLUE)
|
||||
end
|
||||
|
||||
--end
|
||||
@@ -1206,6 +1291,11 @@ function OPSZONE:EvaluateZone()
|
||||
self:E(self.lid.."ERROR: Unknown coaliton!")
|
||||
end
|
||||
|
||||
|
||||
-- No units of any coalition in zone any more ==> Empty!
|
||||
if Nblu==0 and Nred==0 and Nnut==0 and (not self:IsEmpty()) then
|
||||
self:Empty()
|
||||
end
|
||||
|
||||
-- Finally, check airbase coalition
|
||||
if self.airbase then
|
||||
@@ -1219,6 +1309,9 @@ function OPSZONE:EvaluateZone()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Trigger event.
|
||||
self:Evaluated()
|
||||
|
||||
end
|
||||
|
||||
@@ -1328,7 +1421,7 @@ function OPSZONE:_UpdateMarker()
|
||||
|
||||
end
|
||||
|
||||
--- Get marker text
|
||||
--- Get marker text.
|
||||
-- @param #OPSZONE self
|
||||
-- @return #string Marker text.
|
||||
function OPSZONE:_GetMarkerText()
|
||||
@@ -1337,8 +1430,10 @@ function OPSZONE:_GetMarkerText()
|
||||
local prevowner=UTILS.GetCoalitionName(self.ownerPrevious)
|
||||
|
||||
-- Get marker text.
|
||||
local text=string.format("%s: Owner=%s [%s]\nState=%s [Contested=%s]\nBlue=%d, Red=%d, Neutral=%d",
|
||||
self.zoneName, owner, prevowner, self:GetState(), tostring(self:IsContested()), self.Nblu, self.Nred, self.Nnut)
|
||||
local text=string.format("%s [N=%d, TL=%d T=%d]:\nOwner=%s [%s]\nState=%s [Contested=%s]\nBlue=%d [TL=%d]\nRed=%d [TL=%d]\nNeutral=%d [TL=%d]",
|
||||
self.zoneName, self.nunitsCapture or 0, self.threatlevelCapture or 0, self.TminCaptured or 0,
|
||||
owner, prevowner, self:GetState(), tostring(self:IsContested()),
|
||||
self.Nblu, self.Tblu, self.Nred, self.Tred, self.Nnut, self.Tnut)
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user