From cdd240abb7b03cdd2ce407853e2a25fabc1696b0 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 2 Jan 2023 14:28:35 +0100 Subject: [PATCH 1/6] PseudoATC - Option to display playername (#1870) Use `myatc:SetReportPlayername()` to switch this on #1864 --- .../Moose/Functional/PseudoATC.lua | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 0cf76281f..67b86c5fe 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -45,6 +45,7 @@ -- @field #number talt Interval in seconds between reporting altitude until touchdown. Default 3 sec. -- @field #boolean chatty Display some messages on events like take-off and touchdown. -- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. +-- @field #boolean reportplayername If true, use playername not callsign on callouts -- @extends Core.Base#BASE --- Adds some rudimentary ATC functionality via the radio menu. @@ -88,6 +89,7 @@ PSEUDOATC={ talt=3, chatty=true, eventsmoose=true, + reportplayername = false, } ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -98,7 +100,7 @@ PSEUDOATC.id="PseudoATC | " --- PSEUDOATC version. -- @field #number version -PSEUDOATC.version="0.9.2" +PSEUDOATC.version="0.9.3" ----------------------------------------------------------------------------------------------------------------------------------------- @@ -183,6 +185,13 @@ function PSEUDOATC:SetMessageDuration(duration) self.mdur=duration or 30 end +--- Use player name, not call sign, in callouts +-- @param #PSEUDOATC self +function PSEUDOATC:SetReportPlayername() + self.reportplayername = true + return self +end + --- Set time interval after which the F10 radio menu is refreshed. -- @param #PSEUDOATC self -- @param #number interval Interval in seconds. Default is every 120 sec. @@ -485,6 +494,9 @@ function PSEUDOATC:PlayerTakeOff(unit, place) -- Bye-Bye message. if place and self.chatty then local text=string.format("%s, %s, you are airborne. Have a safe trip!", place, CallSign) + if self.reportplayername then + text=string.format("%s, %s, you are airborne. Have a safe trip!", place, PlayerName) + end MESSAGE:New(text, self.mdur):ToGroup(group) end @@ -844,7 +856,8 @@ function PSEUDOATC:ReportHeight(GID, UID, dt, _clear) local position=unit:GetCoordinate() local height=get_AGL(position) local callsign=unit:GetCallsign() - + local PlayerName=self.group[GID].player[UID].playername + -- Settings. local settings=_DATABASE:GetPlayerSettings(self.group[GID].player[UID].playername) or _SETTINGS --Core.Settings#SETTINGS @@ -856,7 +869,9 @@ function PSEUDOATC:ReportHeight(GID, UID, dt, _clear) -- Message text. local _text=string.format("%s, your altitude is %s AGL.", callsign, Hs) - + if self.reportplayername then + _text=string.format("%s, your altitude is %s AGL.", PlayerName, Hs) + end -- Append flight level. if _clear==false then _text=_text..string.format(" FL%03d.", position.y/30.48) From 3fbfb8b5284820187dad7f473dfe049c773bd200 Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Mon, 2 Jan 2023 17:27:01 +0100 Subject: [PATCH 2/6] #UNIT * Fix #1865 --- Moose Development/Moose/Wrapper/Unit.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 8cf7c9226..596f670e4 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -229,7 +229,7 @@ function UNIT:ReSpawnAt( Coordinate, Heading ) SpawnGroupTemplate.y = Coordinate.z self:F( #SpawnGroupTemplate.units ) - for UnitID, UnitData in pairs( SpawnGroup:GetUnits() ) do + for UnitID, UnitData in pairs( SpawnGroup:GetUnits() or {} ) do local GroupUnit = UnitData -- #UNIT self:F( GroupUnit:GetName() ) if GroupUnit:IsAlive() then From b0eef34146edb5d76020698389a21a4e97fcd59c Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 3 Jan 2023 10:14:34 +0100 Subject: [PATCH 3/6] #CONTROLLABLE * Docu fix #WAREHOUSE * Changes from dev #ZONE * Additions to ZONE_POLYGON --- Moose Development/Moose/Core/Zone.lua | 72 ++++++ .../Moose/Functional/Warehouse.lua | 236 +++++++++++------- .../Moose/Wrapper/Controllable.lua | 2 +- 3 files changed, 215 insertions(+), 95 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 5f89fdaed..70495842d 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -2084,6 +2084,52 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph return self end + +--- Get the smallest circular zone encompassing all points points of the polygon zone. +-- @param #ZONE_POLYGON_BASE self +-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone. +-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered. +-- @return #ZONE_RADIUS The circular zone. +function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone) + + local center=self:GetVec2() + + local radius=0 + + for _,_vec2 in pairs(self._.Polygon) do + local vec2=_vec2 --DCS#Vec2 + + local r=UTILS.VecDist2D(center, vec2) + + if r>radius then + radius=r + end + + end + + local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName, center, radius, DoNotRegisterZone) + + return zone +end + + +--- Get the smallest rectangular zone encompassing all points points of the polygon zone. +-- @param #ZONE_POLYGON_BASE self +-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone. +-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered. +-- @return #ZONE_POLYGON The rectangular zone. +function ZONE_POLYGON_BASE:GetZoneQuad(ZoneName, DoNotRegisterZone) + + local vec1, vec3=self:GetBoundingVec2() + + local vec2={x=vec1.x, y=vec3.y} + local vec4={x=vec3.x, y=vec1.y} + + local zone=ZONE_POLYGON_BASE:New(ZoneName or self.ZoneName, {vec1, vec2, vec3, vec4}) + + return zone +end + --- Smokes the zone boundaries in a color. -- @param #ZONE_POLYGON_BASE self -- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. @@ -2286,6 +2332,32 @@ function ZONE_POLYGON_BASE:GetBoundingSquare() return { x1 = x1, y1 = y1, x2 = x2, y2 = y2 } end +--- Get the bounding 2D vectors of the polygon. +-- @param #ZONE_POLYGON_BASE self +-- @return DCS#Vec2 Coordinates of western-southern-lower vertex of the box. +-- @return DCS#Vec2 Coordinates of eastern-northern-upper vertex of the box. +function ZONE_POLYGON_BASE:GetBoundingVec2() + + local x1 = self._.Polygon[1].x + local y1 = self._.Polygon[1].y + local x2 = self._.Polygon[1].x + local y2 = self._.Polygon[1].y + + for i = 2, #self._.Polygon do + self:T2( { self._.Polygon[i], x1, y1, x2, y2 } ) + x1 = ( x1 > self._.Polygon[i].x ) and self._.Polygon[i].x or x1 + x2 = ( x2 < self._.Polygon[i].x ) and self._.Polygon[i].x or x2 + y1 = ( y1 > self._.Polygon[i].y ) and self._.Polygon[i].y or y1 + y2 = ( y2 < self._.Polygon[i].y ) and self._.Polygon[i].y or y2 + + end + + local vec1={x=x1, y=y1} + local vec2={x=x2, y=y2} + + return vec1, vec2 +end + --- Draw a frontier on the F10 map with small filled circles. -- @param #ZONE_POLYGON_BASE self -- @param #number Coalition (Optional) Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1= All. diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 26cef4db9..8a1dfcd95 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -302,8 +302,8 @@ -- -- Initial Spawn states is as follows: -- GROUND: ROE, "Return Fire" Alarm, "Green" --- AIR: ROE, "Return Fire" Reaction to Threat, "Passive Defense" --- NAVAL ROE, "Return Fire" Alarm,"N/A" +-- AIR: ROE, "Return Fire" Reaction to Threat, "Passive Defense" +-- NAVAL ROE, "Return Fire" Alarm,"N/A" -- -- A request can be added by the @{#WAREHOUSE.AddRequest}(*warehouse*, *AssetDescriptor*, *AssetDescriptorValue*, *nAsset*, *TransportType*, *nTransport*, *Prio*, *Assignment*) function. -- The parameters are @@ -2647,6 +2647,13 @@ function WAREHOUSE:SetWarehouseZone(zone) return self end +--- Get the warehouse zone. +-- @param #WAREHOUSE self +-- @return Core.Zone#ZONE The warehouse zone. +function WAREHOUSE:GetWarehouseZone() + return self.zone +end + --- Set auto defence on. When the warehouse is under attack, all ground assets are spawned automatically and will defend the warehouse zone. -- @param #WAREHOUSE self -- @return #WAREHOUSE self @@ -5810,6 +5817,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. local Parking={} if Request.cargocategory==Group.Category.AIRPLANE or Request.cargocategory==Group.Category.HELICOPTER then + --TODO: Check for airstart. Should be a request property. Parking=self:_FindParkingForAssets(self.airbase, cargoassets) or {} end @@ -6069,7 +6077,9 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol end if self.Debug then - coord:MarkToAll(string.format("Spawnplace unit %s terminal %d.", unit.name, terminal)) + local text=string.format("Spawnplace unit %s terminal %d.", unit.name, terminal) + coord:MarkToAll(text) + env.info(text) end unit.x=coord.x @@ -7374,6 +7384,7 @@ function WAREHOUSE:_CheckRequestNow(request) local _transports local _assetattribute local _assetcategory + local _assetairstart=false -- Check if at least one (cargo) asset is available. if _nassets>0 then @@ -7381,21 +7392,28 @@ function WAREHOUSE:_CheckRequestNow(request) -- Get the attibute of the requested asset. _assetattribute=_assets[1].attribute _assetcategory=_assets[1].category + _assetairstart=_assets[1].takeoffType and _assets[1].takeoffType==COORDINATE.WaypointType.TurningPoint or false -- Check available parking for air asset units. if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then if self.airbase and self.airbase:GetCoalition()==self:GetCoalition() then - if self:IsRunwayOperational() then + if self:IsRunwayOperational() or _assetairstart then - local Parking=self:_FindParkingForAssets(self.airbase,_assets) - - --if Parking==nil and not (self.category==Airbase.Category.HELIPAD) then - if Parking==nil then - local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all requested assets at the moment.", self.alias) - self:_InfoMessage(text, 5) - return false + if _assetairstart then + -- Airstart no need to check parking + else + + -- Check parking. + local Parking=self:_FindParkingForAssets(self.airbase,_assets) + + -- No parking? + if Parking==nil then + local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all requested assets at the moment.", self.alias) + self:_InfoMessage(text, 5) + return false + end end else @@ -7969,93 +7987,123 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Loop over all assets that need a parking psot. for _,asset in pairs(assets) do local _asset=asset --#WAREHOUSE.Assetitem - - -- Get terminal type of this asset - local terminaltype=asset.terminalType or self:_GetTerminal(asset.attribute, self:GetAirbaseCategory()) - - -- Asset specific parking. - parking[_asset.uid]={} - - -- Loop over all units - each one needs a spot. - for i=1,_asset.nunits do - -- Asset name - local assetname=_asset.spawngroupname.."-"..tostring(i) - - -- Loop over all parking spots. - local gotit=false - for _,_parkingspot in pairs(parkingdata) do - local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot - - -- Check correct terminal type for asset. We don't want helos in shelters etc. - if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) and self:_CheckParkingValid(parkingspot) and self:_CheckParkingAsset(parkingspot, asset) and airbase:_CheckParkingLists(parkingspot.TerminalID) then - - -- Coordinate of the parking spot. - local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE - local _termid=parkingspot.TerminalID - local free=true - local problem=nil - - -- Loop over all obstacles. - for _,obstacle in pairs(obstacles) do - - -- Check if aircraft overlaps with any obstacle. - local dist=_spot:Get2DDistance(obstacle.coord) - local safe=_overlap(_asset.size, obstacle.size, dist) - - -- Spot is blocked. - if not safe then - self:T3(self.lid..string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is NOT SAFE", assetname, _asset.uid, _termid, dist)) - free=false - problem=obstacle - problem.dist=dist - break - else - --env.info(string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is SAFE", assetname, _asset.uid, _termid, dist)) - end - - end - - -- Check if spot is free - if free then - - -- Add parkingspot for this asset unit. - table.insert(parking[_asset.uid], parkingspot) - - -- Debug - self:T(self.lid..string.format("Parking spot %d is free for asset %s [id=%d]!", _termid, assetname, _asset.uid)) - - -- Add the unit as obstacle so that this spot will not be available for the next unit. - table.insert(obstacles, {coord=_spot, size=_asset.size, name=assetname, type="asset"}) - - gotit=true - break + if not _asset.spawned then + -- Get terminal type of this asset + local terminaltype=asset.terminalType or self:_GetTerminal(asset.attribute, self:GetAirbaseCategory()) + + -- Asset specific parking. + parking[_asset.uid]={} + + -- Loop over all units - each one needs a spot. + for i=1,_asset.nunits do + + -- Asset name + local assetname=_asset.spawngroupname.."-"..tostring(i) + + -- Loop over all parking spots. + local gotit=false + for _,_parkingspot in pairs(parkingdata) do + local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot + + -- Parking valid? + local valid=true + + if asset.parkingIDs then + -- If asset has assigned parking spots, we take these no matter what. + valid=self:_CheckParkingAsset(parkingspot, asset) else - - -- Debug output for occupied spots. - if self.Debug then - local coord=problem.coord --Core.Point#COORDINATE - local text=string.format("Obstacle %s [type=%s] blocking spot=%d! Size=%.1f m and distance=%.1f m.", problem.name, problem.type, _termid, problem.size, problem.dist) - self:I(self.lid..text) - coord:MarkToAll(string.format(text)) - else - self:T(self.lid..string.format("Parking spot %d is occupied or not big enough!", _termid)) - end - + + -- Valid terminal type depending on attribute. + local validTerminal=AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) + + -- Valid parking list. + local validParking=self:_CheckParkingValid(parkingspot) + + -- Black and white list. + local validBWlist=airbase:_CheckParkingLists(parkingspot.TerminalID) + + -- Debug info. + --env.info(string.format("FF validTerminal = %s", tostring(validTerminal))) + --env.info(string.format("FF validParking = %s", tostring(validParking))) + --env.info(string.format("FF validBWlist = %s", tostring(validBWlist))) + + -- Check if all are true + valid=validTerminal and validParking and validBWlist end - - else - self:T2(self.lid..string.format("Terminal ID=%d: type=%s not supported", parkingspot.TerminalID, parkingspot.TerminalType)) - end -- check terminal type - end -- loop over parking spots - - -- No parking spot for at least one asset :( - if not gotit then - self:I(self.lid..string.format("WARNING: No free parking spot for asset %s [id=%d]", assetname, _asset.uid)) - return nil - end - end -- loop over asset units + + + -- Check correct terminal type for asset. We don't want helos in shelters etc. + if valid then + + -- Coordinate of the parking spot. + local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE + local _termid=parkingspot.TerminalID + local free=true + local problem=nil + + -- Loop over all obstacles. + for _,obstacle in pairs(obstacles) do + + -- Check if aircraft overlaps with any obstacle. + local dist=_spot:Get2DDistance(obstacle.coord) + local safe=_overlap(_asset.size, obstacle.size, dist) + + -- Spot is blocked. + if not safe then + self:T3(self.lid..string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is NOT SAFE", assetname, _asset.uid, _termid, dist)) + free=false + problem=obstacle + problem.dist=dist + break + else + --env.info(string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is SAFE", assetname, _asset.uid, _termid, dist)) + end + + end + + -- Check if spot is free + if free then + + -- Add parkingspot for this asset unit. + table.insert(parking[_asset.uid], parkingspot) + + -- Debug + self:T(self.lid..string.format("Parking spot %d is free for asset %s [id=%d]!", _termid, assetname, _asset.uid)) + + -- Add the unit as obstacle so that this spot will not be available for the next unit. + table.insert(obstacles, {coord=_spot, size=_asset.size, name=assetname, type="asset"}) + + gotit=true + break + + else + + -- Debug output for occupied spots. + if self.Debug then + local coord=problem.coord --Core.Point#COORDINATE + local text=string.format("Obstacle %s [type=%s] blocking spot=%d! Size=%.1f m and distance=%.1f m.", problem.name, problem.type, _termid, problem.size, problem.dist) + self:I(self.lid..text) + coord:MarkToAll(string.format(text)) + else + self:T(self.lid..string.format("Parking spot %d is occupied or not big enough!", _termid)) + end + + end + + else + self:T2(self.lid..string.format("Terminal ID=%d: type=%s not supported", parkingspot.TerminalID, parkingspot.TerminalType)) + end -- check terminal type + end -- loop over parking spots + + -- No parking spot for at least one asset :( + if not gotit then + self:I(self.lid..string.format("WARNING: No free parking spot for asset %s [id=%d]", assetname, _asset.uid)) + return nil + end + end -- loop over asset units + end -- Asset spawned check end -- loop over asset groups return parking diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 5cf5b4ac2..44e95efe2 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1416,7 +1416,7 @@ end -- @param #CONTROLLABLE self -- @param Core.Zone#ZONE Zone The zone where to land. -- @param #number Duration The duration in seconds to stay on the ground. --- @return #CONTROLLABLE self +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskLandAtZone( Zone, Duration, RandomPoint ) -- Get landing point From 88a837b7698536483c136dba39ef8b3ba3788b4a Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 3 Jan 2023 10:15:35 +0100 Subject: [PATCH 4/6] #FlightGroup * Added options to allow afterburner, jettison of empty tanks and jettison of weapons --- Moose Development/Moose/Ops/FlightGroup.lua | 61 +++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 246f5312c..ec1189049 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -56,6 +56,9 @@ -- @field #number RTBRecallCount Number that counts RTB calls. -- @field Ops.FlightControl#FLIGHTCONTROL.HoldingStack stack Holding stack. -- @field #boolean isReadyTO Flight is ready for takeoff. This is for FLIGHTCONTROL. +-- @field #boolean prohibitAB Disallow (true) or allow (false) AI to use the afterburner. +-- @field #boolean jettisonEmptyTanks Allow (true) or disallow (false) AI to jettison empty fuel tanks. +-- @field #boolean jettisonWeapons Allow (true) or disallow (false) AI to jettison weapons if in danger. -- -- @extends Ops.OpsGroup#OPSGROUP @@ -145,6 +148,9 @@ FLIGHTGROUP = { RTBRecallCount = 0, playerSettings = {}, playerWarnings = {}, + prohibitAB = true, + jettisonEmptyTanks = true, + jettisonWeapons = true, -- that's actually a negative option like prohibitAB } @@ -210,7 +216,7 @@ FLIGHTGROUP.Players={} --- FLIGHTGROUP class version. -- @field #string version -FLIGHTGROUP.version="0.8.2" +FLIGHTGROUP.version="0.8.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -410,6 +416,52 @@ function FLIGHTGROUP:SetVTOL() return self end +--- Set if aircraft is **not** allowed to use afterburner. +-- @param #FLIGHTGROUP self +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetProhibitAfterburner() + self.prohibitAB = true + if self:GetGroup():IsAlive() then + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB, true) + end + return self +end + +--- Set if aircraft is allowed to use afterburner. +-- @param #FLIGHTGROUP self +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetAllowAfterburner() + self.prohibitAB = false + if self:GetGroup():IsAlive() then + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB, false) + end + return self +end + +--- Set if aircraft is allowed to drop empty fuel tanks - set to true to allow, and false to forbid it. +-- @param #FLIGHTGROUP self +-- @param #boolean Switch true or false +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetJettisonEmptyTanks(Switch) + self.jettisonEmptyTanks = Switch + if self:GetGroup():IsAlive() then + self:GetGroup():SetOption(AI.Option.Air.id.JETT_TANKS_IF_EMPTY, Switch) + end + return self +end + +--- Set if aircraft is allowed to drop weapons to escape danger - set to true to allow, and false to forbid it. +-- @param #FLIGHTGROUP self +-- @param #boolean Switch true or false +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:SetJettisonWeapons(Switch) + self.jettisonWeapons = not Switch + if self:GetGroup():IsAlive() then + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT, not Switch) + end + return self +end + --- Set if group is ready for taxi/takeoff if controlled by a `FLIGHTCONTROL`. -- @param #FLIGHTGROUP self -- @param #boolean ReadyTO If `true`, flight is ready for takeoff. @@ -1759,9 +1811,10 @@ function FLIGHTGROUP:onafterSpawned(From, Event, To) end -- TODO: make this input. - self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT, true) - self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB, true) -- Does not seem to work. AI still used the after burner. - self:GetGroup():SetOption(AI.Option.Air.id.RTB_ON_BINGO, false) + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_JETT, self.jettisonWeapons) + self:GetGroup():SetOption(AI.Option.Air.id.PROHIBIT_AB, self.prohibitAB) -- Does not seem to work. AI still used the after burner. + self:GetGroup():SetOption(AI.Option.Air.id.RTB_ON_BINGO, false) + self:GetGroup():SetOption(AI.Option.Air.id.JETT_TANKS_IF_EMPTY, self.jettisonEmptyTanks) --self.group:SetOption(AI.Option.Air.id.RADAR_USING, AI.Option.Air.val.RADAR_USING.FOR_CONTINUOUS_SEARCH) -- Update route. From 793c0d988eb91fb3125de814f208411fe8ae895b Mon Sep 17 00:00:00 2001 From: Applevangelist Date: Tue, 3 Jan 2023 10:22:10 +0100 Subject: [PATCH 5/6] Changes from dev --- Moose Development/Moose/Core/Set.lua | 644 ++++++++++++++++++-- Moose Development/Moose/Utilities/Utils.lua | 76 ++- 2 files changed, 663 insertions(+), 57 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 82a7d65a5..b973172c1 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -53,6 +53,7 @@ do -- SET_BASE -- @field #table Index Table of indices. -- @field #table List Unused table. -- @field Core.Scheduler#SCHEDULER CallScheduler + -- @field #SET_BASE.Filters Filter Filters -- @extends Core.Base#BASE --- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects. @@ -83,6 +84,11 @@ do -- SET_BASE YieldInterval = nil, } + --- Filters + -- @type SET_BASE.Filters + -- @field #table Coalition Coalitions + -- @field #table Prefix Prefixes. + --- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_BASE self -- @return #SET_BASE @@ -135,11 +141,12 @@ do -- SET_BASE --- Clear the Objects in the Set. -- @param #SET_BASE self + -- @param #boolean TriggerEvent If `true`, an event remove is triggered for each group that is removed from the set. -- @return #SET_BASE self - function SET_BASE:Clear() + function SET_BASE:Clear(TriggerEvent) for Name, Object in pairs( self.Set ) do - self:Remove( Name ) + self:Remove( Name, not TriggerEvent ) end return self @@ -166,7 +173,7 @@ do -- SET_BASE --- Gets a list of the Names of the Objects in the Set. -- @param #SET_BASE self - -- @return #SET_BASE self + -- @return #table Table of names. function SET_BASE:GetSetNames() -- R2.3 self:F2() @@ -181,7 +188,7 @@ do -- SET_BASE --- Returns a table of the Objects in the Set. -- @param #SET_BASE self - -- @return #SET_BASE self + -- @return #table Table of objects. function SET_BASE:GetSetObjects() -- R2.3 self:F2() @@ -197,16 +204,21 @@ do -- SET_BASE --- Removes a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name. -- @param #SET_BASE self -- @param #string ObjectName - -- @param NoTriggerEvent (Optional) When `true`, the :Remove() method will not trigger a **Removed** event. + -- @param #boolean NoTriggerEvent (Optional) When `true`, the :Remove() method will not trigger a **Removed** event. function SET_BASE:Remove( ObjectName, NoTriggerEvent ) self:F2( { ObjectName = ObjectName } ) local TriggerEvent = true - if NoTriggerEvent then TriggerEvent = false end + if NoTriggerEvent then + TriggerEvent = false + else + TriggerEvent = true + end local Object = self.Set[ObjectName] if Object then + for Index, Key in ipairs( self.Index ) do if Key == ObjectName then table.remove( self.Index, Index ) @@ -214,6 +226,7 @@ do -- SET_BASE break end end + -- When NoTriggerEvent is true, then no Removed event will be triggered. if TriggerEvent then self:Removed( ObjectName, Object ) @@ -311,7 +324,6 @@ do -- SET_BASE -- @param #SET_BASE self -- @param Core.Set#SET_BASE SetB Set other set, called *B*. -- @return Core.Set#SET_BASE A set of objects that is included in set *A* **and** in set *B*. - function SET_BASE:GetSetIntersection(SetB) local intersection=SET_BASE:New() @@ -461,16 +473,32 @@ do -- SET_BASE -- @param #SET_BASE self -- @return #SET_BASE self function SET_BASE:FilterOnce() + + --self:Clear() for ObjectName, Object in pairs( self.Database ) do if self:IsIncludeObject( Object ) then self:Add( ObjectName, Object ) + else + self:Remove(ObjectName, true) end end return self end + + --- Clear all filters. You still need to apply :FilterOnce() + -- @param #SET_BASE self + -- @return #SET_BASE self + function SET_BASE:FilterClear() + + for key,value in pairs(self.Filter) do + self.Filter[key]={} + end + + return self + end --- Starts the filtering for the defined collection. -- @param #SET_BASE self @@ -817,7 +845,7 @@ do -- SET_BASE --- Decides whether an object is in the SET -- @param #SET_BASE self -- @param #table Object - -- @return #SET_BASE self + -- @return #boolean `true` if object is in set and `false` otherwise. function SET_BASE:IsInSet( Object ) self:F3( Object ) local outcome = false @@ -1021,9 +1049,9 @@ do -- SET_GROUP return self end - --- Gets the Set. + --- Get a *new* set that only contains alive groups. -- @param #SET_GROUP self - -- @return #table Table of objects + -- @return #SET_GROUP Set of alive groups. function SET_GROUP:GetAliveSet() self:F2() @@ -1169,11 +1197,14 @@ do -- SET_GROUP --- Builds a set of groups in zones. -- @param #SET_GROUP self -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE + -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_GROUP self - function SET_GROUP:FilterZones( Zones ) - if not self.Filter.Zones then + function SET_GROUP:FilterZones( Zones, Clear ) + + if Clear or not self.Filter.Zones then self.Filter.Zones = {} end + local zones = {} if Zones.ClassName and Zones.ClassName == "SET_ZONE" then zones = Zones.Set @@ -1183,34 +1214,12 @@ do -- SET_GROUP else zones = Zones end + for _, Zone in pairs( zones ) do local zonename = Zone:GetName() self.Filter.Zones[zonename] = Zone end - return self - end - - --- Builds a set of groups in zones. - -- @param #SET_GROUP self - -- @param #table Zones Table of Core.Zone#ZONE Zone objects, or a Core.Set#SET_ZONE - -- @return #SET_GROUP self - function SET_GROUP:FilterZones( Zones ) - if not self.Filter.Zones then - self.Filter.Zones = {} - end - local zones = {} - if Zones.ClassName and Zones.ClassName == "SET_ZONE" then - zones = Zones.Set - elseif type( Zones ) ~= "table" or (type( Zones ) == "table" and Zones.ClassName ) then - self:E("***** FilterZones needs either a table of ZONE Objects or a SET_ZONE as parameter!") - return self - else - zones = Zones - end - for _,Zone in pairs( zones ) do - local zonename = Zone:GetName() - self.Filter.Zones[zonename] = Zone - end + return self end @@ -1218,17 +1227,21 @@ do -- SET_GROUP -- Possible current coalitions are red, blue and neutral. -- @param #SET_GROUP self -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". + -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_GROUP self - function SET_GROUP:FilterCoalitions( Coalitions ) - if not self.Filter.Coalitions then + function SET_GROUP:FilterCoalitions( Coalitions, Clear ) + + if Clear or (not self.Filter.Coalitions) then self.Filter.Coalitions = {} end - if type( Coalitions ) ~= "table" then - Coalitions = { Coalitions } - end + + -- Ensure table. + Coalitions = UTILS.EnsureTable(Coalitions, false) + for CoalitionID, Coalition in pairs( Coalitions ) do self.Filter.Coalitions[Coalition] = Coalition end + return self end @@ -1236,17 +1249,22 @@ do -- SET_GROUP -- Possible current categories are plane, helicopter, ground, ship. -- @param #SET_GROUP self -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". + -- @param #boolean Clear If `true`, clear any previously defined filters. -- @return #SET_GROUP self - function SET_GROUP:FilterCategories( Categories ) - if not self.Filter.Categories then + function SET_GROUP:FilterCategories( Categories, Clear ) + + if Clear or not self.Filter.Categories then self.Filter.Categories = {} end + if type( Categories ) ~= "table" then Categories = { Categories } end + for CategoryID, Category in pairs( Categories ) do self.Filter.Categories[Category] = Category end + return self end @@ -1899,6 +1917,41 @@ do -- SET_GROUP end end + + --- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search. + -- @param #SET_GROUP self + -- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined. + -- @return Wrapper.Group#GROUP The closest group (if any). + -- @return #number Distance in meters to the closest group. + function SET_GROUP:GetClosestGroup(Coordinate, Coalitions) + + local Set = self:GetSet() + + local dmin=math.huge + local gmin=nil + + for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP + local group=GroupData --Wrapper.Group#GROUP + + if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then + + local coord=group:GetCoord() + + -- Distance between ref. coordinate and group coordinate. + local d=UTILS.VecDist3D(Coordinate, coord) + + if d Date: Tue, 3 Jan 2023 10:33:51 +0100 Subject: [PATCH 6/6] #FG * make us AB the default --- Moose Development/Moose/Ops/FlightGroup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index ec1189049..b46109e29 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -148,7 +148,7 @@ FLIGHTGROUP = { RTBRecallCount = 0, playerSettings = {}, playerWarnings = {}, - prohibitAB = true, + prohibitAB = false, jettisonEmptyTanks = true, jettisonWeapons = true, -- that's actually a negative option like prohibitAB }