From 7a2508bf17f9aa39b47484325ff850b0407ca431 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 6 Nov 2021 18:10:49 +0100 Subject: [PATCH] OPSTRANSPORT - Improved pre-defined paths for pickup and transport --- Moose Development/Moose/Core/Zone.lua | 41 ++--- Moose Development/Moose/Ops/OpsGroup.lua | 163 ++++++++++++++----- Moose Development/Moose/Ops/OpsTransport.lua | 151 ++++++++--------- 3 files changed, 205 insertions(+), 150 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index eec1dace9..8ca1a07bd 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1201,8 +1201,9 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes) end local function _checkSurface(point) + local stype=land.getSurfaceType(point) for _,sf in pairs(surfacetypes) do - if sf==land.getSurfaceType(point) then + if sf==stype then return true end end @@ -1212,10 +1213,15 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes) local point=_getpoint() if surfacetypes then - local N=1 ; local Nmax=1000 - while _checkSurface(point)==false and N<=Nmax do - point=_getpoint() - N=N+1 + local N=1 ; local Nmax=1000 ; local gotit=false + while gotit==false and N<=Nmax do + gotit=_checkSurface(point) + if gotit then + env.info(string.format("Got random coordinate with surface type %d after N=%d/%d iterations", land.getSurfaceType(point), N, Nmax)) + else + point=_getpoint() + N=N+1 + end end end @@ -2195,6 +2201,9 @@ end do -- ZONE_AIRBASE --- @type ZONE_AIRBASE + -- @field #boolean isShip If `true`, airbase is a ship. + -- @field #boolean isHelipad If `true`, airbase is a helipad. + -- @field #boolean isAirdrome If `true`, airbase is an airdrome. -- @extends #ZONE_RADIUS @@ -2251,9 +2260,9 @@ do -- ZONE_AIRBASE return self._.ZoneAirbase end - --- Returns the current location of the @{Wrapper.Group}. + --- Returns the current location of the AIRBASE. -- @param #ZONE_AIRBASE self - -- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. + -- @return DCS#Vec2 The location of the zone based on the AIRBASE location. function ZONE_AIRBASE:GetVec2() self:F( self.ZoneName ) @@ -2271,24 +2280,6 @@ do -- ZONE_AIRBASE return ZoneVec2 end - --- Returns a random location within the zone of the @{Wrapper.Group}. - -- @param #ZONE_AIRBASE self - -- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location. - function ZONE_AIRBASE:GetRandomVec2() - self:F( self.ZoneName ) - - local Point = {} - local Vec2 = self._.ZoneAirbase:GetVec2() - - local angle = math.random() * math.pi*2; - Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); - Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); - - self:T( { Point } ) - - return Point - end - --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. -- @param #ZONE_AIRBASE self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index ea6a20683..a5ae33c04 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -3998,18 +3998,23 @@ function OPSGROUP:CountRemainingTransports() -- Loop over mission queue. for _,_transport in pairs(self.cargoqueue) do local transport=_transport --Ops.OpsTransport#OPSTRANSPORT + + local mystatus=transport:GetCarrierTransportStatus(self) + local status=transport:GetState() -- Debug info. - self:T(self.lid..string.format("Transport status=%s [%s]", transport:GetCarrierTransportStatus(self), transport:GetState())) + self:T(self.lid..string.format("Transport my status=%s [%s]", mystatus, status)) -- Count not delivered (executing or scheduled) assignments. - if transport and transport:GetCarrierTransportStatus(self)==OPSTRANSPORT.Status.SCHEDULED and transport:GetState()~=OPSTRANSPORT.Status.DELIVERED then + if transport and mystatus==OPSTRANSPORT.Status.SCHEDULED and status~=OPSTRANSPORT.Status.DELIVERED and status~=OPSTRANSPORT.Status.CANCELLED then N=N+1 end end -- In case we directly set the cargo transport (not in queue). - if N==0 and self.cargoTransport and self.cargoTransport:GetState()~=OPSTRANSPORT.Status.DELIVERED then + if N==0 and self.cargoTransport and + self.cargoTransport:GetState()~=OPSTRANSPORT.Status.DELIVERED and self.cargoTransport:GetCarrierTransportStatus(self)~=OPSTRANSPORT.Status.DELIVERED and + self.cargoTransport:GetState()~=OPSTRANSPORT.Status.CANCELLED and self.cargoTransport:GetCarrierTransportStatus(self)~=OPSTRANSPORT.Status.CANCELLED then N=1 end @@ -6440,6 +6445,28 @@ function OPSGROUP:_AddCargobay(CargoGroup, CarrierElement, Reserved) return self end +--- Get all groups currently loaded as cargo. +-- @param #OPSGROUP self +-- @param #string CarrierName (Optional) Only return cargo groups loaded into a particular carrier unit. +-- @return #table Cargo ops groups. +function OPSGROUP:GetCargoGroups(CarrierName) + local cargos={} + + for _,_element in pairs(self.elements) do + local element=_element --#OPSGROUP.Element + if CarrierName==nil or element.name==CarrierName then + for _,_cargo in pairs(element.cargoBay) do + local cargo=_cargo --#OPSGROUP.MyCargo + if not cargo.reserved then + table.insert(cargos, cargo.group) + end + end + end + end + + return cargos +end + --- Get cargo bay item. -- @param #OPSGROUP self -- @param #OPSGROUP CargoGroup Cargo group. @@ -6655,7 +6682,7 @@ function OPSGROUP:DelOpsTransport(CargoTransport) table.remove(self.cargoqueue, i) -- Remove carrier from ops transport. - CargoTransport:_DelCarrier(self, 1) + CargoTransport:_DelCarrier(self) return self end @@ -7049,6 +7076,9 @@ end -- @param #string To To state. function OPSGROUP:onafterPickup(From, Event, To) + -- Old status. + local oldstatus=self.carrierStatus + -- Set carrier status. self:_NewCarrierStatus(OPSGROUP.CarrierStatus.PICKUP) @@ -7094,8 +7124,18 @@ function OPSGROUP:onafterPickup(From, Event, To) else + -- Set surface type of random coordinate. + local surfacetypes=nil + if self:IsArmygroup() or self:IsFlightgroup() then + surfacetypes={land.SurfaceType.LAND} + elseif self:IsNavygroup() then + surfacetypes={land.SurfaceType.WATER} + end + -- Get a random coordinate in the pickup zone and let the carrier go there. - local Coordinate=Zone:GetRandomCoordinate() + local Coordinate=Zone:GetRandomCoordinate(nil, nil, surfacetypes) + + --Coordinate:MarkToAll(string.format("Pickup coordinate for group %s [Surface type=%d]", self:GetName(), Coordinate:GetSurfaceType())) -- Add waypoint. if self:IsFlightgroup() then @@ -7151,19 +7191,29 @@ function OPSGROUP:onafterPickup(From, Event, To) local uid=cwp and cwp.uid or nil -- Get a (random) pre-defined transport path. - local path=self.cargoTransport:_GetPathPickup(self.cargoTZC) + local path=self.cargoTransport:_GetPathPickup(self.category, self.cargoTZC) - if path then - -- Loop over coordinates. - for i,coordinate in pairs(path) do - local waypoint=NAVYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true - uid=waypoint.uid - --coordinate:MarkToAll(string.format("Path i=%d, UID=%d", i, uid)) + -- Get transport path. + if path and oldstatus~=OPSGROUP.CarrierStatus.NOTCARRIER then + if path.reverse then + for i=#path.waypoints,1,-1 do + local wp=path.waypoints[i] + local coordinate=COORDINATE:NewFromWaypoint(wp) + local waypoint=NAVYGROUP.AddWaypoint(self, coordinate, nil, uid, nil, false) ; waypoint.temp=true + uid=waypoint.uid + end + else + for i=1,#path.waypoints do + local wp=path.waypoints[i] + local coordinate=COORDINATE:NewFromWaypoint(wp) + local waypoint=NAVYGROUP.AddWaypoint(self, coordinate, nil, uid, nil, false) ; waypoint.temp=true + uid=waypoint.uid + end end end -- NAVYGROUP - local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate, nil, uid, self.altitudeCruise) ; waypoint.detour=1 + local waypoint=NAVYGROUP.AddWaypoint(self, Coordinate, nil, uid, self.altitudeCruise, false) ; waypoint.detour=1 -- Give cruise command. self:__Cruise(-2) @@ -7179,21 +7229,32 @@ function OPSGROUP:onafterPickup(From, Event, To) local uid=cwp and cwp.uid or nil -- Get a (random) pre-defined transport path. - local path=self.cargoTransport:_GetPathPickup(self.cargoTZC) + local path=self.cargoTransport:_GetPathPickup(self.category, self.cargoTZC) -- Formation used to go to the pickup zone.. local Formation=self.cargoTransport:_GetFormationPickup(self.cargoTZC) + -- Get transport path. if path then - -- Loop over coordinates. - for i,coordinate in pairs(path) do - local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid, Formation) ; waypoint.temp=true - uid=waypoint.uid + if path.reverse then + for i=#path.waypoints,1,-1 do + local wp=path.waypoints[i] + local coordinate=COORDINATE:NewFromWaypoint(wp) + local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid, wp.action, false) ; waypoint.temp=true + uid=waypoint.uid + end + else + for i=1,#path.waypoints do + local wp=path.waypoints[i] + local coordinate=COORDINATE:NewFromWaypoint(wp) + local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid, wp.action, false) ; waypoint.temp=true + uid=waypoint.uid + end end end -- ARMYGROUP - local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, uid, Formation) ; waypoint.detour=1 + local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, uid, Formation, false) ; waypoint.detour=1 -- Give cruise command. self:__Cruise(-2) @@ -7452,7 +7513,7 @@ function OPSGROUP:onafterTransport(From, Event, To) else local surfacetypes=nil - if self:IsArmygroup() then + if self:IsArmygroup() or self:IsFlightgroup() then surfacetypes={land.SurfaceType.LAND} elseif self:IsNavygroup() then surfacetypes={land.SurfaceType.WATER} @@ -7460,6 +7521,8 @@ function OPSGROUP:onafterTransport(From, Event, To) -- Coord where the carrier goes to unload. local Coordinate=Zone:GetRandomCoordinate(nil, nil, surfacetypes) --Core.Point#COORDINATE + + --Coordinate:MarkToAll(string.format("Deploy coordinate for group %s [Surface type=%d]", self:GetName(), Coordinate:GetSurfaceType())) -- Add waypoint. if self:IsFlightgroup() then @@ -7507,29 +7570,34 @@ function OPSGROUP:onafterTransport(From, Event, To) local uid=cwp and cwp.uid or nil -- Get transport path. - local path=self.cargoTransport:_GetPathTransport(self.cargoTZC) + local path=self.cargoTransport:_GetPathTransport(self.category, self.cargoTZC) -- Formation used for transporting. local Formation=self.cargoTransport:_GetFormationTransport(self.cargoTZC) - - --[[ - local coordinate=self:GetCoordinate() - local pathonroad=coordinate:GetPathOnRoad(Coordinate, false, false, true) - if pathonroad then - env.info("FF got path on road") - end - ]] + -- Get transport path. if path then - -- Loop over coordinates. - for i,coordinate in pairs(path) do - local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid, Formation) ; waypoint.temp=true - uid=waypoint.uid + env.info("FF 100") + if path.reverse then + for i=#path.waypoints,1,-1 do + local wp=path.waypoints[i] + local coordinate=COORDINATE:NewFromWaypoint(wp) + local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid, wp.action, false) ; waypoint.temp=true + uid=waypoint.uid + end + else + for i=1,#path.waypoints do + local wp=path.waypoints[i] + local coordinate=COORDINATE:NewFromWaypoint(wp) + coordinate:MarkToAll("Path") + local waypoint=ARMYGROUP.AddWaypoint(self, coordinate, nil, uid, wp.action, false) ; waypoint.temp=true + uid=waypoint.uid + end end end -- ARMYGROUP - local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, self:GetWaypointCurrent().uid, Formation) ; waypoint.detour=1 + local waypoint=ARMYGROUP.AddWaypoint(self, Coordinate, nil, uid, Formation, false) ; waypoint.detour=1 -- Give cruise command. self:Cruise() @@ -7540,13 +7608,24 @@ function OPSGROUP:onafterTransport(From, Event, To) local uid=cwp and cwp.uid or nil -- Get a (random) pre-defined transport path. - local path=self.cargoTransport:_GetPathTransport(self.cargoTZC) + local path=self.cargoTransport:_GetPathTransport(self.category, self.cargoTZC) + -- Get transport path. if path then - -- Loop over coordinates. - for i,coordinate in pairs(path) do - local waypoint=NAVYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true - uid=waypoint.uid + if path.reverse then + for i=#path.waypoints,1,-1 do + local wp=path.waypoints[i] + local coordinate=COORDINATE:NewFromWaypoint(wp) + local waypoint=NAVYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true + uid=waypoint.uid + end + else + for i=1,#path.waypoints do + local wp=path.waypoints[i] + local coordinate=COORDINATE:NewFromWaypoint(wp) + local waypoint=NAVYGROUP.AddWaypoint(self, coordinate, nil, uid) ; waypoint.temp=true + uid=waypoint.uid + end end end @@ -8045,13 +8124,13 @@ function OPSGROUP:onafterTransportCancel(From, Event, To, Transport) --- -- Set mission group status. - --Transport:SetGroupStatus(self, AUFTRAG.GroupStatus.CANCELLED) + Transport:SetCarrierTransportStatus(self, AUFTRAG.GroupStatus.CANCELLED) - -- Remove transport from queue. + -- Remove transport from queue. This also removes the carrier from the transport. self:DelOpsTransport(Transport) -- Remove carrier. - Transport:_DelCarrier(self) + --Transport:_DelCarrier(self) -- Send group RTB or WAIT if nothing left to do. self:_CheckGroupDone(1) diff --git a/Moose Development/Moose/Ops/OpsTransport.lua b/Moose Development/Moose/Ops/OpsTransport.lua index 1838be0de..9f9412e13 100644 --- a/Moose Development/Moose/Ops/OpsTransport.lua +++ b/Moose Development/Moose/Ops/OpsTransport.lua @@ -175,9 +175,10 @@ OPSTRANSPORT.Status={ --- Path used for pickup or transport. -- @type OPSTRANSPORT.Path --- @field #table coords Table of coordinates. --- @field #number radius Radomization radius in meters. Default 0 m. --- @field #number altitude Altitude in feet AGL. Only for aircraft. +-- @field #table waypoints Table of waypoints. +-- @field #number category Category for which carriers this path is used. +-- @field #number radius Radomization radius for waypoints in meters. Default 0 m. +-- @field #boolean reverse If `true`, path is used in reversed order. --- Generic transport condition. -- @type OPSTRANSPORT.Condition @@ -189,15 +190,17 @@ _OPSTRANSPORTID=0 --- Army Group version. -- @field #string version -OPSTRANSPORT.version="0.5.0" +OPSTRANSPORT.version="0.6.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Trains. --- TODO: Special transport cohorts/legions. Similar to mission. --- TODO: Stop/abort transport. +-- TODO: Stop transport. +-- TODO: Improve pickup and transport paths. +-- DONE: Special transport cohorts/legions. Similar to mission. +-- DONE: Cancel transport. -- DONE: Allow multiple pickup/depoly zones. -- DONE: Add start conditions. -- DONE: Check carrier(s) dead. @@ -1011,7 +1014,7 @@ end function OPSTRANSPORT:_DelCarrier(CarrierGroup, Delay) if Delay and Delay>0 then - self:ScheduleOnce(Delay, OPSTRANSPORT._DelCarrier, CarrierGroup) + self:ScheduleOnce(Delay, OPSTRANSPORT._DelCarrier, self, CarrierGroup) else if self:IsCarrier(CarrierGroup) then @@ -1180,41 +1183,34 @@ function OPSTRANSPORT:AddConditionStart(ConditionFunction, ...) return self end ---- Add path used for transportation from the pickup to the deploy zone. If multiple paths are defined, a random one is chosen. +--- Add path used for transportation from the pickup to the deploy zone for **ground** and **naval** carriers. +-- If multiple paths are defined, a random one is chosen. The path is retrieved from the waypoints of a given group. +-- **NOTE** that the category group defines for which carriers this path is valid. +-- For example, if you specify a GROUND group to provide the waypoints, only assigned GROUND carriers will use the +-- path. -- @param #OPSTRANSPORT self -- @param Wrapper.Group#GROUP PathGroup A (late activated) GROUP defining a transport path by their waypoints. -- @param #boolean Reversed If `true`, add waypoints of group in reversed order. -- @param #number Radius Randomization radius in meters. Default 0 m. --- @param #number Altitude Altitude in feet AGL. Only for aircraft. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport Zone combo. -- @return #OPSTRANSPORT self -function OPSTRANSPORT:AddPathTransport(PathGroup, Reversed, Radius, Altitude, TransportZoneCombo) +function OPSTRANSPORT:AddPathTransport(PathGroup, Reversed, Radius, TransportZoneCombo) -- Use default TZC if no transport zone combo is provided. TransportZoneCombo=TransportZoneCombo or self.tzcDefault - local path={} --#OPSTRANSPORT.Path - path.coords={} - path.radius=Radius or 0 - path.altitude=Altitude - - -- Get route points. - local waypoints=PathGroup:GetTaskRoute() - - if Reversed then - for i=#waypoints,1,-1 do - local wp=waypoints[i] - local coord=COORDINATE:New(wp.x, wp.alt, wp.y) - table.insert(path.coords, coord) - end - else - for i=1,#waypoints do - local wp=waypoints[i] - local coord=COORDINATE:New(wp.x, wp.alt, wp.y) - table.insert(path.coords, coord) - end + if type(PathGroup)=="string" then + PathGroup=GROUP:FindByName(PathGroup) end + local path={} --#OPSTRANSPORT.Path + path.coords={} + path.category=PathGroup:GetCategory() + path.radius=Radius or 0 + path.reverse=Reversed + path.waypoints=PathGroup:GetTaskRoute() + + -- TODO: Check that only flyover waypoints are given for aircraft. -- Add path. table.insert(TransportZoneCombo.TransportPaths, path) @@ -1224,9 +1220,10 @@ end --- Get a path for transportation. -- @param #OPSTRANSPORT self +-- @param #number Category Group category. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport Zone combo. --- @return #table The path of COORDINATEs. -function OPSTRANSPORT:_GetPathTransport(TransportZoneCombo) +-- @return #OPSTRANSPORT.Path The path object. +function OPSTRANSPORT:_GetPathTransport(Category, TransportZoneCombo) -- Use default TZC if no transport zone combo is provided. TransportZoneCombo=TransportZoneCombo or self.tzcDefault @@ -1235,21 +1232,21 @@ function OPSTRANSPORT:_GetPathTransport(TransportZoneCombo) if pathsTransport and #pathsTransport>0 then - -- Get a random path for transport. - local path=pathsTransport[math.random(#pathsTransport)] --#OPSTRANSPORT.Path - + local paths={} - local coordinates={} - for _,_coord in ipairs(path.coords) do - local coord=_coord --Core.Point#COORDINATE - - -- Get random coordinate. - local c=coord:GetRandomCoordinateInRadius(path.radius) - - table.insert(coordinates, c) + for _,_path in pairs(pathsTransport) do + local path=_path --#OPSTRANSPORT.Path + if path.category==Category then + table.insert(paths, path) + end + end + + if #paths>0 then + + local path=paths[math.random(#paths)] --#OPSTRANSPORT.Path + + return path end - - return coordinates end return nil @@ -1261,35 +1258,22 @@ end -- @param Wrapper.Group#GROUP PathGroup A (late activated) GROUP defining a transport path by their waypoints. -- @param #boolean Reversed If `true`, add waypoints of group in reversed order. -- @param #number Radius Randomization radius in meters. Default 0 m. --- @param #number Altitude Altitude in feet AGL. Only for aircraft. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport Zone combo. -- @return #OPSTRANSPORT self -function OPSTRANSPORT:AddPathPickup(PathGroup, Reversed, Radius, Altitude, TransportZoneCombo) +function OPSTRANSPORT:AddPathPickup(PathGroup, Reversed, Radius, TransportZoneCombo) -- Use default TZC if no transport zone combo is provided. TransportZoneCombo=TransportZoneCombo or self.tzcDefault - local path={} --#OPSTRANSPORT.Path - path.coords={} - path.radius=Radius or 0 - path.altitude=Altitude - - -- Get route points. - local waypoints=PathGroup:GetTaskRoute() - - if Reversed then - for i=#waypoints,1,-1 do - local wp=waypoints[i] - local coord=COORDINATE:New(wp.x, wp.alt, wp.y) - table.insert(path.coords, coord) - end - else - for i=1,#waypoints do - local wp=waypoints[i] - local coord=COORDINATE:New(wp.x, wp.alt, wp.y) - table.insert(path.coords, coord) - end + if type(PathGroup)=="string" then + PathGroup=GROUP:FindByName(PathGroup) end + + local path={} --#OPSTRANSPORT.Path + path.category=PathGroup:GetCategory() + path.radius=Radius or 0 + path.reverse=Reversed + path.waypoints=PathGroup:GetTaskRoute() -- Add path. table.insert(TransportZoneCombo.PickupPaths, path) @@ -1299,32 +1283,33 @@ end --- Get a path for pickup. -- @param #OPSTRANSPORT self +-- @param #number Category Group category. -- @param #OPSTRANSPORT.TransportZoneCombo TransportZoneCombo Transport Zone combo. -- @return #table The path of COORDINATEs. -function OPSTRANSPORT:_GetPathPickup(TransportZoneCombo) +function OPSTRANSPORT:_GetPathPickup(Category, TransportZoneCombo) -- Use default TZC if no transport zone combo is provided. TransportZoneCombo=TransportZoneCombo or self.tzcDefault - local paths=TransportZoneCombo.PickupPaths + local Paths=TransportZoneCombo.PickupPaths - if paths and #paths>0 then + if Paths and #Paths>0 then - -- Get a random path for transport. - local path=paths[math.random(#paths)] --#OPSTRANSPORT.Path - + local paths={} - local coordinates={} - for _,_coord in ipairs(path.coords) do - local coord=_coord --Core.Point#COORDINATE - - -- Get random coordinate. - local c=coord:GetRandomCoordinateInRadius(path.radius) - - table.insert(coordinates, c) + for _,_path in pairs(Paths) do + local path=_path --#OPSTRANSPORT.Path + if path.category==Category then + table.insert(paths, path) + end + end + + if #paths>0 then + + local path=paths[math.random(#paths)] --#OPSTRANSPORT.Path + + return path end - - return coordinates end return nil