From 2a73d8d34af0e9251516b65ec21fa95b5ec6146f Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 1 Aug 2026 16:29:24 +0200 Subject: [PATCH] Add TERRITORY class and various fixes Add a new passive TERRITORY functional module (Functional/Territory.lua) and register it in Modules.lua. TERRITORY provides zone-backed territory objects and DB helpers (Find/Add/Delete). Update AUFTRAG: bump version to 1.5.0, introduce AUFTRAG.Summary, add an "Evaluated" event/transition, populate and emit mission summary from Evaluate(), set a dTevaluate for BAI, replace Stop() with __Stop(-120) on terminal states, and clear summary on repeats/stops. Minor ARTY debug prints and make Speed param optional. INTEL: add _UpdateAgents and SetAgentAuto to optionally auto-refresh the detection set and invoke it during status updates. OPSGROUP: require StayInZoneTime>0 before treating task as stayed. AIRBASE: determine DCS object category and name, handle ship/oil-rig/helipad edge cases (add static helipad to DB), keep Zell detection, and create parkingCircle with explicit parameter. Coherent small refactors and safety checks across modules. --- .../Moose/Functional/Territory.lua | 292 ++++++++++++++++++ Moose Development/Moose/Modules.lua | 1 + Moose Development/Moose/Ops/Auftrag.lua | 66 +++- Moose Development/Moose/Ops/Intelligence.lua | 30 ++ Moose Development/Moose/Ops/OpsGroup.lua | 2 +- Moose Development/Moose/Wrapper/Airbase.lua | 51 ++- 6 files changed, 421 insertions(+), 21 deletions(-) create mode 100644 Moose Development/Moose/Functional/Territory.lua diff --git a/Moose Development/Moose/Functional/Territory.lua b/Moose Development/Moose/Functional/Territory.lua new file mode 100644 index 000000000..53d644aba --- /dev/null +++ b/Moose Development/Moose/Functional/Territory.lua @@ -0,0 +1,292 @@ +--- **Ops** - Passive strategic territory. +-- +-- **Main Features:** +-- +-- * Associate a static MOOSE zone with a coalition +-- * Keep strategic territory geometry separate from OPSZONE scanning +-- * Register territories in the MOOSE database +-- * Delegate coordinate checks and F10 drawing to the underlying zone +-- +-- A TERRITORY is deliberately passive. It does not scan DCS objects, evaluate +-- ownership, run a scheduler, or make tactical or strategic decisions. +-- +-- === +-- +-- @module Ops.Territory + + +--- TERRITORY class. +-- @type TERRITORY +-- @field #string ClassName Name of the class. +-- @field #string version Class version. +-- @field #number verbose Verbosity level. +-- @field #string lid Log ID string. +-- @field #string name Unique territory name. +-- @field #string zoneName Name of the underlying MOOSE zone. +-- @field Core.Zone#ZONE_BASE zone Underlying MOOSE zone. +-- @field #number coalition Coalition associated with the territory. +-- @extends Core.Base#BASE + +--- A passive strategic area defined by an existing MOOSE zone. +-- +-- TERRITORY contains geometry and declarative ownership only. Unlike +-- @{Ops.OpsZone#OPSZONE}, it performs no periodic object scans and has no FSM. +-- +-- @field #TERRITORY +TERRITORY = { + ClassName = "TERRITORY", + verbose = 0, +} + +--- TERRITORY class version. +-- @field #string version +TERRITORY.version = "0.1.0" + +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- DATABASE extension +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Finds a TERRITORY based on its name. +-- @param Core.Database#DATABASE self +-- @param #string TerritoryName Name of the territory. +-- @return #TERRITORY The found territory or `nil`. +function DATABASE:FindTerritory(TerritoryName) + local territories = self.TERRITORIES or {} + return territories[TerritoryName] +end + +--- Adds a TERRITORY to the database. +-- @param Core.Database#DATABASE self +-- @param #TERRITORY Territory Territory to add. +-- @return #TERRITORY The registered territory or `nil`. +function DATABASE:AddTerritory(Territory) + if not Territory then + return nil + end + + self.TERRITORIES = self.TERRITORIES or {} + + local territoryName = Territory:GetName() + if not self.TERRITORIES[territoryName] then + self.TERRITORIES[territoryName] = Territory + end + + return self.TERRITORIES[territoryName] +end + +--- Deletes a TERRITORY from the database. +-- @param Core.Database#DATABASE self +-- @param #string TerritoryName Name of the territory. +-- @return Core.Database#DATABASE self +function DATABASE:DeleteTerritory(TerritoryName) + if self.TERRITORIES then + self.TERRITORIES[TerritoryName] = nil + end + return self +end + +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create a new TERRITORY class object. +-- @param #TERRITORY self +-- @param Core.Zone#ZONE_BASE Zone The underlying MOOSE zone or its Mission Editor name. +-- @param #number Coalition (Optional) Associated coalition. Default `coalition.side.NEUTRAL`. +-- @param #string Name (Optional) Unique territory name. Default is the zone name. +-- @return #TERRITORY self +-- @usage +-- local north = TERRITORY:New("Territory North", coalition.side.BLUE) +-- local southZone = ZONE:FindByName("Territory South") +-- local south = TERRITORY:New(southZone, coalition.side.RED, "Southern Territory") +function TERRITORY:New(Zone, Coalition, Name) + + -- Inherit everything from BASE class. + local self = BASE:Inherit(self, BASE:New()) -- #TERRITORY + + -- Resolve a Mission Editor zone name. + if type(Zone) == "string" then + local zoneName = Zone + Zone = ZONE:FindByName(zoneName) + if not Zone then + self:E(string.format("ERROR: No ZONE found for name: %s", tostring(zoneName))) + return nil + end + elseif not Zone then + self:E("ERROR: First parameter Zone is nil in TERRITORY:New(Zone) call!") + return nil + end + + -- A territory relies only on the common ZONE_BASE interface. + if type(Zone.GetName) ~= "function" + or type(Zone.GetCoordinate) ~= "function" + or type(Zone.IsCoordinateInZone) ~= "function" then + self:E("ERROR: TERRITORY requires a ZONE_BASE derived object!") + return nil + end + + local zoneName = Zone:GetName() + local territoryName = Name or zoneName + if type(territoryName) ~= "string" or territoryName == "" then + self:E("ERROR: TERRITORY requires a non-empty name!") + return nil + end + + self.zone = Zone + self.zoneName = zoneName + self.name = territoryName + self.lid = string.format("TERRITORY %s | ", territoryName) + + if not self:SetCoalition(Coalition or coalition.side.NEUTRAL) then + return nil + end + + -- Register in the MOOSE database. + _DATABASE:AddTerritory(self) + + return self +end + +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Set functions +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Set the coalition associated with this territory. +-- This method only changes declarative ownership. It does not evaluate DCS +-- units or trigger any capture logic. +-- @param #TERRITORY self +-- @param #number Coalition Coalition side number. +-- @return #TERRITORY self or `nil` if the coalition is invalid. +function TERRITORY:SetCoalition(Coalition) + if Coalition ~= coalition.side.NEUTRAL + and Coalition ~= coalition.side.RED + and Coalition ~= coalition.side.BLUE then + self:E(self.lid .. string.format("ERROR: Invalid coalition: %s", tostring(Coalition))) + return nil + end + + self.coalition = Coalition + return self +end + +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Get functions +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Find a TERRITORY by name. +-- @param #TERRITORY self +-- @param #string Name Name of the territory. +-- @return #TERRITORY The found territory or `nil`. +function TERRITORY:FindByName(Name) + return _DATABASE:FindTerritory(Name) +end + +--- Get the territory name. +-- @param #TERRITORY self +-- @return #string Territory name. +function TERRITORY:GetName() + return self.name +end + +--- Get the underlying zone name. +-- @param #TERRITORY self +-- @return #string Zone name. +function TERRITORY:GetZoneName() + return self.zoneName +end + +--- Get the underlying MOOSE zone. +-- @param #TERRITORY self +-- @return Core.Zone#ZONE_BASE The underlying zone. +function TERRITORY:GetZone() + return self.zone +end + +--- Get the territory center coordinate. +-- @param #TERRITORY self +-- @return Core.Point#COORDINATE Territory center coordinate. +function TERRITORY:GetCoordinate() + return self.zone:GetCoordinate() +end + +--- Get the coalition associated with the territory. +-- @param #TERRITORY self +-- @return #number Coalition side number. +function TERRITORY:GetCoalition() + return self.coalition +end + +--- Get the name of the coalition associated with the territory. +-- @param #TERRITORY self +-- @return #string Coalition name. +function TERRITORY:GetCoalitionName() + return UTILS.GetCoalitionName(self.coalition) +end + +--- Get the owner of the territory. +-- This is an alias for @{#TERRITORY.GetCoalition}. +-- @param #TERRITORY self +-- @return #number Coalition side number. +function TERRITORY:GetOwner() + return self:GetCoalition() +end + +--- Get the owner coalition name. +-- This is an alias for @{#TERRITORY.GetCoalitionName}. +-- @param #TERRITORY self +-- @return #string Coalition name. +function TERRITORY:GetOwnerName() + return self:GetCoalitionName() +end + +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Zone functions +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Test whether a coordinate lies inside the territory. +-- @param #TERRITORY self +-- @param Core.Point#COORDINATE Coordinate Coordinate to test. +-- @return #boolean `true` if the coordinate lies inside the territory. +function TERRITORY:ContainsCoordinate(Coordinate) + return self.zone:IsCoordinateInZone(Coordinate) +end + +--- Test whether a Vec2 lies inside the territory. +-- @param #TERRITORY self +-- @param DCS#Vec2 Vec2 Vec2 to test. +-- @return #boolean `true` if the Vec2 lies inside the territory. +function TERRITORY:ContainsVec2(Vec2) + return self.zone:IsVec2InZone(Vec2) +end + +--- Draw the territory on the F10 map. +-- Drawing is delegated to the underlying MOOSE zone. +-- @param #TERRITORY self +-- @param #number Coalition (Optional) Coalition visibility. Default `-1` for all. +-- @param #table Color (Optional) RGB line color. +-- @param #number Alpha (Optional) Line alpha. +-- @param #table FillColor (Optional) RGB fill color. +-- @param #number FillAlpha (Optional) Fill alpha. +-- @param #number LineType (Optional) DCS line type. +-- @return #TERRITORY self +function TERRITORY:Draw(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) + self.zone:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType) + return self +end + +--- Remove the territory drawing from the F10 map. +-- @param #TERRITORY self +-- @return #TERRITORY self +function TERRITORY:Undraw() + self.zone:UndrawZone() + return self +end + +--- Remove the territory from the MOOSE database. +-- The underlying zone is not deleted. +-- @param #TERRITORY self +-- @return #TERRITORY self +function TERRITORY:Remove() + _DATABASE:DeleteTerritory(self.name) + return self +end diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index e937c92ab..20ca5666e 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -79,6 +79,7 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Tiresias.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Stratego.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ClientWatch.lua') __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Formation.lua') +__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Territory.lua') __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Airboss.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RecoveryTanker.lua' ) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index f90af9d65..91ff5d5b8 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -191,6 +191,8 @@ -- @field #boolean optionEmission Emission is on or off. -- @field #boolean optionInvisible Invisible is on/off. -- @field #boolean optionImmortal Immortal is on/off. +-- +-- @field #AUFTRAG.Summary summary Auftrag summary. -- -- @extends Core.Fsm#FSM @@ -656,6 +658,19 @@ AUFTRAG.Category={ -- @field #string DAMAGED Target was damaged. -- @field #string DESTROYED Target was destroyed. +--- Mission summary. +-- @type AUFTRAG.Summary +-- @field #boolean success If true, mission was successful. +-- @field #number Ntargets0 Number of initial targets. +-- @field #number Ntargets Number of final targets after mission is done. +-- @field #number damage Target damage in per cent. +-- @field #number Ndestroyed Number of destroyed targets. +-- @field #number Nkills Number of kills from assigned groups. +-- @field #number Nelements Number of elements assigned to mission. +-- @field #number targetLife Target life points after mission is over. +-- @field #number category Target category. +-- @field #number Ncasualties Number of own casualties. + --- Generic mission condition. -- @type AUFTRAG.Condition -- @field #function func Callback function to check for a condition. Should return a #boolean. @@ -676,7 +691,7 @@ AUFTRAG.Category={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="1.4.2" +AUFTRAG.version="1.5.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -776,6 +791,7 @@ function AUFTRAG:New(Type) self:AddTransition("*", "Cancel", AUFTRAG.Status.CANCELLED) -- Command to cancel the mission. + self:AddTransition("*", "Evaluated", "*") self:AddTransition("*", "Success", AUFTRAG.Status.SUCCESS) self:AddTransition("*", "Failed", AUFTRAG.Status.FAILED) @@ -946,6 +962,24 @@ function AUFTRAG:New(Type) -- @param #string Event Event. -- @param #string To To state. + --- Triggers the FSM event "Evaluated". + -- @function [parent=#AUFTRAG] Evaluated + -- @param #AUFTRAG self + -- @param #AUFTRAG.Summary + + --- Triggers the FSM event "Evaluated" after a delay. + -- @function [parent=#AUFTRAG] __Evaluated + -- @param #AUFTRAG self + -- @param #number delay Delay in seconds. + -- @param #AUFTRAG.Summary Summary Mission summary. + + --- On after "Evaluated" event. + -- @function [parent=#AUFTRAG] OnAfterEvaluated + -- @param #AUFTRAG self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #AUFTRAG.Summary Summary Mission summary. --- Triggers the FSM event "Success". -- @function [parent=#AUFTRAG] Success @@ -1750,6 +1784,9 @@ function AUFTRAG:NewBAI(Target, Altitude) mission.missionFraction=0.75 mission.optionROE=ENUMS.ROE.OpenFire mission.optionROT=ENUMS.ROT.PassiveDefense + + -- Evaluate result after 5 min. We might need time until the bombs have dropped and targets have been detroyed. + mission.dTevaluate=5*60 mission.categories={AUFTRAG.Category.AIRCRAFT} @@ -2303,6 +2340,9 @@ end function AUFTRAG:NewARTY(Target, Nshots, Radius, Altitude) local mission=AUFTRAG:New(AUFTRAG.Type.ARTY) + + printf("FF nshots=%s", tostring(Nshots)) + printf("FF radius=%s", tostring(Radius)) mission:_TargetFromObject(Target) @@ -2407,7 +2447,7 @@ end -- @param #AUFTRAG self -- @param Ops.OpsZone#OPSZONE OpsZone The OPS zone to capture. -- @param #number Coalition The coalition which should capture the zone for the mission to be successful. --- @param #number Speed Speed in knots. +-- @param #number Speed (Optional) Speed in knots. -- @param #number Altitude (Optional) Altitude in feet. Only for airborne units. Default 2000 feet ASL. -- @param #string Formation (Optional) Formation used by ground units during patrol. Default "Off Road". -- @param #number StayInZoneTime Stay this many seconds in the zone when done, only then drive back. @@ -4689,6 +4729,18 @@ function AUFTRAG:Evaluate() elseif successCondition then failed=false end + + self.summary={} --#AUFTRAG.Summary + self.summary.success=not failed + self.summary.damage=targetdamage + self.summary.Ntargets=Ntargets + self.summary.Ntargets0=Ntargets0 + self.summary.Ncasualties=self.Ncasualties + self.summary.Ndestroyed=self.engageTarget.Ndestroyed + self.summary.Nelements=self.Nelements + self.summary.Nkills=self.Nkills + self.summary.category=self.engageTarget:GetCategory() + self.summary.targetLife=Life -- Debug text. if self.verbose > 0 then @@ -4708,6 +4760,9 @@ function AUFTRAG:Evaluate() text=text..string.format("=========================") self:I(self.lid..text) end + + -- Trigger evaluated result and pass summary. + self:Evaluated(self.summary) -- Trigger events. if failed then @@ -5429,7 +5484,7 @@ function AUFTRAG:onafterSuccess(From, Event, To) -- Stop mission. self:T(self.lid..string.format("Mission SUCCESS! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) - self:Stop() + self:__Stop(-120) end @@ -5471,7 +5526,7 @@ function AUFTRAG:onafterFailed(From, Event, To) -- Stop mission. self:T(self.lid..string.format("Mission FAILED! Number of max repeats %d reached ==> Stopping mission!", self.repeated+1)) - self:Stop() + self:__Stop(-120) end @@ -5584,6 +5639,7 @@ function AUFTRAG:onafterRepeat(From, Event, To) self.Ngroups=0 self.Nassigned=nil self.Ndead=0 + self.summary=nil -- Update DCS mission task. Could be that the initial task (e.g. for bombing) was destroyed. Then we need to update the coordinate. self.DCStask=self:GetDCSMissionTask() @@ -5634,6 +5690,8 @@ function AUFTRAG:onafterStop(From, Event, To) -- No group data. self.groupdata={} + + self.summary=nil -- Clear pending scheduler calls. self.CallScheduler:Clear() diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index f69accb57..4467ad1b1 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -350,6 +350,18 @@ INTEL.RCS_NoseOnFraction = 0.15 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Create a new INTEL object and start the FSM. +-- @param #INTEL self +-- @return #INTEL self +function INTEL:_UpdateAgents() + + -- Filter coalition. + if self.coalition then + local coalitionname=UTILS.GetCoalitionName(self.coalition):lower() + self.detectionset:FilterCoalitions(coalitionname):FilterAlive():FilterOnce() + end + +end --- Create a new INTEL object and start the FSM. -- @param #INTEL self @@ -837,6 +849,20 @@ function INTEL:AddAgent(AgentGroup) return self end +--- Set whether the detection set, aka agents, are updated periodically. With this all alive groups of the INTEL coalition are included. +-- @param #INTEL self +-- @param #boolean switch If `true` or nil, all groups of the coalition will be added automatically to the Agent set. +-- @return #INTEL self +function INTEL:SetAgentAuto(switch) + if switch==nil then + switch=true + else + switch=false + end + self.update_detectionset=switch + return self +end + --- Enable or disable cluster analysis of detected targets. -- Targets will be grouped in coupled clusters. -- @param #INTEL self @@ -1025,6 +1051,10 @@ function INTEL:onafterStatus(From, Event, To) -- FSM state. local fsmstate=self:GetState() + + if self.update_detectionset then + self:_UpdateAgents() + end -- Fresh arrays. self.ContactsLost={} diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 22f265657..37dc72c4c 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -4691,7 +4691,7 @@ function OPSGROUP:_UpdateTask(Task, Mission) self:T(self.lid..string.format("Zone %s captured ==> Task DONE!", zoneCurr:GetName())) -- Task done. - if Task.StayInZoneTime then + if Task.StayInZoneTime and Task.StayInZoneTime>0 then local stay = Task.StayInZoneTime self:__TaskDone(stay,Task) else diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 0ad74e65d..105c75684 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -1963,6 +1963,25 @@ function AIRBASE:Register(AirbaseName) -- Category. self.category=self.descriptors and self.descriptors.category or Airbase.Category.AIRDROME + + -- Get DCS object. + local airbase=self:GetDCSObject() + + if airbase then + self.objectcategory=Object.getCategory(airbase) + else + self.objectcategory=Object.Category.BASE + end + + if self.objectcategory==Object.Category.BASE then + self.objectcategoryName="BASE" + elseif self.objectcategory==Object.Category.STATIC then + self.objectcategoryName="STATIC" + elseif self.objectcategory==Object.Category.UNIT then + self.objectcategoryName="UNIT" + else + self.objectcategoryName="OTHER" + end -- H2 is bugged --if self.AirbaseName == "H4" and self.descriptors == nil then @@ -1971,24 +1990,24 @@ function AIRBASE:Register(AirbaseName) --end -- Set category. -if self.category==Airbase.Category.AIRDROME then - self.isAirdrome=true -elseif self.category==Airbase.Category.HELIPAD or self.descriptors.typeName=="FARP_SINGLE_01" then - self.isHelipad=true - self.category=Airbase.Category.HELIPAD -elseif self.category==Airbase.Category.SHIP then - self.isShip=true - -- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects() - if self.descriptors.typeName=="Oil rig" or self.descriptors.typeName=="Ga" then + if self.category==Airbase.Category.AIRDROME then + self.isAirdrome=true + elseif self.category==Airbase.Category.HELIPAD or self.descriptors.typeName=="FARP_SINGLE_01" then self.isHelipad=true - self.isShip=false self.category=Airbase.Category.HELIPAD - _DATABASE:AddStatic(AirbaseName) + elseif self.category==Airbase.Category.SHIP then + self.isShip=true + -- DCS bug: Oil rigs and gas platforms have category=2 (ship). Also they cannot be retrieved by coalition.getStaticObjects() + if self.descriptors.typeName=="Oil rig" or self.descriptors.typeName=="Ga" then + self.isHelipad=true + self.isShip=false + self.category=Airbase.Category.HELIPAD + _DATABASE:AddStatic(AirbaseName) + end + if self:GetTypeName() == "Zell" then self.isZell = true end + else + self:E("ERROR: Unknown airbase category!") end - if self:GetTypeName() == "Zell" then self.isZell = true end -else - self:E("ERROR: Unknown airbase category!") -end -- Init Runways. self:_InitRunways() @@ -2614,7 +2633,7 @@ function AIRBASE:GetMinimumBoundingCircleFromParkingSpots(mark) local spots = self:GetParkingSpotsVec2s() if #spots == 0 then return self.AirbaseZone end local center, radius = UTILS.GetMinimumBoundingCircle(spots) - self.parkingCircle = ZONE_RADIUS:New(self.AirbaseName.." ParkingCircle",center,radius+50) + self.parkingCircle = ZONE_RADIUS:New(self.AirbaseName.." ParkingCircle", center, radius+50, true) if mark == true then self.parkingCircle:DrawZone(-1,{1,0,0},1,{0,1,0},0.2,3) end