diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index e699af694..da8ed7835 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2389,7 +2389,6 @@ do -- COORDINATE end --- Creates a free form shape on the F10 map. The first point is the current COORDINATE. The remaining points need to be specified. - -- **NOTE**: A free form polygon must have **at least three points** in total and currently only **up to 15 points** in total are supported. -- @param #COORDINATE self -- @param #table Coordinates Table of coordinates of the remaining points of the shape. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. @@ -2463,9 +2462,30 @@ do -- COORDINATE vecs[11], vecs[12], vecs[13], vecs[14], vecs[15], Color, FillColor, LineType, ReadOnly, Text or "") else - self:E("ERROR: Currently a free form polygon can only have 15 points in total!") + -- Unfortunately, unpack(vecs) does not work! So no idea how to generalize this :( - trigger.action.markupToAll(7, Coalition, MarkID, unpack(vecs), Color, FillColor, LineType, ReadOnly, Text or "") + --trigger.action.markupToAll(7, Coalition, MarkID, unpack(vecs), Color, FillColor, LineType, ReadOnly, Text or "") + + -- Write command as string and execute that. Idea by Grimes https://forum.dcs.world/topic/324201-mark-to-all-function/#comment-5273793 + local s=string.format("trigger.action.markupToAll(7, %d, %d,", Coalition, MarkID) + for _,vec in pairs(vecs) do + s=s..string.format("%s,", UTILS._OneLineSerialize(vec)) + end + s=s..string.format("%s, %s, %s, %s", UTILS._OneLineSerialize(Color), UTILS._OneLineSerialize(FillColor), tostring(LineType), tostring(ReadOnly)) + if Text and Text~="" then + s=s..string.format(", \"%s\"", Text) + end + s=s..")" + + + -- Execute string command + local success=UTILS.DoString(s) + + if not success then + self:E("ERROR: Could not draw polygon") + env.info(s) + end + end return MarkID diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 2ba9d95ac..973090bb7 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -46,8 +46,9 @@ -- @image Core_Sets.JPG do -- SET_BASE - - --- @type SET_BASE + + --- + -- @type SET_BASE -- @field #table Filter Table of filters. -- @field #table Set Table of objects. -- @field #table Index Table of indices. @@ -242,7 +243,7 @@ do -- SET_BASE function SET_BASE:Add( ObjectName, Object ) -- Debug info. - self:T( { ObjectName = ObjectName, Object = Object } ) + self:T2( { ObjectName = ObjectName, Object = Object } ) -- Ensure that the existing element is removed from the Set before a new one is inserted to the Set if self.Set[ObjectName] then @@ -257,6 +258,8 @@ do -- SET_BASE -- Trigger Added event. self:Added( ObjectName, Object ) + + return self end --- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index. @@ -578,8 +581,8 @@ do -- SET_BASE end ----- Private method that registers all alive players in the mission. - ---- @param #SET_BASE self - ---- @return #SET_BASE self + -- @param #SET_BASE self + -- @return #SET_BASE self -- function SET_BASE:_RegisterPlayers() -- -- local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } @@ -796,8 +799,8 @@ do -- SET_BASE ----- Iterate the SET_BASE and call an iterator function for each **alive** unit, providing the Unit and optional parameters. - ---- @param #SET_BASE self - ---- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self -- function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) -- self:F3( arg ) @@ -808,8 +811,8 @@ do -- SET_BASE -- end -- ----- Iterate the SET_BASE and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters. - ---- @param #SET_BASE self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a UNIT parameter. ---- @return #SET_BASE self -- function SET_BASE:ForEachPlayer( IteratorFunction, ... ) -- self:F3( arg ) @@ -821,8 +824,8 @@ do -- SET_BASE -- -- ----- Iterate the SET_BASE and call an iterator function for each client, providing the Client to the function and optional parameters. - ---- @param #SET_BASE self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_BASE. The function needs to accept a CLIENT parameter. ---- @return #SET_BASE self -- function SET_BASE:ForEachClient( IteratorFunction, ... ) -- self:F3( arg ) @@ -901,9 +904,12 @@ do -- SET_BASE end -do -- SET_GROUP +do - --- @type SET_GROUP #SET_GROUP + -- SET_GROUP + + --- + -- @type SET_GROUP #SET_GROUP -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -1568,7 +1574,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsCompletelyInZone( ZoneObject ) then @@ -1590,7 +1596,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsPartlyInZone( ZoneObject ) then @@ -1612,7 +1618,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsNotInZone( ZoneObject ) then @@ -1659,7 +1665,7 @@ do -- SET_GROUP self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Group#GROUP GroupObject function( ZoneObject, GroupObject ) if GroupObject:IsAnyInZone( ZoneObject ) then @@ -1857,8 +1863,8 @@ do -- SET_GROUP end ----- Iterate the SET_GROUP and call an iterator function for each **alive** player, providing the Group of the player and optional parameters. - ---- @param #SET_GROUP self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. + -- @param #SET_GROUP self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a GROUP parameter. ---- @return #SET_GROUP self -- function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) -- self:F2( arg ) @@ -1870,8 +1876,8 @@ do -- SET_GROUP -- -- ----- Iterate the SET_GROUP and call an iterator function for each client, providing the Client to the function and optional parameters. - ---- @param #SET_GROUP self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. + -- @param #SET_GROUP self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_GROUP. The function needs to accept a CLIENT parameter. ---- @return #SET_GROUP self -- function SET_GROUP:ForEachClient( IteratorFunction, ... ) -- self:F2( arg ) @@ -1945,7 +1951,7 @@ do -- SET_GROUP if self.Filter.Zones then local MGroupZone = false for ZoneName, Zone in pairs( self.Filter.Zones ) do - --self:I( "Zone:", ZoneName ) + --self:T( "Zone:", ZoneName ) if MGroup:IsInZone(Zone) then MGroupZone = true end @@ -2011,8 +2017,9 @@ do -- SET_GROUP end do -- SET_UNIT - - --- @type SET_UNIT + + --- + -- @type SET_UNIT -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -2672,7 +2679,7 @@ do -- SET_UNIT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject function( ZoneObject, UnitObject ) if UnitObject:IsInZone( ZoneObject ) then @@ -2694,7 +2701,7 @@ do -- SET_UNIT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Unit#UNIT UnitObject function( ZoneObject, UnitObject ) if UnitObject:IsNotInZone( ZoneObject ) then @@ -3011,8 +3018,8 @@ do -- SET_UNIT ----- Iterate the SET_UNIT and call an iterator function for each **alive** player, providing the Unit of the player and optional parameters. - ---- @param #SET_UNIT self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. + -- @param #SET_UNIT self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a UNIT parameter. ---- @return #SET_UNIT self -- function SET_UNIT:ForEachPlayer( IteratorFunction, ... ) -- self:F2( arg ) @@ -3024,8 +3031,8 @@ do -- SET_UNIT -- -- ----- Iterate the SET_UNIT and call an iterator function for each client, providing the Client to the function and optional parameters. - ---- @param #SET_UNIT self - ---- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. + -- @param #SET_UNIT self + -- @param #function IteratorFunction The function that will be called when there is an alive player in the SET_UNIT. The function needs to accept a CLIENT parameter. ---- @return #SET_UNIT self -- function SET_UNIT:ForEachClient( IteratorFunction, ... ) -- self:F2( arg ) @@ -3040,7 +3047,7 @@ do -- SET_UNIT -- @param Wrapper.Unit#UNIT MUnit -- @return #SET_UNIT self function SET_UNIT:IsIncludeObject( MUnit ) - self:F2( MUnit ) + self:F2( {MUnit} ) local MUnitInclude = false @@ -3191,8 +3198,9 @@ do -- SET_UNIT end do -- SET_STATIC - - --- @type SET_STATIC + + --- + -- @type SET_STATIC -- @extends Core.Set#SET_BASE --- Mission designers can use the SET_STATIC class to build sets of Statics belonging to certain: @@ -3612,7 +3620,7 @@ do -- SET_STATIC self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject function( ZoneObject, StaticObject ) if StaticObject:IsInZone( ZoneObject ) then @@ -3634,7 +3642,7 @@ do -- SET_STATIC self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Static#STATIC StaticObject function( ZoneObject, StaticObject ) if StaticObject:IsNotInZone( ZoneObject ) then @@ -3945,8 +3953,9 @@ do -- SET_STATIC end do -- SET_CLIENT - - --- @type SET_CLIENT + + --- + -- @type SET_CLIENT -- @field Core.Timer#TIMER ZoneTimer -- @field #number ZoneTimerInterval -- @extends Core.Set#SET_BASE @@ -4008,8 +4017,8 @@ do -- SET_CLIENT Countries = nil, ClientPrefixes = nil, Zones = nil, - Playernames = nil, - Callsigns = nil, + Playernames = nil, + Callsigns = nil, }, FilterMeta = { Coalitions = { @@ -4059,7 +4068,7 @@ do -- SET_CLIENT --- Remove CLIENT(s) from SET_CLIENT. -- @param Core.Set#SET_CLIENT self - -- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names. + -- @param Wrapper.Client#CLIENT RemoveClientNames A single object or an array of CLIENT objects. -- @return self function SET_CLIENT:RemoveClientsByName( RemoveClientNames ) @@ -4369,7 +4378,7 @@ do -- SET_CLIENT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsInZone( ZoneObject ) then @@ -4391,7 +4400,7 @@ do -- SET_CLIENT self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsNotInZone( ZoneObject ) then @@ -4459,7 +4468,7 @@ do -- SET_CLIENT if self.Filter.Active == false or (self.Filter.Active == true and MClient:IsActive() == true and MClient:IsAlive() == true) then MClientActive = true end - --self:I( { "Evaluated Active", MClientActive } ) + --self:T( { "Evaluated Active", MClientActive } ) MClientInclude = MClientInclude and MClientActive end @@ -4526,45 +4535,45 @@ do -- SET_CLIENT MClientInclude = MClientInclude and MClientPrefix end - if self.Filter.Zones then - local MClientZone = false - for ZoneName, Zone in pairs( self.Filter.Zones ) do - self:T3( "Zone:", ZoneName ) - local unit = MClient:GetClientGroupUnit() - if unit and unit:IsInZone(Zone) then - MClientZone = true - end - end - MClientInclude = MClientInclude and MClientZone - end - - if self.Filter.Playernames then - local MClientPlayername = false - local playername = MClient:GetPlayerName() or "Unknown" - --self:I(playername) - for _,_Playername in pairs(self.Filter.Playernames) do - if playername and string.find(playername,_Playername) then - MClientPlayername = true - end - end - self:T( { "Evaluated Playername", MClientPlayername } ) - MClientInclude = MClientInclude and MClientPlayername - end - - if self.Filter.Callsigns then - local MClientCallsigns = false - local callsign = MClient:GetCallsign() - --self:I(callsign) - for _,_Callsign in pairs(self.Filter.Callsigns) do - if callsign and string.find(callsign,_Callsign) then - MClientCallsigns = true - end - end - self:T( { "Evaluated Callsign", MClientCallsigns } ) - MClientInclude = MClientInclude and MClientCallsigns - end - - end + if self.Filter.Zones then + local MClientZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + self:T3( "Zone:", ZoneName ) + local unit = MClient:GetClientGroupUnit() + if unit and unit:IsInZone(Zone) then + MClientZone = true + end + end + MClientInclude = MClientInclude and MClientZone + end + + if self.Filter.Playernames then + local MClientPlayername = false + local playername = MClient:GetPlayerName() or "Unknown" + --self:T(playername) + for _,_Playername in pairs(self.Filter.Playernames) do + if playername and string.find(playername,_Playername) then + MClientPlayername = true + end + end + self:T( { "Evaluated Playername", MClientPlayername } ) + MClientInclude = MClientInclude and MClientPlayername + end + + if self.Filter.Callsigns then + local MClientCallsigns = false + local callsign = MClient:GetCallsign() + --self:T(callsign) + for _,_Callsign in pairs(self.Filter.Callsigns) do + if callsign and string.find(callsign,_Callsign) then + MClientCallsigns = true + end + end + self:T( { "Evaluated Callsign", MClientCallsigns } ) + MClientInclude = MClientInclude and MClientCallsigns + end + + end self:T2( MClientInclude ) return MClientInclude end @@ -4573,7 +4582,8 @@ end do -- SET_PLAYER - --- @type SET_PLAYER + --- + -- @type SET_PLAYER -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_PLAYER} class to build sets of units belonging to alive players: @@ -4868,7 +4878,7 @@ do -- SET_PLAYER self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsInZone( ZoneObject ) then @@ -4890,7 +4900,7 @@ do -- SET_PLAYER self:F2( arg ) self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Core.Zone#ZONE_BASE ZoneObject -- @param Wrapper.Client#CLIENT ClientObject function( ZoneObject, ClientObject ) if ClientObject:IsNotInZone( ZoneObject ) then @@ -4998,8 +5008,9 @@ do -- SET_PLAYER end do -- SET_AIRBASE - - --- @type SET_AIRBASE + + --- + -- @type SET_AIRBASE -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: @@ -5341,8 +5352,9 @@ do -- SET_AIRBASE end do -- SET_CARGO - - --- @type SET_CARGO + + --- + -- @type SET_CARGO -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_CARGO} class to build sets of cargos optionally belonging to certain: @@ -5767,8 +5779,9 @@ do -- SET_CARGO end do -- SET_ZONE - - --- @type SET_ZONE + + --- + -- @type SET_ZONE -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_ZONE} class to build sets of zones of various types. @@ -6135,8 +6148,9 @@ do -- SET_ZONE end do -- SET_ZONE_GOAL - - --- @type SET_ZONE_GOAL + + --- + -- @type SET_ZONE_GOAL -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_ZONE_GOAL} class to build sets of zones of various types. @@ -6446,8 +6460,9 @@ do -- SET_ZONE_GOAL end do -- SET_OPSZONE - - --- @type SET_OPSZONE + + --- + -- @type SET_OPSZONE -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_OPSZONE} class to build sets of zones of various types. @@ -6903,8 +6918,9 @@ end do -- SET_OPSGROUP - - --- @type SET_OPSGROUP + + --- + -- @type SET_OPSGROUP -- @extends Core.Set#SET_BASE --- Mission designers can use the @{Core.Set#SET_OPSGROUP} class to build sets of OPS groups belonging to certain: @@ -7571,6 +7587,7 @@ do -- SET_SCENERY Scenerys = {}, Filter = { SceneryPrefixes = nil, + SceneryRoles = nil, Zones = nil, }, } @@ -7593,7 +7610,7 @@ do -- SET_SCENERY if ZoneSet then for _,_zone in pairs(ZoneSet.Set) do - self:T("Zone type handed: "..tostring(_zone.ClassName)) + self:T("Zone type handed: "..tostring(_zone.ClassName)) table.insert(zonenames,_zone:GetName()) end self:AddSceneryByName(zonenames) @@ -7617,7 +7634,7 @@ do -- SET_SCENERY --- Add SCENERY(s) to SET_SCENERY. -- @param #SET_SCENERY self - -- @param #string AddScenery A single SCENERY. + -- @param Wrapper.Scenery#SCENERY AddScenery A single SCENERY object. -- @return #SET_SCENERY self function SET_SCENERY:AddScenery( AddScenery ) self:F2( AddScenery:GetName() ) @@ -7687,6 +7704,7 @@ do -- SET_SCENERY end for _,Zone in pairs( zones ) do local zonename = Zone:GetName() + self:T(zonename) self.Filter.Zones[zonename] = Zone end return self @@ -7705,11 +7723,30 @@ do -- SET_SCENERY Prefixes = { Prefixes } end for PrefixID, Prefix in pairs( Prefixes ) do + --self:T(Prefix) self.Filter.SceneryPrefixes[Prefix] = Prefix end return self end - + + --- Builds a set of SCENERYs that **contain** an exact match of the "ROLE" property. + -- @param #SET_SCENERY self + -- @param #string Role The string pattern(s) that needs to exactly match the scenery "ROLE" property from the ME quad-zone properties. Can also be passed as a `#table` of strings. + -- @return #SET_SCENERY self + function SET_SCENERY:FilterRoles( Role ) + if not self.Filter.SceneryRoles then + self.Filter.SceneryRoles = {} + end + if type( Role ) ~= "table" then + Role = { Role } + end + for PrefixID, Prefix in pairs( Role ) do + --self:T(Prefix) + self.Filter.SceneryRoles[Prefix] = Prefix + end + return self + end + --- Iterate the SET_SCENERY and count how many SCENERYSs are alive. -- @param #SET_SCENERY self -- @return #number The number of SCENERYSs alive. @@ -7802,10 +7839,76 @@ do -- SET_SCENERY -- @param Wrapper.Scenery#SCENERY MScenery -- @return #SET_SCENERY self function SET_SCENERY:IsIncludeObject( MScenery ) - self:F2( MScenery ) - return true + self:T( MScenery.SceneryName ) + + local MSceneryInclude = true + + if MScenery then + local MSceneryName = MScenery:GetName() + + -- Filter Prefixes + if self.Filter.Prefixes then + local MSceneryPrefix = false + for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do + self:T( { "Prefix:", string.find( MSceneryName, ZonePrefix, 1 ), ZonePrefix } ) + if string.find( MSceneryName, ZonePrefix, 1 ) then + MSceneryPrefix = true + end + end + self:T( { "Evaluated Prefix", MSceneryPrefix } ) + MSceneryInclude = MSceneryInclude and MSceneryPrefix + end + + if self.Filter.Zones then + local MSceneryZone = false + for ZoneName, Zone in pairs( self.Filter.Zones ) do + --self:T( "Zone:", ZoneName ) + local coord = MScenery:GetCoordinate() + if coord and Zone:IsCoordinateInZone(coord) then + MSceneryZone = true + end + self:T( { "Evaluated Zone", MSceneryZone } ) + end + MSceneryInclude = MSceneryInclude and MSceneryZone + end + + -- Filter Roles + if self.Filter.SceneryRoles then + local MSceneryRole = false + local Role = MScenery:GetProperty("ROLE") or "none" + for ZoneRoleId, ZoneRole in pairs( self.Filter.SceneryRoles ) do + self:T( { "Role:", ZoneRole, Role } ) + if ZoneRole == Role then + MSceneryRole = true + end + end + self:T( { "Evaluated Role ", MSceneryRole } ) + MSceneryInclude = MSceneryInclude and MSceneryRole + end + end + + self:T2( MSceneryInclude ) + return MSceneryInclude end + --- Filters for the defined collection. + -- @param #SET_SCENERY self + -- @return #SET_SCENERY self + function SET_SCENERY:FilterOnce() + + for ObjectName, Object in pairs( self:GetSet() ) do + self:T(ObjectName) + if self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) + else + self:Remove(ObjectName, true) + end + end + + return self --FilteredSet + end + + --- Count overall initial (Life0) lifepoints of the SET objects. -- @param #SET_SCENERY self -- @return #number LIfe0Points @@ -7843,7 +7946,7 @@ do -- SET_SCENERY function SET_SCENERY:GetRelativeLife() local life = self:GetLife() local life0 = self:GetLife0() - self:T3(string.format("Set Lifepoints: %d life0 | %d life",life0,life)) + self:T2(string.format("Set Lifepoints: %d life0 | %d life",life0,life)) local rlife = math.floor((life / life0) * 100) return rlife end diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 3d0c56b9f..ec95dc8a3 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -1420,7 +1420,7 @@ function SCORING:ReportDetailedPlayerHits( PlayerName ) Penalty = Penalty + UnitData.Penalty PenaltyHit = UnitData.PenaltyHit end - local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty ) + local ScoreMessageHit = string.format( "%s: %d ", CategoryName, Score - Penalty ) self:T( ScoreMessageHit ) ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit PlayerScore = PlayerScore + Score @@ -1476,7 +1476,7 @@ function SCORING:ReportDetailedPlayerDestroys( PlayerName ) end end - local ScoreMessageDestroy = string.format( " %s:%d ", CategoryName, Score - Penalty ) + local ScoreMessageDestroy = string.format( " %s: %d ", CategoryName, Score - Penalty ) self:T( ScoreMessageDestroy ) ScoreMessageDestroys = ScoreMessageDestroys .. ScoreMessageDestroy diff --git a/Moose Development/Moose/Ops/Airboss.lua b/Moose Development/Moose/Ops/Airboss.lua index 005f5b614..ca554f823 100644 --- a/Moose Development/Moose/Ops/Airboss.lua +++ b/Moose Development/Moose/Ops/Airboss.lua @@ -1735,10 +1735,10 @@ AIRBOSS.Difficulty = { -- @field #table trapsheet Groove data table recorded every 0.5 seconds. -- @field #boolean trapon If true, save trap sheets. -- @field #string debriefschedulerID Debrief scheduler ID. --- +-- -- @field Sound.SRS#MSRS SRS -- @field Sound.SRS#MSRSQUEUE SRSQ --- +-- -- @extends #AIRBOSS.FlightGroup --- Main group level radio menu: F10 Other/Airboss. @@ -3058,7 +3058,7 @@ end -- @param #number Port Port of the SRS server, defaults to 5002. -- @param #string Culture (Optional, Airboss Culture) Culture, defaults to "en-US". -- @param #string Gender (Optional, Airboss Gender) Gender, e.g. "male" or "female". Defaults to "male". --- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings. +-- @param #string Voice (Optional, Airboss Voice) Set to use a specific voice. Will **override gender and culture** settings. -- @param #string GoogleCreds (Optional) Path to Google credentials, e.g. "C:\\Program Files\\DCS-SimpleRadio-Standalone\\yourgooglekey.json". -- @param #number Volume (Optional) E.g. 0.75. Defaults to 1.0 (loudest). -- @param #table AltBackend (Optional) See MSRS for details. @@ -3087,10 +3087,10 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum -- SRSQUEUE self.SRSQ = MSRSQUEUE:New("AIRBOSS") self.SRSQ:SetTransmitOnlyWithPlayers(true) - if not self.PilotRadio then + if not self.PilotRadio then self:SetSRSPilotVoice() end - return self + return self end --- Set LSO radio frequency and modulation. Default frequency is 264 MHz AM. @@ -5924,9 +5924,11 @@ function AIRBOSS:_ScanCarrierZone() -- Get aircraft type name. local actype = group:GetTypeName() - -- Create a new flight group if knownflight then + -- Debug output. + self:T2(self.lid..string.format("Known flight group %s of type %s in CCA.", groupname, actype)) + -- Check if flight is AI and if we want to handle it at all. if knownflight.ai and knownflight.flag == -100 and self.handleai then @@ -5943,9 +5945,10 @@ function AIRBOSS:_ScanCarrierZone() flight.airboss = self end - -- Send AI flight to marshal stack. + -- Send AI flight to marshal stack if group closes in more than 5 and has initial flag value. if putintomarshal then + -- Get the next free stack for current recovery case. local stack = self:_GetFreeStack( knownflight.ai ) @@ -11553,7 +11556,7 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) local function adjustDegreesForWindSpeed(windSpeed) local degreesAdjustment = 0 -- the windspeeds are in m/s - + -- +0 degrees at 15m/s = 37kts -- +0 degrees at 14m/s = 35kts -- +0 degrees at 13m/s = 33kts @@ -11568,7 +11571,7 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) -- +20 degrees at 4m/s = 26kts -- +20 degrees at 3m/s = 26kts -- +30 degrees at 2m/s = 26kts 1s - + if windSpeed > 0 and windSpeed < 3 then degreesAdjustment = 30 elseif windSpeed >= 3 and windSpeed < 5 then @@ -11580,17 +11583,17 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord ) elseif windSpeed >= 13 then degreesAdjustment = 0 end - + return degreesAdjustment end -- Get direction the wind is blowing from. This is where we want to go. local windfrom, vwind = self:GetWind( nil, nil, coord ) - + --self:I("windfrom="..windfrom.." vwind="..vwind) - + vwind = vwind + adjustDegreesForWindSpeed(vwind) - + --self:I("windfrom="..windfrom.." (c)vwind="..vwind) -- Actually, we want the runway in the wind. @@ -14730,12 +14733,12 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p if radio == nil or call == nil then return end - + if not self.SRS then - + -- Create a new radio transmission item. local transmission = {} -- #AIRBOSS.Radioitem - + transmission.radio = radio transmission.call = call transmission.Tplay = timer.getAbsTime() + (delay or 0) @@ -14743,58 +14746,58 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p transmission.isplaying = false transmission.Tstarted = nil transmission.loud = loud and call.loud - + -- Player onboard number if sender has one. if self:_IsOnboard( call.modexsender ) then self:_Number2Radio( radio, call.modexsender, delay, 0.3, pilotcall ) end - + -- Play onboard number if receiver has one. if self:_IsOnboard( call.modexreceiver ) then self:_Number2Radio( radio, call.modexreceiver, delay, 0.3, pilotcall ) end - + -- Add transmission to the right queue. local caller = "" if radio.alias == "LSO" then - + table.insert( self.RQLSO, transmission ) - + caller = "LSOCall" - + -- Schedule radio queue checks. if not self.RQLid then self:T( self.lid .. string.format( "Starting LSO radio queue." ) ) self.RQLid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQLSO, "LSO" }, 0.02, 0.05 ) end - + elseif radio.alias == "MARSHAL" then - + table.insert( self.RQMarshal, transmission ) - + caller = "MarshalCall" - + if not self.RQMid then self:T( self.lid .. string.format( "Starting Marhal radio queue." ) ) self.RQMid = self.radiotimer:Schedule( nil, AIRBOSS._CheckRadioQueue, { self, self.RQMarshal, "MARSHAL" }, 0.02, 0.05 ) end - + end - + -- Append radio click sound at the end of the transmission. if click then self:RadioTransmission( radio, self[caller].CLICK, false, delay ) end - + else -- SRS transmission - + local frequency = self.MarshalRadio.frequency local modulation = self.MarshalRadio.modulation local voice = nil local gender = nil local culture = nil - + if radio.alias == "AIRBOSS" then frequency = self.AirbossRadio.frequency modulation = self.AirbossRadio.modulation @@ -14802,13 +14805,13 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p gender = self.AirbossRadio.gender culture = self.AirbossRadio.culture end - + if radio.alias == "MARSHAL" then voice = self.MarshalRadio.voice gender = self.MarshalRadio.gender culture = self.MarshalRadio.culture end - + if radio.alias == "LSO" then frequency = self.LSORadio.frequency modulation = self.LSORadio.modulation @@ -14816,7 +14819,7 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p gender = self.LSORadio.gender culture = self.LSORadio.culture end - + if pilotcall then voice = self.PilotRadio.voice gender = self.PilotRadio.gender @@ -14830,16 +14833,16 @@ function AIRBOSS:RadioTransmission( radio, call, loud, delay, interval, click, p modulation = self.AirbossRadio.modulation radio.alias = "AIRBOSS" end - + local volume = nil - + if loud then volume = 1.0 end - + --local text = tostring(call.modexreceiver).."; "..radio.alias.."; "..call.subtitle local text = call.subtitle - self:I(self.lid..text) + self:I(self.lid..text) local srstext = self:_GetNiceSRSText(text) self.SRSQ:NewTransmission(srstext, call.duration, self.SRS, tstart, 0.1, subgroups, call.subtitle, call.subduration, frequency, modulation, gender, culture, voice, volume, radio.alias) end @@ -14858,11 +14861,11 @@ function AIRBOSS:SetSRSPilotVoice( Voice, Gender, Culture ) self.PilotRadio.voice = Voice or MSRS.Voices.Microsoft.David self.PilotRadio.gender = Gender or "male" self.PilotRadio.culture = Culture or "en-US" - + if (not Voice) and self.SRS and self.SRS.google then self.PilotRadio.voice = MSRS.Voices.Google.Standard.en_US_Standard_J end - + return self end @@ -15176,44 +15179,44 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio -- SCHEDULER:New(nil, self.MessageToPlayer, {self, playerData, message, sender, receiver, duration, clear}, delay) self:ScheduleOnce( delay, self.MessageToPlayer, self, playerData, message, sender, receiver, duration, clear ) else - + if not self.SRS then -- Wait until previous sound finished. local wait = 0 - + -- Onboard number to get the attention. if receiver == playerData.onboard then - + -- Which voice over number to use. if sender and (sender == "LSO" or sender == "MARSHAL" or sender == "AIRBOSS") then - + -- User sound of board number. wait = wait + self:_Number2Sound( playerData, sender, receiver ) - + end end - + -- Negative. if string.find( text:lower(), "negative" ) then local filename = self:_RadioFilename( self.MarshalCall.NEGATIVE, false, "MARSHAL" ) USERSOUND:New( filename ):ToGroup( playerData.group, wait ) wait = wait + self.MarshalCall.NEGATIVE.duration end - + -- Affirm. if string.find( text:lower(), "affirm" ) then local filename = self:_RadioFilename( self.MarshalCall.AFFIRMATIVE, false, "MARSHAL" ) USERSOUND:New( filename ):ToGroup( playerData.group, wait ) wait = wait + self.MarshalCall.AFFIRMATIVE.duration end - + -- Roger. if string.find( text:lower(), "roger" ) then local filename = self:_RadioFilename( self.MarshalCall.ROGER, false, "MARSHAL" ) USERSOUND:New( filename ):ToGroup( playerData.group, wait ) wait = wait + self.MarshalCall.ROGER.duration end - + -- Play click sound to end message. if wait > 0 then local filename = self:_RadioFilename( self.MarshalCall.CLICK ) @@ -15226,7 +15229,7 @@ function AIRBOSS:MessageToPlayer( playerData, message, sender, receiver, duratio local voice = self.MarshalRadio.voice local gender = self.MarshalRadio.gender local culture = self.MarshalRadio.culture - + if not sender then sender = "AIRBOSS" end if string.find(sender,"AIRBOSS" ) then diff --git a/Moose Development/Moose/Ops/Awacs.lua b/Moose Development/Moose/Ops/Awacs.lua index 3c04848a3..5bff6cc86 100644 --- a/Moose Development/Moose/Ops/Awacs.lua +++ b/Moose Development/Moose/Ops/Awacs.lua @@ -1517,6 +1517,23 @@ function AWACS:_SubScribeTactRadio(Group,Frequency) return self end +--- [Internal] _CheckSubscribers +-- @param #AWACS self +-- @return #AWACS self +function AWACS:_CheckSubscribers() + self:T(self.lid.."_InitLocalization") + + for _name,_freq in pairs(self.TacticalSubscribers or {}) do + local grp = GROUP:FindByName(_name) + if (not grp) or (not grp:IsAlive()) then + self.TacticalFrequencies[_freq] = _freq + self.TacticalSubscribers[_name] = nil + end + end + + return self +end + --- [Internal] Init localization -- @param #AWACS self -- @return #AWACS self @@ -1596,7 +1613,7 @@ function AWACS:_NewRadioEntry(TextTTS, TextScreen,GID,IsGroup,ToScreen,IsNew,Fro local RadioEntry = {} -- #AWACS.RadioEntry RadioEntry.IsNew = IsNew RadioEntry.TextTTS = TextTTS - RadioEntry.TextScreen = TextScreen + RadioEntry.TextScreen = TextScreen or TextTTS RadioEntry.GroupID = GID RadioEntry.ToScreen = ToScreen RadioEntry.Duration = STTS.getSpeechTime(TextTTS,0.95,false) or 8 @@ -6180,6 +6197,8 @@ function AWACS:onafterStatus(From, Event, To) self:_CheckMerges() + self:_CheckSubscribers() + local outcome, targets = self:_TargetSelectionProcess(true) self:_CheckTaskQueue() diff --git a/Moose Development/Moose/Ops/Chief.lua b/Moose Development/Moose/Ops/Chief.lua index 0272ec659..77477e0cc 100644 --- a/Moose Development/Moose/Ops/Chief.lua +++ b/Moose Development/Moose/Ops/Chief.lua @@ -1485,6 +1485,17 @@ function CHIEF:AddGciCapZone(Zone, Altitude, Speed, Heading, Leg) return zone end +--- Remove a GCI CAP +-- @param #CHIEF self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function CHIEF:RemoveGciCapZone(Zone) + + -- Hand over to commander. + local zone=self.commander:RemoveGciCapZone(Zone) + + return zone +end + --- Add an AWACS zone. -- @param #CHIEF self -- @param Core.Zone#ZONE Zone Zone. @@ -1501,6 +1512,17 @@ function CHIEF:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg) return zone end +--- Remove a AWACS zone. +-- @param #CHIEF self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function CHIEF:RemoveAwacsZone(Zone) + + -- Hand over to commander. + local zone=self.commander:RemoveAwacsZone(Zone) + + return zone +end + --- Add a refuelling tanker zone. -- @param #CHIEF self -- @param Core.Zone#ZONE Zone Zone. @@ -1518,6 +1540,16 @@ function CHIEF:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSystem) return zone end +--- Remove a refuelling tanker zone. +-- @param #CHIEF self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function CHIEF:RemoveTankerZone(Zone) + + -- Hand over to commander. + local zone=self.commander:RemoveTankerZone(Zone) + + return zone +end --- Set border zone set, defining your territory. -- diff --git a/Moose Development/Moose/Ops/Commander.lua b/Moose Development/Moose/Ops/Commander.lua index 0af40ed2d..378df6007 100644 --- a/Moose Development/Moose/Ops/Commander.lua +++ b/Moose Development/Moose/Ops/Commander.lua @@ -710,6 +710,26 @@ function COMMANDER:AddGciCapZone(Zone, Altitude, Speed, Heading, Leg) return patrolzone end +--- Remove a GCI CAP. +-- @param #COMMANDER self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function COMMANDER:RemoveGciCapZone(Zone) + + local patrolzone={} --Ops.AirWing#AIRWING.PatrolZone + + patrolzone.zone=Zone + for i,_patrolzone in pairs(self.gcicapZones) do + if _patrolzone.zone == patrolzone.zone then + if _patrolzone.mission and _patrolzone.mission:IsNotOver() then + _patrolzone.mission:Cancel() + end + table.remove(self.gcicapZones, i) + break + end + end + return patrolzone +end + --- Add an AWACS zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE Zone Zone. @@ -735,6 +755,26 @@ function COMMANDER:AddAwacsZone(Zone, Altitude, Speed, Heading, Leg) return awacszone end +--- Remove a AWACS zone. +-- @param #COMMANDER self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function COMMANDER:RemoveAwacsZone(Zone) + + local awacszone={} --Ops.AirWing#AIRWING.PatrolZone + + awacszone.zone=Zone + for i,_awacszone in pairs(self.awacsZones) do + if _awacszone.zone == awacszone.zone then + if _awacszone.mission and _awacszone.mission:IsNotOver() then + _awacszone.mission:Cancel() + end + table.remove(self.awacsZones, i) + break + end + end + return awacszone +end + --- Add a refuelling tanker zone. -- @param #COMMANDER self -- @param Core.Zone#ZONE Zone Zone. @@ -762,6 +802,26 @@ function COMMANDER:AddTankerZone(Zone, Altitude, Speed, Heading, Leg, RefuelSyst return tankerzone end +--- Remove a refuelling tanker zone. +-- @param #COMMANDER self +-- @param Core.Zone#ZONE Zone Zone, where the flight orbits. +function COMMANDER:RemoveTankerZone(Zone) + + local tankerzone={} --Ops.AirWing#AIRWING.PatrolZone + + tankerzone.zone=Zone + for i,_tankerzone in pairs(self.tankerZones) do + if _tankerzone.zone == tankerzone.zone then + if _tankerzone.mission and _tankerzone.mission:IsNotOver() then + _tankerzone.mission:Cancel() + end + table.remove(self.tankerZones, i) + break + end + end + return tankerzone +end + --- Check if this mission is already in the queue. -- @param #COMMANDER self -- @param Ops.Auftrag#AUFTRAG Mission The mission. diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index bde363415..5800625df 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -325,7 +325,322 @@ function FLIGHTGROUP:New(group) -- @param #FLIGHTGROUP self -- @param #number delay Delay in seconds. - -- TODO: Add pseudo functions. + --- FSM Function OnAfterElementSpawned. + -- @function [parent=#FLIGHTGROUP] OnAfterElementSpawned + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementParking. + -- @function [parent=#FLIGHTGROUP] OnAfterElementParking + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE.ParkingSpot Spot Parking Spot. + + --- FSM Function OnAfterElementEngineOn. + -- @function [parent=#FLIGHTGROUP] OnAfterElementEngineOn + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementTaxiing. + -- @function [parent=#FLIGHTGROUP] OnAfterElementTaxiing + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementTakeoff. + -- @function [parent=#FLIGHTGROUP] OnAfterElementTakeoff + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil. + + --- FSM Function OnAfterElementAirborne. + -- @function [parent=#FLIGHTGROUP] OnAfterElementAirborne + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementLanded. + -- @function [parent=#FLIGHTGROUP] OnAfterElementLanded + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil. + + --- FSM Function OnAfterElementArrived. + -- @function [parent=#FLIGHTGROUP] OnAfterElementArrived + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase, where the element arrived. + -- @param Wrapper.Airbase#AIRBASE.ParkingSpot Parking The Parking spot the element has. + + --- FSM Function OnAfterElementDestroyed. + -- @function [parent=#FLIGHTGROUP] OnAfterElementDestroyed + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterElementDead. + -- @function [parent=#FLIGHTGROUP] OnAfterElementDead + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element. + + --- FSM Function OnAfterSpawned. + -- @function [parent=#FLIGHTGROUP] OnAfterSpawned + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterParking. + -- @function [parent=#FLIGHTGROUP] OnAfterParking + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterTaxiing. + -- @function [parent=#FLIGHTGROUP] OnAfterTaxiing + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterTakeoff. + -- @function [parent=#FLIGHTGROUP] OnAfterTakeoff + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterAirborne. + -- @function [parent=#FLIGHTGROUP] OnAfterAirborne + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterCruise. + -- @function [parent=#FLIGHTGROUP] OnAfterCruise + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterLanding. + -- @function [parent=#FLIGHTGROUP] OnAfterLanding + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterLanded. + -- @function [parent=#FLIGHTGROUP] OnAfterLanded + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase the flight landed. + + --- FSM Function OnAfterLandedAt. + -- @function [parent=#FLIGHTGROUP] OnAfterLandedAt + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterArrived. + -- @function [parent=#FLIGHTGROUP] OnAfterArrived + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterDead. + -- @function [parent=#FLIGHTGROUP] OnAfterDead + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterUpdateRoute. + -- @function [parent=#FLIGHTGROUP] OnAfterUpdateRoute + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number n Next waypoint index. Default is the one coming after that one that has been passed last. + -- @param #number N Waypoint Max waypoint index to be included in the route. Default is the final waypoint. + + --- FSM Function OnAfterOutOfMissilesAA. + -- @function [parent=#FLIGHTGROUP] OnAfterOutOfMissilesAA + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterOutOfMissilesAG. + -- @function [parent=#FLIGHTGROUP] OnAfterOutOfMissilesAG + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterRTB. + -- @function [parent=#FLIGHTGROUP] OnAfterRTB + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + -- @param #number SpeedTo Speed used for traveling from current position to holding point in knots. Default 75% of max speed. + -- @param #number SpeedHold Holding speed in knots. Default 250 kts. + -- @param #number SpeedLand Landing speed in knots. Default 170 kts. + + --- FSM Function OnAfterLandAtAirbase. + -- @function [parent=#FLIGHTGROUP] OnAfterLandAtAirbase + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + + --- FSM Function OnAfterWait. + -- @function [parent=#FLIGHTGROUP] OnAfterWait + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever). + -- @param #number Altitude Altitude in feet. Default 10,000 ft for airplanes and 1,000 feet for helos. + -- @param #number Speed Speed in knots. Default 250 kts for airplanes and 20 kts for helos. + + --- FSM Function OnAfterRefuel. + -- @function [parent=#FLIGHTGROUP] OnAfterRefuel + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Point#COORDINATE Coordinate The coordinate. + + --- FSM Function OnAfterRefueled. + -- @function [parent=#FLIGHTGROUP] OnAfterRefueled + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterDisengage. + -- @function [parent=#FLIGHTGROUP] OnAfterDisengage + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Set#SET_UNIT TargetUnitSet + + --- FSM Function OnAfterEngageTarget. + -- @function [parent=#FLIGHTGROUP] OnAfterEngageTarget + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table Target Target object. Can be a UNIT, STATIC, GROUP, SET_UNIT or SET_GROUP object. + + --- FSM Function OnAfterLandAt. + -- @function [parent=#FLIGHTGROUP] OnAfterLandAt + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Set#SET_UNIT TargetUnitSet + + --- FSM Function OnAfterFuelLow. + -- @function [parent=#FLIGHTGROUP] OnAfterFuelLow + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnAfterFuelCritical. + -- @function [parent=#FLIGHTGROUP] OnAfterFuelCritical + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- FSM Function OnBeforeUpdateRoute. + -- @function [parent=#FLIGHTGROUP] OnBeforeUpdateRoute + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number n Next waypoint index. Default is the one coming after that one that has been passed last. + -- @param #number N Waypoint Max waypoint index to be included in the route. Default is the final waypoint. + -- @return #boolean Transision allowed? + + --- FSM Function OnBeforeRTB. + -- @function [parent=#FLIGHTGROUP] OnBeforeRTB + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + -- @param #number SpeedTo Speed used for travelling from current position to holding point in knots. + -- @param #number SpeedHold Holding speed in knots. + + --- FSM Function OnBeforeLandAtAirbase. + -- @function [parent=#FLIGHTGROUP] OnBeforeLandAtAirbase + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase to hold at. + + --- FSM Function OnBeforeWait. + -- @function [parent=#FLIGHTGROUP] OnBeforeWait + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #number Duration Duration how long the group will be waiting in seconds. Default `nil` (=forever). + -- @param #number Altitude Altitude in feet. Default 10,000 ft for airplanes and 1,000 feet for helos. + -- @param #number Speed Speed in knots. Default 250 kts for airplanes and 20 kts for helos. + + --- FSM Function OnBeforeLandAt. + -- @function [parent=#FLIGHTGROUP] OnBeforeLandAt + -- @param #FLIGHTGROUP self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Point#COORDINATE Coordinate The coordinate where to land. Default is current position. + -- @param #number Duration The duration in seconds to remain on ground. Default 600 sec (10 min). + + -- TODO: Add pseudo functions ? Normally done, but should be double check -- Handle events: self:HandleEvent(EVENTS.Birth, self.OnEventBirth) diff --git a/Moose Development/Moose/Ops/PlayerTask.lua b/Moose Development/Moose/Ops/PlayerTask.lua index d44e9c644..f91893c7e 100644 --- a/Moose Development/Moose/Ops/PlayerTask.lua +++ b/Moose Development/Moose/Ops/PlayerTask.lua @@ -21,7 +21,7 @@ -- === -- @module Ops.PlayerTask -- @image OPS_PlayerTask.jpg --- @date Last Update June 2023 +-- @date Last Update July 2023 do @@ -98,7 +98,7 @@ PLAYERTASK = { --- PLAYERTASK class version. -- @field #string version -PLAYERTASK.version="0.1.18" +PLAYERTASK.version="0.1.19" --- Generic task condition. -- @type PLAYERTASK.Condition @@ -1656,13 +1656,6 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter) local starttime = math.random(5,10) self:__Status(starttime) - -- Player leaves - self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) - self:HandleEvent(EVENTS.Ejection, self._EventHandler) - self:HandleEvent(EVENTS.Crash, self._EventHandler) - self:HandleEvent(EVENTS.PilotDead, self._EventHandler) - self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) - self:I(self.lid..self.version.." Started.") return self @@ -2177,7 +2170,7 @@ end -- @return #PLAYERTASKCONTROLLER self function PLAYERTASKCONTROLLER:_EventHandler(EventData) self:T(self.lid.."_EventHandler: "..EventData.id) - self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName) + --self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName) if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then if EventData.IniPlayerName then self:T(self.lid.."Event for player: "..EventData.IniPlayerName) @@ -2483,6 +2476,15 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue() self:T("*****Removing player " .. _id) self.TasksPerPlayer:PullByID(_id) end + local clients=task:GetClientObjects() + for _,client in pairs(clients) do + self:_RemoveMenuEntriesForTask(task,client) + --self:_SwitchMenuForClient(client,"Info") + end + for _,client in pairs(clients) do + -- self:_RemoveMenuEntriesForTask(Task,client) + self:_SwitchMenuForClient(client,"Info",5) + end -- Follow-up tasks? local nexttasks = {} if task.FinalState == "Success" then @@ -3594,28 +3596,28 @@ function PLAYERTASKCONTROLLER:_RemoveMenuEntriesForTask(Task,Client) if Task then if Task.UUIDS and self.JoinTaskMenuTemplate then --self:I("***** JoinTaskMenuTemplate") - UTILS.PrintTableToLog(Task.UUIDS) + --UTILS.PrintTableToLog(Task.UUIDS) local controller = self.JoinTaskMenuTemplate for _,_uuid in pairs(Task.UUIDS) do local Entry = controller:FindEntryByUUID(_uuid) if Entry then controller:DeleteF10Entry(Entry,Client) controller:DeleteGenericEntry(Entry) - UTILS.PrintTableToLog(controller.menutree) + --UTILS.PrintTableToLog(controller.menutree) end end end if Task.AUUIDS and self.ActiveTaskMenuTemplate then --self:I("***** ActiveTaskMenuTemplate") - UTILS.PrintTableToLog(Task.AUUIDS) + --UTILS.PrintTableToLog(Task.AUUIDS) for _,_uuid in pairs(Task.AUUIDS) do local controller = self.ActiveTaskMenuTemplate local Entry = controller:FindEntryByUUID(_uuid) if Entry then controller:DeleteF10Entry(Entry,Client) controller:DeleteGenericEntry(Entry) - UTILS.PrintTableToLog(controller.menutree) + --UTILS.PrintTableToLog(controller.menutree) end end end @@ -4063,6 +4065,12 @@ function PLAYERTASKCONTROLLER:onafterStart(From, Event, To) self:T(self.lid.."onafterStart") self:_CreateJoinMenuTemplate() self:_CreateActiveTaskMenuTemplate() + -- Player Events + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler) + self:HandleEvent(EVENTS.Ejection, self._EventHandler) + self:HandleEvent(EVENTS.Crash, self._EventHandler) + self:HandleEvent(EVENTS.PilotDead, self._EventHandler) + self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler) return self end @@ -4210,6 +4218,15 @@ function PLAYERTASKCONTROLLER:onafterTaskFailed(From, Event, To, Task) taskname = string.format(failtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType)) self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2) end + local clients=Task:GetClientObjects() + for _,client in pairs(clients) do + self:_RemoveMenuEntriesForTask(Task,client) + --self:_SwitchMenuForClient(client,"Info") + end + for _,client in pairs(clients) do + -- self:_RemoveMenuEntriesForTask(Task,client) + self:_SwitchMenuForClient(client,"Info",5) + end return self end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 99c643a7a..1e1dd407c 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -295,8 +295,9 @@ UTILS.DeepCopy = function(object) end ---- Porting in Slmod's serialize_slmod2. +--- Serialize a given table. -- @param #table tbl Input table. +-- @return #string Table as a string. UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function local lookup_table = {} @@ -373,7 +374,54 @@ local lookup_table = {} return objectreturn end ---porting in Slmod's "safestring" basic serialize +--- Serialize a table to a single line string. +-- @param #table tbl table to serialize. +-- @return #string string containing serialized table. +function UTILS._OneLineSerialize(tbl) + + if type(tbl) == 'table' then --function only works for tables! + + local tbl_str = {} + + tbl_str[#tbl_str + 1] = '{ ' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else --must be a string + tbl_str[#tbl_str + 1] = '[' + tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(val) + tbl_str[#tbl_str + 1] = ', ' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil, ' + elseif type(val) == 'table' then + tbl_str[#tbl_str + 1] = UTILS._OneLineSerialize(val) + tbl_str[#tbl_str + 1] = ', ' --I think this is right, I just added it + else + --log:warn('Unable to serialize value type $1 at index $2', mist.utils.basicSerialize(type(val)), tostring(ind)) + end + + end + + tbl_str[#tbl_str + 1] = '}' + return table.concat(tbl_str) + else + return UTILS.BasicSerialize(tbl) + end +end + +--- Basic serialize (porting in Slmod's "safestring" basic serialize). +-- @param #string s Table to serialize. UTILS.BasicSerialize = function(s) if s == nil then return "\"\"" @@ -404,6 +452,114 @@ function UTILS.PrintTableToLog(table, indent) end end +--- Returns table in a easy readable string representation. +-- @param tbl table to show +-- @param loc +-- @param indent +-- @param tableshow_tbls +-- @return Human readable string representation of given table. +function UTILS.TableShow(tbl, loc, indent, tableshow_tbls) + tableshow_tbls = tableshow_tbls or {} --create table of tables + loc = loc or "" + indent = indent or "" + if type(tbl) == 'table' then --function only works for tables! + tableshow_tbls[tbl] = loc + + local tbl_str = {} + + tbl_str[#tbl_str + 1] = indent .. '{\n' + + for ind,val in pairs(tbl) do -- serialize its fields + if type(ind) == "number" then + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = tostring(ind) + tbl_str[#tbl_str + 1] = '] = ' + else + tbl_str[#tbl_str + 1] = indent + tbl_str[#tbl_str + 1] = loc .. '[' + tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(ind) + tbl_str[#tbl_str + 1] = '] = ' + end + + if ((type(val) == 'number') or (type(val) == 'boolean')) then + tbl_str[#tbl_str + 1] = tostring(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'string' then + tbl_str[#tbl_str + 1] = UTILS.BasicSerialize(val) + tbl_str[#tbl_str + 1] = ',\n' + elseif type(val) == 'nil' then -- won't ever happen, right? + tbl_str[#tbl_str + 1] = 'nil,\n' + elseif type(val) == 'table' then + if tableshow_tbls[val] then + tbl_str[#tbl_str + 1] = tostring(val) .. ' already defined: ' .. tableshow_tbls[val] .. ',\n' + else + tableshow_tbls[val] = loc .. '[' .. UTILS.BasicSerialize(ind) .. ']' + tbl_str[#tbl_str + 1] = tostring(val) .. ' ' + tbl_str[#tbl_str + 1] = UTILS.TableShow(val, loc .. '[' .. UTILS.BasicSerialize(ind).. ']', indent .. ' ', tableshow_tbls) + tbl_str[#tbl_str + 1] = ',\n' + end + elseif type(val) == 'function' then + if debug and debug.getinfo then + local fcnname = tostring(val) + local info = debug.getinfo(val, "S") + if info.what == "C" then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', C function') .. ',\n' + else + if (string.sub(info.source, 1, 2) == [[./]]) then + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')' .. info.source) ..',\n' + else + tbl_str[#tbl_str + 1] = string.format('%q', fcnname .. ', defined in (' .. info.linedefined .. '-' .. info.lastlinedefined .. ')') ..',\n' + end + end + + else + tbl_str[#tbl_str + 1] = 'a function,\n' + end + else + tbl_str[#tbl_str + 1] = 'unable to serialize value type ' .. UTILS.BasicSerialize(type(val)) .. ' at index ' .. tostring(ind) + end + end + + tbl_str[#tbl_str + 1] = indent .. '}' + return table.concat(tbl_str) + end +end + +--- Dumps the global table _G. +-- This dumps the global table _G to a file in the DCS\Logs directory. +-- This function requires you to disable script sanitization in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io libraries. +-- @param #string fname File name. +function UTILS.Gdump(fname) + if lfs and io then + + local fdir = lfs.writedir() .. [[Logs\]] .. fname + + local f = io.open(fdir, 'w') + + f:write(UTILS.TableShow(_G)) + + f:close() + + env.info(string.format('Wrote debug data to $1', fdir)) + else + env.error("WARNING: lfs and/or io not de-sanitized - cannot dump _G!") + end +end + +--- Executes the given string. +-- borrowed from Slmod +-- @param #string s string containing LUA code. +-- @return #boolean `true` if successfully executed, `false` otherwise. +function UTILS.DoString(s) + local f, err = loadstring(s) + if f then + return true, f() + else + return false, err + end +end + UTILS.ToDegree = function(angle) return angle*180/math.pi diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 37067d492..79f794b48 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1734,6 +1734,27 @@ function CONTROLLABLE:EnRouteTaskSEAD(TargetTypes, Priority) return DCSTask end +--- (AIR) Enroute CAP task. +-- @param #CONTROLLABLE self +-- @param DCS#AttributeNameArray TargetTypes Array of target categories allowed to engage. Default `{"Air"}`. +-- @param #number Priority (Optional) All en-route tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. Default 0. +-- @return DCS#Task The DCS task structure. +function CONTROLLABLE:EnRouteTaskCAP(TargetTypes, Priority) + + local DCSTask = { + id = 'EngageTargets', + key = "CAP", + --auto = true, + enabled = true, + params = { + targetTypes = TargetTypes or {"Air"}, + priority = Priority or 0 + } + } + + return DCSTask +end + --- (AIR) Engaging a controllable. The task does not assign the target controllable to the unit/controllable to attack now; -- it just allows the unit/controllable to engage the target controllable as well as other assigned targets. -- See [hoggit](https://wiki.hoggitworld.com/view/DCS_task_engageGroup). diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 6a6f70bd9..1ef31a039 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -18,6 +18,7 @@ -- @field #string SceneryName Name of the scenery object. -- @field DCS#Object SceneryObject DCS scenery object. -- @field #number Life0 Initial life points. +-- @field #table Properties -- @extends Wrapper.Positionable#POSITIONABLE @@ -43,7 +44,7 @@ function SCENERY:Register( SceneryName, SceneryObject ) local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) ) - self.SceneryName = SceneryName + self.SceneryName = tostring(SceneryName) self.SceneryObject = SceneryObject @@ -52,9 +53,43 @@ function SCENERY:Register( SceneryName, SceneryObject ) else self.Life0 = 0 end + + self.Properties = {} + return self end +--- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists. +-- @param #SCENERY self +-- @param #string PropertyName The name of a the QuadZone Property from the scenery assignment to be retrieved. +-- @return #string The Value of the QuadZone Property from the scenery assignment with the given PropertyName, or nil if absent. +function SCENERY:GetProperty(PropertyName) + return self.Properties[PropertyName] +end + +--- Returns the scenery Properties table. +-- @param #SCENERY self +-- @return #table The Key:Value table of QuadZone properties of the zone from the scenery assignment . +function SCENERY:GetAllProperties() + return self.Properties +end + +--- Set a scenery property +-- @param #SCENERY self +-- @param #string PropertyName +-- @param #string PropertyValue +-- @return #SCENERY self +function SCENERY:SetProperty(PropertyName, PropertyValue) + self.Properties[PropertyName] = PropertyValue + return self +end +--- Obtain object name. +--@param #SCENERY self +--@return #string Name +function SCENERY:GetName() + return self.SceneryName +end + --- Obtain DCS Object from the SCENERY Object. --@param #SCENERY self --@return DCS#Object DCS scenery object. @@ -115,26 +150,28 @@ end --@param Core.Point#COORDINATE Coordinate Where to find the scenery object --@param #number Radius (optional) Search radius around coordinate, defaults to 100 --@return #SCENERY Scenery Object or `nil` if it cannot be found -function SCENERY:FindByName(Name, Coordinate, Radius) - +function SCENERY:FindByName(Name, Coordinate, Radius, Role) + local radius = Radius or 100 local name = Name or "unknown" local scenery = nil - BASE:T({name, radius, Coordinate:GetVec2()}) - --- -- @param Core.Point#COORDINATE coordinate -- @param #number radius -- @param #string name - local function SceneryScan(coordinate, radius, name) - if coordinate ~= nil then - local scenerylist = coordinate:ScanScenery(radius) - local rscenery = nil - for _,_scenery in pairs(scenerylist) do + local function SceneryScan(scoordinate, sradius, sname) + if scoordinate ~= nil then + local Vec2 = scoordinate:GetVec2() + local scanzone = ZONE_RADIUS:New("Zone-"..sname,Vec2,sradius,true) + scanzone:Scan({Object.Category.SCENERY}) + local scanned = scanzone:GetScannedSceneryObjects() + local rscenery = nil -- Wrapper.Scenery#SCENERY + for _,_scenery in pairs(scanned) do local scenery = _scenery -- Wrapper.Scenery#SCENERY - if tostring(scenery.SceneryName) == tostring(name) then + if tostring(scenery.SceneryName) == tostring(sname) then rscenery = scenery + if Role then rscenery:SetProperty("ROLE",Role) end break end end @@ -144,6 +181,7 @@ function SCENERY:FindByName(Name, Coordinate, Radius) end if Coordinate then + --BASE:I("Coordinate Scenery Scan") scenery = SceneryScan(Coordinate, radius, name) end @@ -154,7 +192,7 @@ end -- to find the correct object. --@param #SCENERY self --@param #string Name The name or id of the scenery object as taken from the ME. Ex. '595785449' ---@param Core.Zone#ZONE Zone Where to find the scenery object. Can be handed as zone name. +--@param Core.Zone#ZONE_BASE Zone Where to find the scenery object. Can be handed as zone name. --@param #number Radius (optional) Search radius around coordinate, defaults to 100 --@return #SCENERY Scenery Object or `nil` if it cannot be found function SCENERY:FindByNameInZone(Name, Zone, Radius) @@ -164,7 +202,7 @@ function SCENERY:FindByNameInZone(Name, Zone, Radius) Zone = ZONE:FindByName(Zone) end local coordinate = Zone:GetCoordinate() - return self:FindByName(Name,coordinate,Radius) + return self:FindByName(Name,coordinate,Radius,Zone:GetProperty("ROLE")) end --- Find a SCENERY object from its zone name. Since SCENERY isn't registered in the Moose database (just too many objects per map), we need to do a scan first @@ -173,38 +211,34 @@ end --@param #string ZoneName The name of the scenery zone as created with a right-click on the map in the mission editor and select "assigned to...". Can be handed over as ZONE object. --@return #SCENERY First found Scenery Object or `nil` if it cannot be found function SCENERY:FindByZoneName( ZoneName ) - local zone = ZoneName -- Core.Zone#ZONE + local zone = ZoneName -- Core.Zone#ZONE_BASE if type(ZoneName) == "string" then - zone = ZONE:FindByName(ZoneName) + zone = ZONE:FindByName(ZoneName) -- Core.Zone#ZONE_POLYGON end local _id = zone:GetProperty('OBJECT ID') - BASE:T("Object ID ".._id) + --local properties = zone:GetAllProperties() or {} + --BASE:I(string.format("Object ID is: %s",_id or "none")) + --BASE:T("Object ID ".._id) if not _id then -- this zone has no object ID BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) if string.find(zone.ClassName,"POLYGON") then zone:Scan({Object.Category.SCENERY}) - local scanned = zone:GetScannedScenery() + local scanned = zone:GetScannedSceneryObjects() for _,_scenery in (scanned) do local scenery = _scenery -- Wrapper.Scenery#SCENERY if scenery:IsAlive() then + local role = zone:GetProperty("ROLE") + if role then scenery:SetProperty("ROLE",role) end return scenery end end return nil else - local coordinate = zone:GetCoordinate() - local scanned = coordinate:ScanScenery() - for _,_scenery in (scanned) do - local scenery = _scenery -- Wrapper.Scenery#SCENERY - if scenery:IsAlive() then - return scenery - end - end - return nil + return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE")) end else - return self:FindByName(_id, zone:GetCoordinate()) + return self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE")) end end @@ -219,6 +253,7 @@ function SCENERY:FindAllByZoneName( ZoneName ) zone = ZONE:FindByName(ZoneName) end local _id = zone:GetProperty('OBJECT ID') + --local properties = zone:GetAllProperties() or {} if not _id then -- this zone has no object ID --BASE:E("**** Zone without object ID: "..ZoneName.." | Type: "..tostring(zone.ClassName)) @@ -230,7 +265,7 @@ function SCENERY:FindAllByZoneName( ZoneName ) return nil end else - local obj = self:FindByName(_id, zone:GetCoordinate()) + local obj = self:FindByName(_id, zone:GetCoordinate(),nil,zone:GetProperty("ROLE")) if obj then return {obj} else