diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 813d74e1f..279474280 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1342,7 +1342,7 @@ function ZONE:New( ZoneName ) -- Error! if not Zone then - error( "Zone " .. ZoneName .. " does not exist." ) + env.error( "ERROR: Zone " .. ZoneName .. " does not exist!" ) return nil end diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index 499548c3f..14fa66182 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -812,6 +812,39 @@ function ARMYGROUP:onafterDetour(From, Event, To, Coordinate, Speed, Formation, end +--- On after "OutOfAmmo" event. +-- @param #ARMYGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARMYGROUP:onafterOutOfAmmo(From, Event, To) + self:T(self.lid..string.format("Group is out of ammo at t=%.3f", timer.getTime())) + + -- Fist, check if we want to rearm once out-of-ammo. + if self.rearmOnOutOfAmmo then + local truck=self:FindNearestAmmoSupply(30) + if truck then + self:T(self.lid..string.format("Found Ammo Truck %s [%s]")) + local Coordinate=truck:GetCoordinate() + self:Rearm(Coordinate, Formation) + return + end + end + + -- Second, check if we want to retreat once out of ammo. + if self.retreatOnOutOfAmmo then + self:Retreat() + return + end + + -- Third, check if we want to RTZ once out of ammo. + if self.rtzOnOutOfAmmo then + self:RTZ() + end + +end + + --- On before "Rearm" event. -- @param #ARMYGROUP self -- @param #string From From state. @@ -1403,6 +1436,7 @@ end -- @param #ARMYGROUP self -- @param #number Radius Search radius in NM. Default 30 NM. -- @return Wrapper.Group#GROUP Closest ammo supplying group or `nil` if no group is in the given radius. +-- @return #number Distance to closest group in meters. function ARMYGROUP:FindNearestAmmoSupply(Radius) -- Radius in meters. @@ -1410,6 +1444,9 @@ function ARMYGROUP:FindNearestAmmoSupply(Radius) -- Current positon. local coord=self:GetCoordinate() + + -- Get my coalition. + local myCoalition=self:GetCoalition() -- Scanned units. local units=coord:ScanUnits(Radius) @@ -1421,10 +1458,10 @@ function ARMYGROUP:FindNearestAmmoSupply(Radius) local unit=_unit --Wrapper.Unit#UNIT -- Check coaliton and if unit can supply ammo. - if unit:GetCoalition()==self:GetCoalition() and unit:IsAmmoSupply() then + if unit:GetCoalition()==myCoalition and unit:IsAmmoSupply() then -- Distance. - local d=unit:GetCoordinate():Get2DDistance(coord) + local d=coord:Get2DDistance(unit:GetCoord()) -- Check if distance is smaller. if d%s", Legion.alias, tostring(status), tostring(Status))) + self:T(self.lid..string.format("Setting LEGION %s to status %s-->%s", Legion.alias, tostring(status), tostring(Status))) -- New status. self.statusLegion[Legion.alias]=Status @@ -4750,6 +4773,24 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable) DCStask.params=param table.insert(DCStasks, DCStask) + + elseif self.type==AUFTRAG.Type.ONGUARD then + + ---------------------- + -- ON GUARD Mission -- + ---------------------- + + local DCStask={} + + DCStask.id=AUFTRAG.SpecialTask.ONGUARD + + -- We create a "fake" DCS task and pass the parameters to the OPSGROUP. + local param={} + param.coordinate=self:GetObjective() + + DCStask.params=param + + table.insert(DCStasks, DCStask) else self:E(self.lid..string.format("ERROR: Unknown mission task!")) diff --git a/Moose Development/Moose/Ops/Brigade.lua b/Moose Development/Moose/Ops/Brigade.lua index 86b743233..9d77f24ed 100644 --- a/Moose Development/Moose/Ops/Brigade.lua +++ b/Moose Development/Moose/Ops/Brigade.lua @@ -18,7 +18,8 @@ -- @type BRIGADE -- @field #string ClassName Name of the class. -- @field #number verbose Verbosity of output. --- @field #table rearmingZones Rearming zones. Each element is of type `#BRIGADE.RearmingZone`. +-- @field #table rearmingZones Rearming zones. Each element is of type `#BRIGADE.SupplyZone`. +-- @field #table refuellingZones Refuelling zones. Each element is of type `#BRIGADE.SupplyZone`. -- @field Core.Set#SET_ZONE retreatZones Retreat zone set. -- @extends Ops.Legion#LEGION @@ -33,16 +34,17 @@ -- -- @field #BRIGADE BRIGADE = { - ClassName = "BRIGADE", - verbose = 0, - rearmingZones = {}, + ClassName = "BRIGADE", + verbose = 0, + rearmingZones = {}, + refuellingZones = {}, } ---- Rearming Zone. --- @type BRIGADE.RearmingZone +--- Supply Zone. +-- @type BRIGADE.SupplyZone -- @field Core.Zone#ZONE zone The zone. --- @field #boolean occupied If `true`, a rearming truck is present in the zone. --- @field Ops.Auftrag#AUFTRAG mission Mission assigned to supply ammo. +-- @field Ops.Auftrag#AUFTRAG mission Mission assigned to supply ammo or fuel. +-- @field #boolean markerOn If `true`, marker is on. -- @field Wrapper.Marker#MARKER marker F10 marker. --- BRIGADE class version. @@ -225,13 +227,12 @@ end --- Add a rearming zone. -- @param #BRIGADE self -- @param Core.Zone#ZONE RearmingZone Rearming zone. --- @return #BRIGADE.RearmingZone The rearming zone data. +-- @return #BRIGADE.SupplyZone The rearming zone data. function BRIGADE:AddRearmingZone(RearmingZone) - local rearmingzone={} --#BRIGADE.RearmingZone + local rearmingzone={} --#BRIGADE.SupplyZone rearmingzone.zone=RearmingZone - rearmingzone.occupied=false rearmingzone.mission=nil rearmingzone.marker=MARKER:New(rearmingzone.zone:GetCoordinate(), "Rearming Zone"):ToCoalition(self:GetCoalition()) @@ -241,6 +242,24 @@ function BRIGADE:AddRearmingZone(RearmingZone) end +--- Add a refuelling zone. +-- @param #BRIGADE self +-- @param Core.Zone#ZONE RefuellingZone Refuelling zone. +-- @return #BRIGADE.SupplyZone The refuelling zone data. +function BRIGADE:AddRefuellingZone(RefuellingZone) + + local supplyzone={} --#BRIGADE.SupplyZone + + supplyzone.zone=RefuellingZone + supplyzone.mission=nil + supplyzone.marker=MARKER:New(supplyzone.zone:GetCoordinate(), "Refuelling Zone"):ToCoalition(self:GetCoalition()) + + table.insert(self.rearmingZones, supplyzone) + + return supplyzone +end + + --- Get platoon by name. -- @param #BRIGADE self -- @param #string PlatoonName Name of the platoon. @@ -312,14 +331,28 @@ function BRIGADE:onafterStatus(From, Event, To) -- Rearming Zones --- --------------------- - for i,_rearmingzone in pairs(self.rearmingZones) do - local rearmingzone=_rearmingzone --#BRIGADE.RearmingZone + for _,_rearmingzone in pairs(self.rearmingZones) do + local rearmingzone=_rearmingzone --#BRIGADE.SupplyZone if (not rearmingzone.mission) or rearmingzone.mission:IsOver() then rearmingzone.mission=AUFTRAG:NewAMMOSUPPLY(rearmingzone.zone) self:AddMission(rearmingzone.mission) end end + ----------------------- + -- Refuelling Zones --- + ----------------------- + + -- Check refuelling zones. + for _,_supplyzone in pairs(self.refuellingZones) do + local supplyzone=_supplyzone --#BRIGADE.SupplyZone + -- Check if mission is nil or over. + if (not supplyzone.mission) or supplyzone.mission:IsOver() then + supplyzone.mission=AUFTRAG:NewFUELSUPPLY(supplyzone.zone) + self:AddMission(supplyzone.mission) + end + end + ----------- -- Info --- @@ -396,19 +429,28 @@ function BRIGADE:onafterStatus(From, Event, To) ------------------- -- Rearming Info -- ------------------- - if self.verbose>=0 then + if self.verbose>=4 then local text="Rearming Zones:" for i,_rearmingzone in pairs(self.rearmingZones) do - local rearmingzone=_rearmingzone --#BRIGADE.RearmingZone - - local name=rearmingzone.zone:GetName() - - -- Platoon text. - text=text..string.format("\n* %s: Mission status=%s, suppliers=%d", name, rearmingzone.mission:GetState(), rearmingzone.mission:CountOpsGroups()) - + local rearmingzone=_rearmingzone --#BRIGADE.SupplyZone + -- Info text. + text=text..string.format("\n* %s: Mission status=%s, suppliers=%d", rearmingzone.zone:GetName(), rearmingzone.mission:GetState(), rearmingzone.mission:CountOpsGroups()) end 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 + local refuellingzone=_refuellingzone --#BRIGADE.SupplyZone + -- Info text. + 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 diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 892987ceb..48eaf2b62 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -60,11 +60,13 @@ CHIEF.DEFCON = { --- Strategy. -- @type CHIEF.Strategy +-- @field #string PASSIVE No targets at all are engaged. -- @field #string DEFENSIVE Only target in our own terretory are engaged. -- @field #string OFFENSIVE Targets in own terretory and yellow zones are engaged. --- @field #string AGGRESSIVE Targets in own terretroy, yellow zones and engage zones are engaged. +-- @field #string AGGRESSIVE Targets in own terretory, conflict zones and attack zones are engaged. -- @field #string TOTALWAR Anything is engaged anywhere. CHIEF.Strategy = { + PASSIVE="Passive", DEFENSIVE="Defensive", OFFENSIVE="Offensive", AGGRESSIVE="Aggressive", @@ -570,13 +572,25 @@ end --- Add a rearming zone. -- @param #CHIEF self -- @param Core.Zone#ZONE RearmingZone Rearming zone. --- @return Ops.Brigade#BRIGADE.RearmingZone The rearming zone data. +-- @return Ops.Brigade#BRIGADE.SupplyZone The rearming zone data. function CHIEF:AddRearmingZone(RearmingZone) -- Hand over to commander. - local rearmingzone=self.commander:AddRearmingZone(RearmingZone) + local supplyzone=self.commander:AddRearmingZone(RearmingZone) - return rearmingzone + return supplyzone +end + +--- Add a refuelling zone. +-- @param #CHIEF self +-- @param Core.Zone#ZONE RefuellingZone Refuelling zone. +-- @return Ops.Brigade#BRIGADE.SupplyZone The refuelling zone data. +function CHIEF:AddRefuellingZone(RefuellingZone) + + -- Hand over to commander. + local supplyzone=self.commander:AddRefuellingZone(RefuellingZone) + + return supplyzone end @@ -840,7 +854,8 @@ function CHIEF:onafterStatus(From, Event, To) for i,_opszone in pairs(self.zonequeue) do local opszone=_opszone --Ops.OpsZone#OPSZONE - text=text..string.format("\n[%d] %s [%s]: owner=%d [%d]: Blue=%d, Red=%d, Neutral=%d", i, opszone.zone:GetName(), opszone:GetState(), opszone:GetOwner(), opszone:GetPreviousOwner(), opszone.Nblu, opszone.Nred, opszone.Nnut) + text=text..string.format("\n[%d] %s [%s]: owner=%d [%d] (prio=%d, importance=%s): Blue=%d, Red=%d, Neutral=%d", i, + opszone.zone:GetName(), opszone:GetState(), opszone:GetOwner(), opszone:GetPreviousOwner(), opszone.prio, tostring(opszone.importance), opszone.Nblu, opszone.Nred, opszone.Nnut) end self:I(self.lid..text) @@ -1059,14 +1074,22 @@ function CHIEF:CheckTargetQueue() local target=_target --Ops.Target#TARGET -- Is this a threat? - local isThreat=target.threatlevel0>=self.threatLevelMin and target.threatlevel0<=self.threatLevelMax + local isThreat=target.threatlevel0>=self.threatLevelMin and target.threatlevel0<=self.threatLevelMax -- Check that target is alive and not already a mission has been assigned. if target:IsAlive() and (target.importance==nil or target.importance<=vip) and isThreat and not target.mission then -- Check if this target is "valid", i.e. fits with the current strategy. local valid=false - if self.strategy==CHIEF.Strategy.DEFENSIVE then + if self.strategy==CHIEF.Strategy.PASSIVE then + + --- + -- PASSIVE: No targets at all are attacked. + --- + + valid=false + + elseif self.strategy==CHIEF.Strategy.DEFENSIVE then --- -- DEFENSIVE: Attack inside borders only. @@ -1146,7 +1169,6 @@ function CHIEF:CheckTargetQueue() if mission then for _,_asset in pairs(assets) do local asset=_asset - asset.isReserved=true mission:AddAsset(asset) end Legions=legions @@ -1204,19 +1226,20 @@ function CHIEF:CheckOpsZoneQueue() -- Sort results table wrt ?. local function _sort(a, b) - local taskA=a --Ops.Target#TARGET - local taskB=b --Ops.Target#TARGET + local taskA=a --Ops.OpsZone#OPSZONE + local taskB=b --Ops.OpsZone#OPSZONE return (taskA.prio Recruit Patrol zone infantry assets")) + + -- Recruit ground assets that + local recruited=self:RecruitAssetsForZone(opszone, AUFTRAG.Type.PATROLZONE, 1, 3, {Group.Category.GROUND}, {GROUP.Attribute.GROUND_INFANTRY}) + + end + + else + + if not hasMissionCAS then + + -- Debug message. + self:T(self.lid..string.format("Zone is NOT empty ==> recruit CAS assets")) + + -- Recruite CAS assets. + local recruited=self:RecruitAssetsForZone(opszone, AUFTRAG.Type.CAS, 1, 3) + + end + + end end @@ -1568,45 +1619,78 @@ function CHIEF:RecruitAssetsForZone(OpsZone, MissionType, NassetsMin, NassetsMax -- Target position. local TargetVec2=OpsZone.zone:GetVec2() - + -- Recruite infantry assets. - local recruitedInf, assetsInf, legionsInf=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, nil, nil, nil, Categories, Attributes) + local recruited, assets, legions=LEGION.RecruitCohortAssets(Cohorts, MissionType, nil, NassetsMin, NassetsMax, TargetVec2, nil, nil, nil, nil, Categories, Attributes) - if recruitedInf then + if recruited then - env.info(string.format("Recruited %d assets from for PATROL mission", #assetsInf)) - - -- Recruit transport assets for infantry. - local recruitedTrans, transport=LEGION.AssignAssetsForTransport(self.commander, self.commander.legions, assetsInf, 1, 1, OpsZone.zone, nil, {Group.Category.HELICOPTER, Group.Category.GROUND}) - - - -- Create Patrol zone mission. - local mission=AUFTRAG:NewPATROLZONE(OpsZone.zone) - mission:SetEngageDetected() - - -- Add assets to mission. - for _,asset in pairs(assetsInf) do - mission:AddAsset(asset) - end - - -- Attach OPS transport to mission. - mission.opstransport=transport + if MissionType==AUFTRAG.Type.PATROLZONE then + + -- Debug messgage. + self:T2(self.lid..string.format("Recruited %d assets from for PATROL mission", #assets)) + + local recruitedTrans=true + local transport=nil + if Attributes and Attributes[1]==GROUP.Attribute.GROUND_INFANTRY then + + -- Recruit transport assets for infantry. + recruitedTrans, transport=LEGION.AssignAssetsForTransport(self.commander, self.commander.legions, assets, 1, 1, OpsZone.zone, nil, {Group.Category.HELICOPTER, Group.Category.GROUND}) - -- Assign mission to legions. - for _,_legion in pairs(legionsInf) do - local legion=_legion --Ops.Legion#LEGION - self.commander:MissionAssign(legion, mission) - end - - -- Attach mission to ops zone. - -- TODO: Need a better way! - OpsZone.missionPatrol=mission + end + + if recruitedTrans then - return true - else - LEGION.UnRecruitAssets(assetsInf) - return false - end + -- Create Patrol zone mission. + local mission=AUFTRAG:NewPATROLZONE(OpsZone.zone) + mission:SetEngageDetected() + + + -- Add assets to mission. + for _,asset in pairs(assets) do + mission:AddAsset(asset) + end + + -- Attach OPS transport to mission. + mission.opstransport=transport + + -- Assign mission to legions. + for _,_legion in pairs(legions) do + local legion=_legion --Ops.Legion#LEGION + self.commander:MissionAssign(legion, mission) + end + + -- Attach mission to ops zone. + -- TODO: Need a better way! + OpsZone.missionPatrol=mission + + else + LEGION.UnRecruitAssets(assets) + end + + elseif MissionType==AUFTRAG.Type.CAS then + + -- Create Patrol zone mission. + local mission=AUFTRAG:NewCAS(OpsZone.zone) + + -- Add assets to mission. + for _,asset in pairs(assets) do + mission:AddAsset(asset) + end + + -- Assign mission to legions. + for _,_legion in pairs(legions) do + local legion=_legion --Ops.Legion#LEGION + self.commander:MissionAssign(legion, mission) + end + + -- Attach mission to ops zone. + -- TODO: Need a better way! + OpsZone.missionCAS=mission + + end + + end return nil end diff --git a/Moose Development/Moose/Ops/Cohort.lua b/Moose Development/Moose/Ops/Cohort.lua index 47a167e8a..11ed4dd31 100644 --- a/Moose Development/Moose/Ops/Cohort.lua +++ b/Moose Development/Moose/Ops/Cohort.lua @@ -906,8 +906,14 @@ function COHORT:RecruitAssets(MissionType, Npayloads) end - --TODO: Check transport for combat readyness! - + -- 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 + -- This asset is "combatready". if combatready then self:I(self.lid.."Adding SPAWNED asset to ANOTHER mission as it is COMBATREADY") diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index cab981797..3eacd7030 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -20,7 +20,8 @@ -- @field #table legions Table of legions which are commanded. -- @field #table missionqueue Mission queue. -- @field #table transportqueue Transport queue. --- @field #table rearmingZones Rearming zones. Each element is of type `#BRIGADE.RearmingZone`. +-- @field #table rearmingZones Rearming zones. Each element is of type `#BRIGADE.SupplyZone`. +-- @field #table refuellingZones Refuelling zones. Each element is of type `#BRIGADE.SupplyZone`. -- @field Ops.Chief#CHIEF chief Chief of staff. -- @extends Core.Fsm#FSM @@ -35,12 +36,13 @@ -- -- @field #COMMANDER COMMANDER = { - ClassName = "COMMANDER", - verbose = 0, - legions = {}, - missionqueue = {}, - transportqueue = {}, - rearmingZones = {}, + ClassName = "COMMANDER", + verbose = 0, + legions = {}, + missionqueue = {}, + transportqueue = {}, + rearmingZones = {}, + refuellingZones = {}, } --- COMMANDER class version. @@ -335,13 +337,12 @@ end --- Add a rearming zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE RearmingZone Rearming zone. --- @return Ops.Brigade#BRIGADE.RearmingZone The rearming zone data. +-- @return Ops.Brigade#BRIGADE.SupplyZone The rearming zone data. function COMMANDER:AddRearmingZone(RearmingZone) - local rearmingzone={} --Ops.Brigade#BRIGADE.RearmingZone + local rearmingzone={} --Ops.Brigade#BRIGADE.SupplyZone rearmingzone.zone=RearmingZone - rearmingzone.occupied=false rearmingzone.mission=nil rearmingzone.marker=MARKER:New(rearmingzone.zone:GetCoordinate(), "Rearming Zone"):ToCoalition(self:GetCoalition()) @@ -350,6 +351,23 @@ function COMMANDER:AddRearmingZone(RearmingZone) return rearmingzone end +--- Add a refuelling zone. +-- @param #COMMANDER self +-- @param Core.Zone#ZONE RefuellingZone Refuelling zone. +-- @return Ops.Brigade#BRIGADE.SupplyZone The refuelling zone data. +function COMMANDER:AddRefuellingZone(RefuellingZone) + + local rearmingzone={} --Ops.Brigade#BRIGADE.SupplyZone + + rearmingzone.zone=RefuellingZone + rearmingzone.mission=nil + rearmingzone.marker=MARKER:New(rearmingzone.zone:GetCoordinate(), "Refuelling Zone"):ToCoalition(self:GetCoalition()) + + table.insert(self.refuellingZones, rearmingzone) + + return rearmingzone +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Start & Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -402,13 +420,23 @@ function COMMANDER:onafterStatus(From, Event, To) -- Check rearming zones. for _,_rearmingzone in pairs(self.rearmingZones) do - local rearmingzone=_rearmingzone --#BRIGADE.RearmingZone + local rearmingzone=_rearmingzone --#BRIGADE.SupplyZone -- Check if mission is nil or over. if (not rearmingzone.mission) or rearmingzone.mission:IsOver() then rearmingzone.mission=AUFTRAG:NewAMMOSUPPLY(rearmingzone.zone) self:AddMission(rearmingzone.mission) end - end + end + + -- Check refuelling zones. + for _,_supplyzone in pairs(self.refuellingZones) do + local supplyzone=_supplyzone --#BRIGADE.SupplyZone + -- Check if mission is nil or over. + if (not supplyzone.mission) or supplyzone.mission:IsOver() then + supplyzone.mission=AUFTRAG:NewFUELSUPPLY(supplyzone.zone) + self:AddMission(supplyzone.mission) + end + end --- -- LEGIONS diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index c59578513..16cb5e3ae 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -1140,7 +1140,7 @@ function FLIGHTGROUP:OnEventEngineShutdown(EventData) if element.unit and element.unit:IsAlive() then local airbase=self:GetClosestAirbase() - local parking=self:GetParkingSpot(element, 10, airbase) + local parking=self:GetParkingSpot(element, 100, airbase) if airbase and parking then self:ElementArrived(element, airbase, parking) @@ -1548,7 +1548,7 @@ end function FLIGHTGROUP:onafterParking(From, Event, To) -- Get closest airbase - local airbase=self:GetClosestAirbase() --self.group:GetCoordinate():GetClosestAirbase() + local airbase=self:GetClosestAirbase() local airbasename=airbase:GetName() or "unknown" -- Debug info @@ -2006,7 +2006,7 @@ function FLIGHTGROUP:onafterUpdateRoute(From, Event, To, n, N) end -- Set current waypoint or we get problem that the _PassingWaypoint function is triggered too early, i.e. right now and not when passing the next WP. - local current=self.group:GetCoordinate():WaypointAir(COORDINATE.WaypointAltType.BARO, waypointType, waypointAction, speed, true, nil, {}, "Current") + local current=self:GetCoordinate():WaypointAir(COORDINATE.WaypointAltType.BARO, waypointType, waypointAction, speed, true, nil, {}, "Current") table.insert(wp, current) -- Add remaining waypoints to route. @@ -2087,14 +2087,14 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime) if delay and delay>0 then -- Debug info. - self:T(self.lid..string.format("Check FLIGHTGROUP [state=%s] done in %.3f seconds...", fsmstate, delay)) + self:T(self.lid..string.format("Check FLIGHTGROUP [state=%s] done in %.3f seconds... (t=%.4f)", fsmstate, delay, timer.getTime())) -- Delayed call. self:ScheduleOnce(delay, FLIGHTGROUP._CheckGroupDone, self) else -- Debug info. - self:T(self.lid..string.format("Check FLIGHTGROUP [state=%s] done?", fsmstate)) + self:T(self.lid..string.format("Check FLIGHTGROUP [state=%s] done? (t=%.4f)", fsmstate, timer.getTime())) -- First check if there is a paused mission that if self.missionpaused then @@ -2134,7 +2134,9 @@ function FLIGHTGROUP:_CheckGroupDone(delay, waittime) self:T(self.lid..string.format("Remaining (final=%s): missions=%d, tasks=%d, transports=%d", tostring(self.passedfinalwp), nMissions, nTasks, nTransports)) -- Final waypoint passed? - if self:HasPassedFinalWaypoint() then + -- Or next waypoint index is the first waypoint. Could be that the group was on a mission and the mission waypoints were deleted. then the final waypoint is FALSE but no real waypoint left. + -- Since we do not do ad infinitum, this leads to a rapid oscillation between UpdateRoute and CheckGroupDone! + if self:HasPassedFinalWaypoint() or self:GetWaypointIndexNext()==1 then --- -- Final Waypoint PASSED @@ -2376,7 +2378,7 @@ function FLIGHTGROUP:_LandAtAirbase(airbase, SpeedTo, SpeedHold, SpeedLand) local althold=self.isHelo and 1000+math.random(10)*100 or math.random(4,10)*1000 -- Holding points. - local c0=self.group:GetCoordinate() + local c0=self:GetCoordinate() local p0=airbase:GetZone():GetRandomCoordinate():SetAltitude(UTILS.FeetToMeters(althold)) local p1=nil local wpap=nil @@ -2527,7 +2529,7 @@ end function FLIGHTGROUP:onafterWait(From, Event, To, Duration, Altitude, Speed) -- Group will orbit at its current position. - local Coord=self.group:GetCoordinate() + local Coord=self:GetCoordinate() -- Set altitude: 1000 ft for helos and 10,000 ft for panes. if Altitude then @@ -2597,7 +2599,7 @@ function FLIGHTGROUP:onafterRefuel(From, Event, To, Coordinate) local Speed=self.speedCruise - local coordinate=self.group:GetCoordinate() + local coordinate=self:GetCoordinate() Coordinate=Coordinate or coordinate:Translate(UTILS.NMToMeters(5), self.group:GetHeading(), true) @@ -3972,7 +3974,7 @@ function FLIGHTGROUP:_UpdateMenu(delay) self:I(self.lid.."FF updating menu NOW") -- Get current position of group. - local position=self.group:GetCoordinate() + local position=self:GetCoordinate() -- Get all FLIGHTCONTROLS local fc={} diff --git a/Moose Development/Moose/Ops/Legion.lua b/Moose Development/Moose/Ops/Legion.lua index 144f89a6d..5a78d0ac0 100644 --- a/Moose Development/Moose/Ops/Legion.lua +++ b/Moose Development/Moose/Ops/Legion.lua @@ -841,6 +841,13 @@ function LEGION:onafterTransportCancel(From, Event, To, Transport) opsgroup:TransportCancel(Transport) end + -- Delete awaited transport. + local cargos=Transport:GetCargoOpsGroups(false) + for _,_cargo in pairs(cargos) do + local cargo=_cargo --Ops.OpsGroup#OPSGROUP + cargo:_DelMyLift(Transport) + end + -- Remove asset from mission. Transport:DelAsset(asset) @@ -1068,7 +1075,7 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) if cohort then -- Debug info. - self:I(self.lid..string.format("Cohort asset spawned %s", asset.spawngroupname)) + self:T(self.lid..string.format("Cohort asset spawned %s", asset.spawngroupname)) -- Create a flight group. local flightgroup=self:_CreateFlightGroup(asset) @@ -1150,7 +1157,7 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request) -- Add group to the detection set of the CHIEF (INTEL). local chief=self.chief or (self.commander and self.commander.chief or nil) --Ops.Chief#CHIEF if chief then - self:I(self.lid..string.format("Adding group %s to agents of CHIEF", group:GetName())) + self:T(self.lid..string.format("Adding group %s to agents of CHIEF", group:GetName())) chief.detectionset:AddGroup(asset.flightgroup.group) end @@ -1932,7 +1939,7 @@ function LEGION.RecruitCohortAssets(Cohorts, MissionTypeRecruit, MissionTypeOpt, local RightAttribute=CheckAttribute(cohort) -- Debug info. - cohort:I(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, RightCategory=%s, RightAttribute=%s", + cohort:T(cohort.lid..string.format("State=%s: Capable=%s, InRange=%s, Refuel=%s, CanCarry=%s, RightCategory=%s, RightAttribute=%s", cohort:GetState(), tostring(Capable), tostring(InRange), tostring(Refuel), tostring(CanCarry), tostring(RightCategory), tostring(RightAttribute))) -- Check OnDuty, capable, in range and refueling type (if TANKER). diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index dbe18ae14..2a09a1f56 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -69,6 +69,8 @@ -- @field #number Ndestroyed Number of destroyed units. -- @field #number Nkills Number kills of this groups. -- +-- @field #boolean rearmOnOutOfAmmo If `true`, group will go to rearm once it runs out of ammo. +-- -- @field Ops.Legion#LEGION legion Legion the group belongs to. -- @field Ops.Cohort#COHORT cohort Cohort the group belongs to. -- @@ -424,11 +426,13 @@ OPSGROUP.CarrierStatus={ --- Cargo status. -- @type OPSGROUP.CargoStatus +-- @field #string AWAITING Group is awaiting carrier. -- @field #string NOTCARGO This group is no cargo yet. -- @field #string ASSIGNED Cargo is assigned to a carrier. -- @field #string BOARDING Cargo is boarding a carrier. -- @field #string LOADED Cargo is loaded into a carrier. OPSGROUP.CargoStatus={ + AWAITING="Awaiting carrier", NOTCARGO="not cargo", ASSIGNED="assigned to carrier", BOARDING="boarding", @@ -1170,6 +1174,14 @@ function OPSGROUP:SetEngageDetectedOff() return self end +--- Set that group is going to rearm once it runs out of ammo. +-- @param #OPSGROUP self +-- @return #OPSGROUP self +function OPSGROUP:SetRearmOnOutOfAmmo() + self.rearmOnOutOfAmmo=true + 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. @@ -2277,6 +2289,49 @@ function OPSGROUP:IsNotCargo(CheckTransport) return notcargo end +--- Check if awaiting a transport. +-- @param #OPSGROUP self +-- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. +-- @return #OPSGROUP self +function OPSGROUP:_AddMyLift(Transport) + self.mylifts=self.mylifts or {} + self.mylifts[Transport.uid]=true + return self +end + +--- Remove my lift. +-- @param #OPSGROUP self +-- @param Ops.OpsTransport#OPSTRANSPORT Transport The transport. +-- @return #OPSGROUP self +function OPSGROUP:_DelMyLift(Transport) + if self.mylifts then + self.mylifts[Transport.uid]=nil + end + return self +end + + +--- Check if awaiting a transport lift. +-- @param #OPSGROUP self +-- @param Ops.OpsTransport#OPSTRANSPORT Transport (Optional) The transport. +-- @return #boolean If true, group is awaiting transport lift.. +function OPSGROUP:IsAwaitingLift(Transport) + + if self.mylifts then + + for uid,iswaiting in pairs(self.mylifts) do + if Transport==nil or Transport.uid==uid then + if iswaiting==true then + return true + end + end + end + + end + + return false +end + --- Check if the group is currently boarding a carrier. -- @param #OPSGROUP self -- @param #string CarrierGroupName (Optional) Additionally check if group is boarding this particular carrier group. @@ -3589,10 +3644,20 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) elseif Task.dcstask.id==AUFTRAG.SpecialTask.ALERT5 then --- - -- Task Alert 5 mission. + -- Task "Alert 5" mission. --- -- Just stay put on the airfield and wait until something happens. + + elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD then + + --- + -- Task "On Guard" Mission. + --- + + -- Just stay put. + --TODO: Change ALARM STATE + else @@ -3690,7 +3755,9 @@ function OPSGROUP:onafterTaskCancel(From, Event, To, Task) elseif Task.dcstask.id==AUFTRAG.SpecialTask.FUELSUPPLY then done=true elseif Task.dcstask.id==AUFTRAG.SpecialTask.ALERT5 then - done=true + done=true + elseif Task.dcstask.id==AUFTRAG.SpecialTask.ONGUARD then + done=true elseif stopflag==1 or (not self:IsAlive()) or self:IsDead() or self:IsStopped() then -- Manual call TaskDone if setting flag to one was not successful. done=true @@ -4200,7 +4267,7 @@ function OPSGROUP:onafterMissionCancel(From, Event, To, Mission) --- -- Alert 5 missoins dont have a task set, which could be cancelled. - if Mission.type==AUFTRAG.Type.ALERT5 then + if Mission.type==AUFTRAG.Type.ALERT5 or Mission.type==AUFTRAG.Type.ONGUARD then self:MissionDone(Mission) return end @@ -4440,6 +4507,10 @@ function OPSGROUP:RouteToMission(mission, delay) elseif mission.type==AUFTRAG.Type.ARTY then + --- + -- ARTY + --- + -- Get weapon range. local weapondata=self:GetWeaponData(mission.engageWeaponType) @@ -5990,7 +6061,7 @@ end -- @param #string Event Event. -- @param #string To To state. function OPSGROUP:onafterOutOfAmmo(From, Event, To) - self:T(self.lid..string.format("Group is out of ammo at t=%.3f", timer.getTime())) + self:T(self.lid..string.format("Group is out of ammo at t=%.3f", timer.getTime())) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -7174,7 +7245,7 @@ function OPSGROUP:onafterLoad(From, Event, To, CargoGroup, Carrier) -- New cargo status. CargoGroup:_NewCargoStatus(OPSGROUP.CargoStatus.LOADED) - + -- Clear all waypoints. CargoGroup:ClearWaypoints() @@ -7191,6 +7262,7 @@ function OPSGROUP:onafterLoad(From, Event, To, CargoGroup, Carrier) -- Trigger "Loaded" event for current cargo transport. if self.cargoTransport then + CargoGroup:_DelMyLift(self.cargoTransport) self.cargoTransport:Loaded(CargoGroup, self, carrier) else self:T(self.lid..string.format("WARNING: Loaded cargo but no current OPSTRANSPORT assignment!")) @@ -7341,7 +7413,7 @@ function OPSGROUP:onafterTransport(From, Event, To) end -- ARMYGROUP - local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid) ; waypoint.detour=1 + local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid, ENUMS.Formation.Vehicle.OnRoad) ; waypoint.detour=1 -- Give cruise command. self:Cruise() @@ -8444,7 +8516,7 @@ function OPSGROUP:_CheckAmmoStatus() if self.outofMissilesAA and ammo.MissilesAA>0 then self.outofMissilesAA=false end - if ammo.MissilesAA and self.ammo.MissilesAA>0 and not self.outofMissilesAA then + if ammo.MissilesAA==0 and self.ammo.MissilesAA>0 and not self.outofMissilesAA then self.outofMissilesAA=true self:OutOfMissilesAA() end @@ -8453,7 +8525,7 @@ function OPSGROUP:_CheckAmmoStatus() if self.outofMissilesAG and ammo.MissilesAG>0 then self.outofMissilesAG=false end - if ammo.MissilesAG and self.ammo.MissilesAG>0 and not self.outofMissilesAG then + if ammo.MissilesAG==0 and self.ammo.MissilesAG>0 and not self.outofMissilesAG then self.outofMissilesAG=true self:OutOfMissilesAG() end @@ -8462,7 +8534,7 @@ function OPSGROUP:_CheckAmmoStatus() if self.outofMissilesAS and ammo.MissilesAS>0 then self.outofMissilesAS=false end - if ammo.MissilesAS and self.ammo.MissilesAS>0 and not self.outofMissilesAS then + if ammo.MissilesAS==0 and self.ammo.MissilesAS>0 and not self.outofMissilesAS then self.outofMissilesAS=true self:OutOfMissilesAS() end diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index 8e40b8a4f..d55de1522 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -485,6 +485,8 @@ function OPSTRANSPORT:AddCargoGroups(GroupSet, TransportZoneCombo) table.insert(TransportZoneCombo.Cargos, cargo) TransportZoneCombo.Ncargo=TransportZoneCombo.Ncargo+1 + cargo.opsgroup:_AddMyLift(self) + end else @@ -1296,7 +1298,7 @@ end function OPSTRANSPORT:AddLegion(Legion) -- Debug info. - self:I(self.lid..string.format("Adding legion %s", Legion.alias)) + self:T(self.lid..string.format("Adding legion %s", Legion.alias)) -- Add legion to table. table.insert(self.legions, Legion) @@ -1315,7 +1317,7 @@ function OPSTRANSPORT:RemoveLegion(Legion) local legion=self.legions[i] --Ops.Legion#LEGION if legion.alias==Legion.alias then -- Debug info. - self:I(self.lid..string.format("Removing legion %s", Legion.alias)) + self:T(self.lid..string.format("Removing legion %s", Legion.alias)) table.remove(self.legions, i) return self end @@ -1409,7 +1411,7 @@ function OPSTRANSPORT:SetLegionStatus(Legion, Status) local status=self:GetLegionStatus(Legion) -- Debug info. - self:I(self.lid..string.format("Setting LEGION %s to status %s-->%s", Legion.alias, tostring(status), tostring(Status))) + self:T(self.lid..string.format("Setting LEGION %s to status %s-->%s", Legion.alias, tostring(status), tostring(Status))) -- New status. self.statusLegion[Legion.alias]=Status @@ -1681,7 +1683,7 @@ function OPSTRANSPORT:onafterCancel(From, Event, To) local Ngroups = #self.carriers -- Debug info. - self:I(self.lid..string.format("CANCELLING mission in status %s. Will wait for %d groups to report mission DONE before evaluation", self.status, Ngroups)) + self:I(self.lid..string.format("CANCELLING transport in status %s. Will wait for %d carrier groups to report DONE before evaluation", self.status, Ngroups)) -- Time stamp. self.Tover=timer.getAbsTime() @@ -1728,6 +1730,14 @@ function OPSTRANSPORT:onafterCancel(From, Event, To) carrier:TransportCancel(self) end + -- Delete awaited transport. + local cargos=self:GetCargoOpsGroups(false) + for _,_cargo in pairs(cargos) do + local cargo=_cargo --Ops.OpsGroup#OPSGROUP + cargo:_DelMyLift(self) + end + + end -- Special mission states. @@ -1969,7 +1979,7 @@ function OPSTRANSPORT:_CountCargosInZone(Zone, Delivered, Carrier, TransportZone local isInUtero=cargo:IsInUtero() -- Debug info. - self:I(self.lid..string.format("Cargo=%s: notcargo=%s, iscarrier=%s inzone=%s, inutero=%s", cargo:GetName(), tostring(cargo:IsNotCargo(true)), tostring(iscarrier(cargo)), tostring(isInZone), tostring(isInUtero))) + self:T(self.lid..string.format("Cargo=%s: notcargo=%s, iscarrier=%s inzone=%s, inutero=%s", cargo:GetName(), tostring(cargo:IsNotCargo(true)), tostring(iscarrier(cargo)), tostring(isInZone), tostring(isInUtero))) -- We look for groups that are not cargo, in the zone or in utero. if isNotCargo and (isInZone or isInUtero) then diff --git a/Moose Development/Moose/Ops/OpsZone.lua b/Moose Development/Moose/Ops/OpsZone.lua index 5ea26d633..284c0a5f4 100644 --- a/Moose Development/Moose/Ops/OpsZone.lua +++ b/Moose Development/Moose/Ops/OpsZone.lua @@ -36,6 +36,8 @@ -- @field #boolean neutralCanCapture Neutral units can capture. Default `false`. -- @field #boolean drawZone If `true`, draw the zone on the F10 map. -- @field #boolean markZone If `true`, mark the zone on the F10 map. +-- @field #number prio Priority of the zone (for CHIEF queue). +-- @field #number importance Importance of the zone (for CHIEF queue). -- @extends Core.Fsm#FSM --- Be surprised! @@ -90,15 +92,34 @@ function OPSZONE:New(Zone, CoalitionOwner) local self=BASE:Inherit(self, FSM:New()) -- #OPSZONE -- Check if zone name instead of ZONE object was passed. - if type(Zone)=="string" then - Zone=ZONE:New(Zone) + if Zone then + if type(Zone)=="string" then + -- Convert string into a ZONE or ZONE_AIRBASE + local Name=Zone + Zone=ZONE:New(Name) + if not Zone then + local airbase=AIRBASE:FindByName(Name) + if airbase then + Zone=ZONE_AIRBASE:New(Name, 2000) + end + end + if not Zone then + self:E(string.format("ERROR: No ZONE or ZONE_AIRBASE found for name: %s", Name)) + return nil + end + end + else + self:E("ERROR: First parameter Zone is nil in OPSZONE:New(Zone) call!") + return nil end -- Basic checks. - if not Zone then - self:E("ERROR: OPSZONE not found!") - return nil - elseif not Zone:IsInstanceOf("ZONE_RADIUS") then + if Zone:IsInstanceOf("ZONE_AIRBASE") then + self.airbase=Zone._.ZoneAirbase + self.airbaseName=self.airbase:GetName() + elseif Zone:IsInstanceOf("ZONE_RADIUS") then + -- Nothing to do. + else self:E("ERROR: OPSZONE must be a SPHERICAL zone due to DCS restrictions!") return nil end @@ -115,10 +136,21 @@ function OPSZONE:New(Zone, CoalitionOwner) self.ownerCurrent=CoalitionOwner or coalition.side.NEUTRAL self.ownerPrevious=CoalitionOwner or coalition.side.NEUTRAL + -- We take the airbase coalition. + if self.airbase then + self.ownerCurrent=self.airbase:GetCoalition() + self.ownerPrevious=self.airbase:GetCoalition() + end + + -- Set priority (default 50) and importance (default nil). + self:SetPriority() + self:SetImportance() + -- Set object categories. self:SetObjectCategories() self:SetUnitCategories() + -- TODO: make input function self.drawZone=true -- Status timer. @@ -138,8 +170,7 @@ function OPSZONE:New(Zone, CoalitionOwner) self:AddTransition("Empty", "Guarded", "Guarded") -- Owning coalition left the zone and returned. self:AddTransition("*", "Empty", "Empty") -- No red or blue units inside the zone. - - + self:AddTransition("*", "Attacked", "Attacked") -- A guarded zone is under attack. self:AddTransition("*", "Defeated", "Guarded") -- The owning coalition defeated an attack. @@ -318,6 +349,25 @@ function OPSZONE:SetNeutralCanCapture(CanCapture) return self end +--- **[CHIEF]** Set mission priority. +-- @param #OPSZONE self +-- @param #number Prio Priority 1=high, 100=low. Default 50. +-- @return #OPSZONE self +function OPSZONE:SetPriority(Prio) + self.prio=Prio or 50 + return self +end + +--- **[CHIEF]** Set importance. +-- @param #OPSZONE self +-- @param #number Importance Number 1-10. If missions with lower value are in the queue, these have to be finished first. Default is `nil`. +-- @return #OPSZONE self +function OPSZONE:SetImportance(Importance) + self.importance=Importance + return self +end + + --- Get current owner of the zone. -- @param #OPSZONE self -- @return #number Owner coalition. @@ -426,21 +476,14 @@ function OPSZONE:onafterStart(From, Event, To) -- Reinit the timer. self.timerStatus=self.timerStatus or TIMER:New(OPSZONE.Status, self) - -- Perform initial scan. - self:Scan() - - if self.Nblu==0 and self.Nred==0 then - elseif self.Nblu>0 and self.Nred>0 then - - elseif self.Nblu>0 then - - elseif self.Nred>0 then - - end - -- Status update. self.timerStatus:Start(1, 60) + -- Handle base captured event. + if self.airbase then + self:HandleEvent(EVENTS.BaseCaptured) + end + end --- Stop OPSZONE FSM. @@ -456,6 +499,9 @@ function OPSZONE:onafterStop(From, Event, To) -- Reinit the timer. self.timerStatus:Stop() + -- Unhandle events. + self:UnHandleEvent(EVENTS.BaseCaptured) + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -481,6 +527,9 @@ function OPSZONE:Status() -- Scanning zone. self:Scan() + -- Evaluate the scan result. + self:EvaluateZone() + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -782,10 +831,14 @@ function OPSZONE:EvaluateZone() if Nblu>0 then -- Blue captured red zone. - self:Captured(coalition.side.BLUE) + if not self.airbase then + self:Captured(coalition.side.BLUE) + end elseif Nnut>0 and self.neutralCanCapture then -- Neutral captured red zone. - self:Captured(coalition.side.NEUTRAL) + if not self.airbase then + self:Captured(coalition.side.NEUTRAL) + end else -- Red zone is now empty (but will remain red). if not self:IsEmpty() then @@ -835,10 +888,14 @@ function OPSZONE:EvaluateZone() if Nred>0 then -- Red captured blue zone. - self:Captured(coalition.side.RED) + if not self.airbase then + self:Captured(coalition.side.RED) + end elseif Nnut>0 and self.neutralCanCapture then -- Neutral captured blue zone. - self:Captured(coalition.side.NEUTRAL) + if not self.airbase then + self:Captured(coalition.side.NEUTRAL) + end else -- Blue zone is empty now. if not self:IsEmpty() then @@ -896,10 +953,14 @@ function OPSZONE:EvaluateZone() self.isContested=true elseif Nred>0 then -- Red captured neutral zone. - self:Captured(coalition.side.RED) + if not self.airbase then + self:Captured(coalition.side.RED) + end elseif Nblu>0 then -- Blue captured neutral zone. - self:Captured(coalition.side.BLUE) + if not self.airbase then + self:Captured(coalition.side.BLUE) + end else -- Neutral zone is empty now. if not self:IsEmpty() then @@ -947,7 +1008,7 @@ function OPSZONE:OnEventHit(EventData) end ---- Monitor hit events. +--- Monitor base captured events. -- @param #OPSZONE self -- @param Core.Event#EVENTDATA EventData The event data. function OPSZONE:OnEventBaseCaptured(EventData) @@ -960,6 +1021,17 @@ function OPSZONE:OnEventBaseCaptured(EventData) -- Check that this airbase belongs or did belong to this warehouse. if EventData.PlaceName==self.airbaseName then + -- New coalition of the airbase + local CoalitionNew=airbase:GetCoalition() + + -- Debug info. + self:I(self.lid..string.format("EVENT BASE CAPTURED: New coalition of airbase %s: %d [previous=%d]", self.airbaseName, CoalitionNew, self.ownerCurrent)) + + -- Check that coalition actually changed. + if CoalitionNew~=self.ownerCurrent then + self:Captured(CoalitionNew) + end + end end