From a8c5ccd4ad4666343404e517f1ba63e1ce35d4ba Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 23 Oct 2017 15:30:40 +0200 Subject: [PATCH] * Final version * Monitor off taxi way * Monitor maximum speed * Monitor kick speed --- Moose Development/Moose/Core/Set.lua | 399 ++++++++++++++++++ Moose Development/Moose/Core/Velocity.lua | 184 ++++++++ .../Moose/Functional/AirbasePolice.lua | 172 ++++++-- Moose Development/Moose/Tasking/Task_A2G.lua | 2 +- Moose Development/Moose/Utilities/Utils.lua | 38 +- .../Moose/Wrapper/Positionable.lua | 29 +- Moose Development/Moose/Wrapper/Unit.lua | 3 +- Moose Mission Setup/Moose.files | 1 + Moose Mission Setup/Moose.lua | 3 +- Moose Mission Setup/Moose_.lua | 3 +- 10 files changed, 766 insertions(+), 68 deletions(-) create mode 100644 Moose Development/Moose/Core/Velocity.lua diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 3f48db8b3..58f12c4f1 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -3326,6 +3326,405 @@ function SET_CLIENT:IsIncludeObject( MClient ) return MClientInclude end +--- SET_PLAYER + + +--- @type SET_PLAYER +-- @extends Core.Set#SET_BASE + + + +--- # 4) SET_PLAYER class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_PLAYER} class to build sets of units belonging to alive players: +-- +-- ## 4.1) SET_PLAYER constructor +-- +-- Create a new SET_PLAYER object with the @{#SET_PLAYER.New} method: +-- +-- * @{#SET_PLAYER.New}: Creates a new SET_PLAYER object. +-- +-- ## 4.3) SET_PLAYER filter criteria +-- +-- You can set filter criteria to define the set of clients within the SET_PLAYER. +-- Filter criteria are defined by: +-- +-- * @{#SET_PLAYER.FilterCoalitions}: Builds the SET_PLAYER with the clients belonging to the coalition(s). +-- * @{#SET_PLAYER.FilterCategories}: Builds the SET_PLAYER with the clients belonging to the category(ies). +-- * @{#SET_PLAYER.FilterTypes}: Builds the SET_PLAYER with the clients belonging to the client type(s). +-- * @{#SET_PLAYER.FilterCountries}: Builds the SET_PLAYER with the clients belonging to the country(ies). +-- * @{#SET_PLAYER.FilterPrefixes}: Builds the SET_PLAYER with the clients starting with the same prefix string(s). +-- +-- Once the filter criteria have been set for the SET_PLAYER, you can start filtering using: +-- +-- * @{#SET_PLAYER.FilterStart}: Starts the filtering of the clients within the SET_PLAYER. +-- +-- Planned filter criteria within development are (so these are not yet available): +-- +-- * @{#SET_PLAYER.FilterZones}: Builds the SET_PLAYER with the clients within a @{Zone#ZONE}. +-- +-- ## 4.4) SET_PLAYER iterators +-- +-- Once the filters have been defined and the SET_PLAYER has been built, you can iterate the SET_PLAYER with the available iterator methods. +-- The iterator methods will walk the SET_PLAYER set, and call for each element within the set a function that you provide. +-- The following iterator methods are currently available within the SET_PLAYER: +-- +-- * @{#SET_PLAYER.ForEachClient}: Calls a function for each alive client it finds within the SET_PLAYER. +-- +-- === +-- @field #SET_PLAYER SET_PLAYER +SET_PLAYER = { + ClassName = "SET_PLAYER", + Clients = {}, + Filter = { + Coalitions = nil, + Categories = nil, + Types = nil, + Countries = nil, + ClientPrefixes = nil, + }, + FilterMeta = { + Coalitions = { + red = coalition.side.RED, + blue = coalition.side.BLUE, + neutral = coalition.side.NEUTRAL, + }, + Categories = { + plane = Unit.Category.AIRPLANE, + helicopter = Unit.Category.HELICOPTER, + ground = Unit.Category.GROUND_UNIT, + ship = Unit.Category.SHIP, + structure = Unit.Category.STRUCTURE, + }, + }, +} + + +--- Creates a new SET_PLAYER object, building a set of clients belonging to a coalitions, categories, countries, types or with defined prefix names. +-- @param #SET_PLAYER self +-- @return #SET_PLAYER +-- @usage +-- -- Define a new SET_PLAYER Object. This DBObject will contain a reference to all Clients. +-- DBObject = SET_PLAYER:New() +function SET_PLAYER:New() + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.PLAYERS ) ) + + return self +end + +--- Add CLIENT(s) to SET_PLAYER. +-- @param Core.Set#SET_PLAYER self +-- @param #string AddClientNames A single name or an array of CLIENT names. +-- @return self +function SET_PLAYER:AddClientsByName( AddClientNames ) + + local AddClientNamesArray = ( type( AddClientNames ) == "table" ) and AddClientNames or { AddClientNames } + + for AddClientID, AddClientName in pairs( AddClientNamesArray ) do + self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) + end + + return self +end + +--- Remove CLIENT(s) from SET_PLAYER. +-- @param Core.Set#SET_PLAYER self +-- @param Wrapper.Client#CLIENT RemoveClientNames A single name or an array of CLIENT names. +-- @return self +function SET_PLAYER:RemoveClientsByName( RemoveClientNames ) + + local RemoveClientNamesArray = ( type( RemoveClientNames ) == "table" ) and RemoveClientNames or { RemoveClientNames } + + for RemoveClientID, RemoveClientName in pairs( RemoveClientNamesArray ) do + self:Remove( RemoveClientName.ClientName ) + end + + return self +end + + +--- Finds a Client based on the Player Name. +-- @param #SET_PLAYER self +-- @param #string PlayerName +-- @return Wrapper.Client#CLIENT The found Client. +function SET_PLAYER:FindClient( PlayerName ) + + local ClientFound = self.Set[PlayerName] + return ClientFound +end + + + +--- Builds a set of clients of coalitions joined by specific players. +-- Possible current coalitions are red, blue and neutral. +-- @param #SET_PLAYER self +-- @param #string Coalitions Can take the following values: "red", "blue", "neutral". +-- @return #SET_PLAYER self +function SET_PLAYER:FilterCoalitions( Coalitions ) + if not self.Filter.Coalitions then + self.Filter.Coalitions = {} + end + if type( Coalitions ) ~= "table" then + Coalitions = { Coalitions } + end + for CoalitionID, Coalition in pairs( Coalitions ) do + self.Filter.Coalitions[Coalition] = Coalition + end + return self +end + + +--- Builds a set of clients out of categories joined by players. +-- Possible current categories are plane, helicopter, ground, ship. +-- @param #SET_PLAYER self +-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". +-- @return #SET_PLAYER self +function SET_PLAYER:FilterCategories( Categories ) + if 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 + + +--- Builds a set of clients of defined client types joined by players. +-- Possible current types are those types known within DCS world. +-- @param #SET_PLAYER self +-- @param #string Types Can take those type strings known within DCS world. +-- @return #SET_PLAYER self +function SET_PLAYER:FilterTypes( Types ) + if not self.Filter.Types then + self.Filter.Types = {} + end + if type( Types ) ~= "table" then + Types = { Types } + end + for TypeID, Type in pairs( Types ) do + self.Filter.Types[Type] = Type + end + return self +end + + +--- Builds a set of clients of defined countries. +-- Possible current countries are those known within DCS world. +-- @param #SET_PLAYER self +-- @param #string Countries Can take those country strings known within DCS world. +-- @return #SET_PLAYER self +function SET_PLAYER:FilterCountries( Countries ) + if not self.Filter.Countries then + self.Filter.Countries = {} + end + if type( Countries ) ~= "table" then + Countries = { Countries } + end + for CountryID, Country in pairs( Countries ) do + self.Filter.Countries[Country] = Country + end + return self +end + + +--- Builds a set of clients of defined client prefixes. +-- All the clients starting with the given prefixes will be included within the set. +-- @param #SET_PLAYER self +-- @param #string Prefixes The prefix of which the client name starts with. +-- @return #SET_PLAYER self +function SET_PLAYER:FilterPrefixes( Prefixes ) + if not self.Filter.ClientPrefixes then + self.Filter.ClientPrefixes = {} + end + if type( Prefixes ) ~= "table" then + Prefixes = { Prefixes } + end + for PrefixID, Prefix in pairs( Prefixes ) do + self.Filter.ClientPrefixes[Prefix] = Prefix + end + return self +end + + + + +--- Starts the filtering. +-- @param #SET_PLAYER self +-- @return #SET_PLAYER self +function SET_PLAYER:FilterStart() + + if _DATABASE then + self:_FilterStart() + end + + return self +end + +--- Handles the Database to check on an event (birth) that the Object was added in the Database. +-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event! +-- @param #SET_PLAYER self +-- @param Core.Event#EVENTDATA Event +-- @return #string The name of the CLIENT +-- @return #table The CLIENT +function SET_PLAYER:AddInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] +end + +--- Handles the Database to check on any event that Object exists in the Database. +-- This is required, because sometimes the _DATABASE event gets called later than the SET_BASE event or vise versa! +-- @param #SET_PLAYER self +-- @param Core.Event#EVENTDATA Event +-- @return #string The name of the CLIENT +-- @return #table The CLIENT +function SET_PLAYER:FindInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] +end + +--- Iterate the SET_PLAYER and call an interator function for each **alive** CLIENT, providing the CLIENT and optional parameters. +-- @param #SET_PLAYER self +-- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. +-- @return #SET_PLAYER self +function SET_PLAYER:ForEachPlayer( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self.Set ) + + return self +end + +--- Iterate the SET_PLAYER and call an iterator function for each **alive** CLIENT presence completely in a @{Zone}, providing the CLIENT and optional parameters to the called function. +-- @param #SET_PLAYER self +-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. +-- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. +-- @return #SET_PLAYER self +function SET_PLAYER:ForEachPlayerInZone( ZoneObject, IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self.Set, + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Client#CLIENT ClientObject + function( ZoneObject, ClientObject ) + if ClientObject:IsInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) + + return self +end + +--- Iterate the SET_PLAYER and call an iterator function for each **alive** CLIENT presence not in a @{Zone}, providing the CLIENT and optional parameters to the called function. +-- @param #SET_PLAYER self +-- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. +-- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_PLAYER. The function needs to accept a CLIENT parameter. +-- @return #SET_PLAYER self +function SET_PLAYER:ForEachPlayerNotInZone( ZoneObject, IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self.Set, + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Client#CLIENT ClientObject + function( ZoneObject, ClientObject ) + if ClientObject:IsNotInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) + + return self +end + +--- +-- @param #SET_PLAYER self +-- @param Wrapper.Client#CLIENT MClient +-- @return #SET_PLAYER self +function SET_PLAYER:IsIncludeObject( MClient ) + self:F2( MClient ) + + local MClientInclude = true + + if MClient then + local MClientName = MClient.UnitName + + if self.Filter.Coalitions then + local MClientCoalition = false + for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do + local ClientCoalitionID = _DATABASE:GetCoalitionFromClientTemplate( MClientName ) + self:T3( { "Coalition:", ClientCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == ClientCoalitionID then + MClientCoalition = true + end + end + self:T( { "Evaluated Coalition", MClientCoalition } ) + MClientInclude = MClientInclude and MClientCoalition + end + + if self.Filter.Categories then + local MClientCategory = false + for CategoryID, CategoryName in pairs( self.Filter.Categories ) do + local ClientCategoryID = _DATABASE:GetCategoryFromClientTemplate( MClientName ) + self:T3( { "Category:", ClientCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == ClientCategoryID then + MClientCategory = true + end + end + self:T( { "Evaluated Category", MClientCategory } ) + MClientInclude = MClientInclude and MClientCategory + end + + if self.Filter.Types then + local MClientType = false + for TypeID, TypeName in pairs( self.Filter.Types ) do + self:T3( { "Type:", MClient:GetTypeName(), TypeName } ) + if TypeName == MClient:GetTypeName() then + MClientType = true + end + end + self:T( { "Evaluated Type", MClientType } ) + MClientInclude = MClientInclude and MClientType + end + + if self.Filter.Countries then + local MClientCountry = false + for CountryID, CountryName in pairs( self.Filter.Countries ) do + local ClientCountryID = _DATABASE:GetCountryFromClientTemplate(MClientName) + self:T3( { "Country:", ClientCountryID, country.id[CountryName], CountryName } ) + if country.id[CountryName] and country.id[CountryName] == ClientCountryID then + MClientCountry = true + end + end + self:T( { "Evaluated Country", MClientCountry } ) + MClientInclude = MClientInclude and MClientCountry + end + + if self.Filter.ClientPrefixes then + local MClientPrefix = false + for ClientPrefixId, ClientPrefix in pairs( self.Filter.ClientPrefixes ) do + self:T3( { "Prefix:", string.find( MClient.UnitName, ClientPrefix, 1 ), ClientPrefix } ) + if string.find( MClient.UnitName, ClientPrefix, 1 ) then + MClientPrefix = true + end + end + self:T( { "Evaluated Prefix", MClientPrefix } ) + MClientInclude = MClientInclude and MClientPrefix + end + end + + self:T2( MClientInclude ) + return MClientInclude +end + --- @type SET_AIRBASE -- @extends Core.Set#SET_BASE diff --git a/Moose Development/Moose/Core/Velocity.lua b/Moose Development/Moose/Core/Velocity.lua new file mode 100644 index 000000000..333ad0f88 --- /dev/null +++ b/Moose Development/Moose/Core/Velocity.lua @@ -0,0 +1,184 @@ +--- **Core** -- VELOCITY models a speed, which can be expressed in various formats according the Settings. +-- +-- === +-- +-- ### Author: **Sven Van de Velde (FlightControl)** +-- ### Contributions: +-- +-- ==== +-- +-- @module Base + +do -- Velocity + + --- @type VELOCITY + -- @extends Core.Base#BASE + + + --- # VELOCITY class, extends @{Base#BASE} + -- + -- VELOCITY models a speed, which can be expressed in various formats according the Settings. + -- + -- ## 1. VELOCITY constructor + -- + -- * @{#VELOCITY.New}(): Creates a new VELOCITY object. + -- + -- @field #VELOCITY + VELOCITY = { + ClassName = "VELOCITY", + } + + --- VELOCITY Constructor. + -- @param #VELOCITY self + -- @param #number VelocityMps The velocity in meters per second. + -- @return #VELOCITY + function VELOCITY:New( VelocityMps ) + local self = BASE:Inherit( self, BASE:New() ) -- #VELOCITY + self:F( {} ) + self.Velocity = VelocityMps + return self + end + + --- Set the velocity in Mps (meters per second). + -- @param #VELOCITY self + -- @param #number VelocityMps The velocity in meters per second. + -- @return #VELOCITY + function VELOCITY:Set( VelocityMps ) + self.Velocity = VelocityMps + return self + end + + --- Get the velocity in Mps (meters per second). + -- @param #VELOCITY self + -- @return #number The velocity in meters per second. + function VELOCITY:Get() + return self.Velocity + end + + --- Set the velocity in Kmph (kilometers per hour). + -- @param #VELOCITY self + -- @param #number VelocityKmph The velocity in kilometers per hour. + -- @return #VELOCITY + function VELOCITY:SetKmph( VelocityKmph ) + self.Velocity = UTILS.KmphToMps( VelocityKmph ) + return self + end + + --- Get the velocity in Kmph (kilometers per hour). + -- @param #VELOCITY self + -- @return #number The velocity in kilometers per hour. + function VELOCITY:GetKmph() + + return UTILS.MpsToKmph( self.Velocity ) + end + + --- Set the velocity in Miph (miles per hour). + -- @param #VELOCITY self + -- @param #number VelocityMiph The velocity in miles per hour. + -- @return #VELOCITY + function VELOCITY:SetMiph( VelocityMiph ) + self.Velocity = UTILS.MiphToMps( VelocityMiph ) + return self + end + + --- Get the velocity in Miph (miles per hour). + -- @param #VELOCITY self + -- @return #number The velocity in miles per hour. + function VELOCITY:GetMiph() + return UTILS.MpsToMiph( self.Velocity ) + end + + + --- Get the velocity in text, according the player @{Settings}. + -- @param #VELOCITY self + -- @param Core.Settings#SETTINGS Settings + -- @return #string The velocity in text. + function VELOCITY:GetText( Settings ) + local Settings = Settings or _SETTINGS + if self.Velocity ~= 0 then + if Settings:IsMetric() then + return string.format( "%d km/h", UTILS.MpsToKmph( self.Velocity ) ) + else + return string.format( "%d mi/h", UTILS.MpsToMiph( self.Velocity ) ) + end + else + return "stationary" + end + end + + --- Get the velocity in text, according the player or default @{Settings}. + -- @param #VELOCITY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable + -- @param Core.Settings#SETTINGS Settings + -- @return #string The velocity in text according the player or default @{Settings} + function VELOCITY:ToString( VelocityGroup, Settings ) -- R2.3 + self:F( { Group = VelocityGroup and VelocityGroup:GetName() } ) + local Settings = Settings or ( VelocityGroup and _DATABASE:GetPlayerSettings( VelocityGroup:GetPlayerName() ) ) or _SETTINGS + return self:GetText( Settings ) + end + +end + +do -- VELOCITY_POSITIONABLE + + --- @type VELOCITY_POSITIONABLE + -- @extends Core.Base#BASE + + + --- # VELOCITY_POSITIONABLE class, extends @{Base#BASE} + -- + -- VELOCITY_POSITIONABLE monitors the speed of an @{Positionable} in the simulation, which can be expressed in various formats according the Settings. + -- + -- ## 1. VELOCITY_POSITIONABLE constructor + -- + -- * @{#VELOCITY_POSITIONABLE.New}(): Creates a new VELOCITY_POSITIONABLE object. + -- + -- @field #VELOCITY_POSITIONABLE + VELOCITY_POSITIONABLE = { + ClassName = "VELOCITY_POSITIONABLE", + } + + --- VELOCITY_POSITIONABLE Constructor. + -- @param #VELOCITY_POSITIONABLE self + -- @param Wrapper.Positionable#POSITIONABLE Positionable The Positionable to monitor the speed. + -- @return #VELOCITY_POSITIONABLE + function VELOCITY_POSITIONABLE:New( Positionable ) + local self = BASE:Inherit( self, VELOCITY:New() ) -- #VELOCITY_POSITIONABLE + self:F( {} ) + self.Positionable = Positionable + return self + end + + --- Get the velocity in Mps (meters per second). + -- @param #VELOCITY_POSITIONABLE self + -- @return #number The velocity in meters per second. + function VELOCITY_POSITIONABLE:Get() + return self.Positionable:GetVelocityMPS() or 0 + end + + --- Get the velocity in Kmph (kilometers per hour). + -- @param #VELOCITY_POSITIONABLE self + -- @return #number The velocity in kilometers per hour. + function VELOCITY_POSITIONABLE:GetKmph() + + return UTILS.MpsToKmph( self.Positionable:GetVelocityMPS() or 0) + end + + --- Get the velocity in Miph (miles per hour). + -- @param #VELOCITY_POSITIONABLE self + -- @return #number The velocity in miles per hour. + function VELOCITY_POSITIONABLE:GetMiph() + return UTILS.MpsToMiph( self.Positionable:GetVelocityMPS() or 0 ) + end + + --- Get the velocity in text, according the player or default @{Settings}. + -- @param #VELOCITY_POSITIONABLE self + -- @return #string The velocity in text according the player or default @{Settings} + function VELOCITY_POSITIONABLE:ToString() -- R2.3 + self:F( { Group = self.Positionable and self.Positionable:GetName() } ) + local Settings = Settings or ( self.Positionable and _DATABASE:GetPlayerSettings( self.Positionable:GetPlayerName() ) ) or _SETTINGS + self.Velocity = self.Positionable:GetVelocityMPS() + return self:GetText( Settings ) + end + +end \ No newline at end of file diff --git a/Moose Development/Moose/Functional/AirbasePolice.lua b/Moose Development/Moose/Functional/AirbasePolice.lua index e764123af..2c8b30069 100644 --- a/Moose Development/Moose/Functional/AirbasePolice.lua +++ b/Moose Development/Moose/Functional/AirbasePolice.lua @@ -29,19 +29,19 @@ AIRBASEPOLICE_BASE = { --- Creates a new AIRBASEPOLICE_BASE object. -- @param #AIRBASEPOLICE_BASE self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. -- @param Airbases A table of Airbase Names. -- @return #AIRBASEPOLICE_BASE self -function AIRBASEPOLICE_BASE:New( SetClient, Airbases, AirbaseList ) +function AIRBASEPOLICE_BASE:New( Airbases, AirbaseList ) -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) - self:E( { self.ClassName, SetClient, Airbases } ) + local self = BASE:Inherit( self, BASE:New() ) -- #AIRBASEPOLICE_BASE + self:E( { self.ClassName, Airbases } ) - self.SetClient = SetClient self.Airbases = Airbases - self.AirbaseList = AirbaseList + + self.SetClient = SET_CLIENT:New():FilterCategories( "plane" ):FilterStart() + for AirbaseID, Airbase in pairs( self.Airbases ) do Airbase.ZoneBoundary = _DATABASE:FindAirbase( AirbaseID ):GetZone() @@ -69,15 +69,19 @@ function AIRBASEPOLICE_BASE:New( SetClient, Airbases, AirbaseList ) function( Client ) Client:SetState( self, "Speeding", false ) Client:SetState( self, "Warnings", 0) + Client:SetState( self, "IsOffRunway", false ) + Client:SetState( self, "OffRunwayWarnings", 0 ) Client:SetState( self, "Taxi", false ) end ) - self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, {}, 0, 2, 0.05 ) + self.AirbaseMonitor = SCHEDULER:New( self, self._AirbaseMonitor, {self }, 0, 2, 0.05 ) -- This is simple slot blocker is used on the server. SSB = USERFLAG:New( "SSB" ) SSB:Set( 100 ) + + self:SetKickSpeedKmph( 100 ) return self end @@ -97,14 +101,36 @@ function AIRBASEPOLICE_BASE:SmokeRunways( SmokeColor ) end +--- Set the maximum speed in Kmph until the player gets kicked. +-- @param #AIRBASEPOLICE_BASE self +-- @param #number KickSpeed Set the maximum speed in Kmph until the player gets kicked. +-- @return #AIRBASEPOLICE_BASE self +function AIRBASEPOLICE_BASE:SetKickSpeedKmph( KickSpeed ) + + self.KickSpeed = UTILS.KmphToMps( KickSpeed ) +end + +--- Set the maximum speed in Miph until the player gets kicked. +-- @param #AIRBASEPOLICE_BASE self +-- @param #number KickSpeedMiph Set the maximum speed in Mph until the player gets kicked. +-- @return #AIRBASEPOLICE_BASE self +function AIRBASEPOLICE_BASE:SetKickSpeedMiph( KickSpeedMiph ) + + self.KickSpeed = UTILS.MiphToMps( KickSpeedMiph ) +end + + + --- @param #AIRBASEPOLICE_BASE self function AIRBASEPOLICE_BASE:_AirbaseMonitor() + self:E( "In Scheduler") + for AirbaseID, AirbaseMeta in pairs( self.Airbases ) do if AirbaseMeta.Monitor == true then - self:E( AirbaseID ) + self:E( AirbaseID, AirbaseMeta.MaximumSpeed ) self.SetClient:ForEachClientInZone( AirbaseMeta.ZoneBoundary, @@ -112,32 +138,40 @@ function AIRBASEPOLICE_BASE:_AirbaseMonitor() function( Client ) self:E( Client.UnitName ) - if Client:IsAlive() then + if Client and Client:IsAlive() then local NotInRunwayZone = true for ZoneRunwayID, ZoneRunway in pairs( AirbaseMeta.ZoneRunways ) do NotInRunwayZone = ( Client:IsNotInZone( ZoneRunway ) == true ) and NotInRunwayZone or false end if NotInRunwayZone then - local Taxi = self:GetState( self, "Taxi" ) + local Taxi = Client:GetState( self, "Taxi" ) self:E( Taxi ) if Taxi == false then - Client:Message( "Welcome at " .. AirbaseID .. ". The maximum taxiing speed is " .. AirbaseMeta.MaximumSpeed " km/h.", 20, "ATC" ) - self:SetState( self, "Taxi", true ) + Client:Message( "Welcome at " .. AirbaseID .. ". The maximum taxiing speed is " .. AirbaseMeta.MaximumSpeed .. " km/h.", 20, "ATC" ) + Client:SetState( self, "Taxi", true ) end -- TODO: GetVelocityKMH function usage - local VelocityVec3 = Client:GetVelocity() - local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec - local Velocity = Velocity * 3.6 -- now it is in km/h. - -- MESSAGE:New( "Velocity = " .. Velocity, 1 ):ToAll() + local Velocity = VELOCITY_POSITIONABLE:New( Client ) + --MESSAGE:New( "Velocity = " .. Velocity:ToString(), 1 ):ToAll() local IsAboveRunway = Client:IsAboveRunway() local IsOnGround = Client:InAir() == false self:T( IsAboveRunway, IsOnGround ) - if IsAboveRunway and IsOnGround then + if IsOnGround then + if Velocity:Get() > self.KickSpeed then + MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " is kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() + Client:Destroy() + Client:SetState( self, "Speeding", false ) + Client:SetState( self, "Warnings", 0 ) + end + end + - if Velocity > AirbaseMeta.MaximumSpeed then + if IsOnGround then + + if Velocity:GetKmph() > AirbaseMeta.MaximumSpeed then local IsSpeeding = Client:GetState( self, "Speeding" ) if IsSpeeding == true then @@ -146,7 +180,7 @@ function AIRBASEPOLICE_BASE:_AirbaseMonitor() if SpeedingWarnings <= 3 then Client:Message( "Warning " .. SpeedingWarnings .. "/3! Airbase traffic rule violation! Slow down now! Your speed is " .. - string.format( "%2.0f km/h", Velocity ), 5, "ATC" ) + string.format( "%s", Velocity:ToString() ), 5, "ATC" ) Client:SetState( self, "Warnings", SpeedingWarnings + 1 ) else MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " is kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() @@ -157,7 +191,7 @@ function AIRBASEPOLICE_BASE:_AirbaseMonitor() end else - Client:Message( "Attention! You are speeding on the taxiway, slow down! Your speed is " .. string.format( "%2.0f km/h", Velocity ), 5, "ATC" ) + Client:Message( "Attention! You are speeding on the taxiway, slow down! Your speed is " .. string.format( "%s", Velocity:ToString() ), 5, "ATC" ) Client:SetState( self, "Speeding", true ) Client:SetState( self, "Warnings", 1 ) end @@ -168,13 +202,45 @@ function AIRBASEPOLICE_BASE:_AirbaseMonitor() end end + if IsOnGround and not IsAboveRunway then + + local IsOffRunway = Client:GetState( self, "IsOffRunway" ) + + if IsOffRunway == true then + local OffRunwayWarnings = Client:GetState( self, "OffRunwayWarnings" ) + self:T( OffRunwayWarnings ) + + if OffRunwayWarnings <= 3 then + Client:Message( "Warning " .. OffRunwayWarnings .. "/3! Airbase traffic rule violation! Get back on the taxi immediately!", 5, "ATC" ) + Client:SetState( self, "OffRunwayWarnings", OffRunwayWarnings + 1 ) + else + MESSAGE:New( "Penalty! Player " .. Client:GetPlayerName() .. " is kicked, due to a severe airbase traffic rule violation ...", 10, "ATC" ):ToAll() + --- @param Wrapper.Client#CLIENT Client + Client:Destroy() + Client:SetState( self, "IsOffRunway", false ) + Client:SetState( self, "OffRunwayWarnings", 0 ) + end + else + Client:Message( "Attention! You are off the taxiway. Get back on the taxiway immediately!", 5, "ATC" ) + Client:SetState( self, "IsOffRunway", true ) + Client:SetState( self, "OffRunwayWarnings", 1 ) + end + + else + Client:SetState( self, "IsOffRunway", false ) + Client:SetState( self, "OffRunwayWarnings", 0 ) + end + + else Client:SetState( self, "Speeding", false ) Client:SetState( self, "Warnings", 0 ) - local Taxi = self:GetState( self, "Taxi" ) + Client:SetState( self, "IsOffRunway", false ) + Client:SetState( self, "OffRunwayWarnings", 0 ) + local Taxi = Client:GetState( self, "Taxi" ) if Taxi == true then Client:Message( "You have progressed to the runway ... Await take-off clearance ...", 20, "ATC" ) - self:SetState( self, "Taxi", false ) + Client:SetState( self, "Taxi", false ) end end end @@ -257,11 +323,8 @@ end -- -- -- This creates a new AIRBASEPOLICE_CAUCASUS object. -- --- -- Create a set of all clients in the mission. --- AllClientsSet = SET_CLIENT:New():FilterStart() --- -- -- Monitor for these clients the airbases. --- AirbasePoliceCaucasus = AIRBASEPOLICE_CAUCASUS:New( AllClientsSet ) +-- AirbasePoliceCaucasus = AIRBASEPOLICE_CAUCASUS:New() -- -- @field #AIRBASEPOLICE_CAUCASUS AIRBASEPOLICE_CAUCASUS = { @@ -554,13 +617,12 @@ AIRBASEPOLICE_CAUCASUS = { --- Creates a new AIRBASEPOLICE_CAUCASUS object. -- @param #AIRBASEPOLICE_CAUCASUS self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. -- @param AirbaseNames A list {} of airbase names (Use AIRBASE.Caucasus enumerator). -- @return #AIRBASEPOLICE_CAUCASUS self -function AIRBASEPOLICE_CAUCASUS:New( SetClient, AirbaseNames ) +function AIRBASEPOLICE_CAUCASUS:New( AirbaseNames ) -- Inherits from BASE - local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( SetClient, self.Airbases, AirbaseNames ) ) + local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( self.Airbases, AirbaseNames ) ) @@ -828,16 +890,25 @@ end -- -- -- This creates a new AIRBASEPOLICE_NEVADA object. -- --- -- Create a set of all clients in the mission. --- AllClientsSet = SET_CLIENT:New():FilterStart() --- -- -- Monitor for these clients the airbases. --- AirbasePoliceCaucasus = AIRBASEPOLICE_NEVADA:New( AllClientsSet ) +-- AirbasePoliceCaucasus = AIRBASEPOLICE_NEVADA:New() -- -- @field #AIRBASEPOLICE_NEVADA AIRBASEPOLICE_NEVADA = { ClassName = "AIRBASEPOLICE_NEVADA", Airbases = { + + [AIRBASE.Nevada.Beatty_Airport] = { + PointsRunways = { + [1] = { + [1]={["y"]=-174950.05857143,["x"]=-329679.65,}, + [2]={["y"]=-174946.53828571,["x"]=-331394.03885715,}, + [3]={["y"]=-174967.10971429,["x"]=-331394.32457143,}, + [4]={["y"]=-174971.01828571,["x"]=-329682.59171429,}, + }, + }, + MaximumSpeed = 50, + }, [AIRBASE.Nevada.Boulder_City_Airport] = { PointsRunways = { [1] = { @@ -951,6 +1022,17 @@ AIRBASEPOLICE_NEVADA = { }, MaximumSpeed = 50, }, + [AIRBASE.Nevada.Lincoln_County] = { + PointsRunways = { + [1] = { + [1]={["y"]=33222.34171429,["x"]=-223959.40171429,}, + [2]={["y"]=33200.040000004,["x"]=-225369.36828572,}, + [3]={["y"]=33177.634571428,["x"]=-225369.21485715,}, + [4]={["y"]=33201.198857147,["x"]=-223960.54457143,}, + }, + }, + MaximumSpeed = 50, + }, [AIRBASE.Nevada.McCarran_International_Airport] = { PointsRunways = { [1] = { @@ -1085,13 +1167,12 @@ AIRBASEPOLICE_NEVADA = { --- Creates a new AIRBASEPOLICE_NEVADA object. -- @param #AIRBASEPOLICE_NEVADA self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. -- @param AirbaseNames A list {} of airbase names (Use AIRBASE.Nevada enumerator). -- @return #AIRBASEPOLICE_NEVADA self -function AIRBASEPOLICE_NEVADA:New( SetClient, AirbaseNames ) +function AIRBASEPOLICE_NEVADA:New( AirbaseNames ) -- Inherits from BASE - local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( SetClient, self.Airbases, AirbaseNames ) ) + local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( self.Airbases, AirbaseNames ) ) @@ -1107,6 +1188,13 @@ function AIRBASEPOLICE_NEVADA:New( SetClient, AirbaseNames ) --[[ + -- Beatty + do + local VillagePrefix = "Beatty" + local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) + local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() + end + -- Boulder do local VillagePrefix = "Boulder" @@ -1168,7 +1256,7 @@ function AIRBASEPOLICE_NEVADA:New( SetClient, AirbaseNames ) -- Lincoln do - local VillagePrefix = "Laughlin" + local VillagePrefix = "Lincoln" local Runway1 = GROUP:FindByName( VillagePrefix .. " 1" ) local Zone1 = ZONE_POLYGON:New( VillagePrefix .. " 1", Runway1 ):SmokeZone(SMOKECOLOR.Red):Flush() end @@ -1299,11 +1387,8 @@ end -- -- -- This creates a new AIRBASEPOLICE_NORMANDY object. -- --- -- Create a set of all clients in the mission. --- AllClientsSet = SET_CLIENT:New():FilterStart() --- -- -- Monitor for these clients the airbases. --- AirbasePoliceCaucasus = AIRBASEPOLICE_NORMANDY:New( AllClientsSet ) +-- AirbasePoliceCaucasus = AIRBASEPOLICE_NORMANDY:New() -- -- @field #AIRBASEPOLICE_NORMANDY AIRBASEPOLICE_NORMANDY = { @@ -1701,13 +1786,12 @@ AIRBASEPOLICE_NORMANDY = { --- Creates a new AIRBASEPOLICE_NORMANDY object. -- @param #AIRBASEPOLICE_NORMANDY self --- @param SetClient A SET_CLIENT object that will contain the CLIENT objects to be monitored if they follow the rules of the airbase. -- @param AirbaseNames A list {} of airbase names (Use AIRBASE.Normandy enumerator). -- @return #AIRBASEPOLICE_NORMANDY self -function AIRBASEPOLICE_NORMANDY:New( SetClient, AirbaseNames ) +function AIRBASEPOLICE_NORMANDY:New( AirbaseNames ) -- Inherits from BASE - local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( SetClient, self.Airbases, AirbaseNames ) ) + local self = BASE:Inherit( self, AIRBASEPOLICE_BASE:New( self.Airbases, AirbaseNames ) ) -- These lines here are for the demonstration mission. -- They create in the dcs.log the coordinates of the runway polygons, that are then diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index e07579827..02e442d2c 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -544,7 +544,7 @@ do -- TASK_A2G_BAI local TargetCoordinate = self:GetInfo( "Coordinate" ) -- Core.Point#COORDINATE - local Velocity = self.TargetSetUnit:GetVelocity() + local Velocity = self.TargetSetUnit:GetVelocityVec3() local Heading = self.TargetSetUnit:GetHeading() TargetCoordinate:SetHeading( Heading ) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index c2fb6e081..cd54b4e1f 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -236,26 +236,36 @@ UTILS.FeetToMeters = function(feet) return feet*0.3048 end -UTILS.MpsToKnots = function(mps) - return mps*3600/1852 -end - -UTILS.MpsToKmph = function(mps) - return mps*3.6 -end - -UTILS.KnotsToMps = function(knots) - return knots*1852/3600 -end - UTILS.KnotsToKmph = function(knots) return knots* 1.852 end -UTILS.KmphToMps = function(kmph) - return kmph/3.6 +UTILS.KmphToMps = function( kmph ) + return kmph / 3.6 end +UTILS.MpsToKmph = function( mps ) + return mps * 3.6 +end + +UTILS.MiphToMps = function( miph ) + return miph * 0.44704 +end + +UTILS.MpsToMiph = function( mps ) + return mps / 0.44704 +end + +UTILS.MpsToKnots = function( mps ) + return mps * 3600 / 1852 +end + +UTILS.KnotsToMps = function( knots ) + return knots * 1852 / 3600 +end + + + --[[acc: in DM: decimal point of minutes. In DMS: decimal point of seconds. diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 177875c89..d349211b0 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -326,16 +326,35 @@ function POSITIONABLE:InAir() return nil end - ---- Returns the POSITIONABLE velocity vector. + +--- Returns the a @{Velocity} object from the positionable. -- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec3 The velocity vector +-- @return Core.Velocity#VELOCITY Velocity The Velocity object. -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetVelocity() self:F2( self.PositionableName ) local DCSPositionable = self:GetDCSObject() + if DCSPositionable then + local Velocity = VELOCITY:New( self ) + return Velocity + end + + return nil +end + + + +--- Returns the POSITIONABLE velocity Vec3 vector. +-- @param Wrapper.Positionable#POSITIONABLE self +-- @return Dcs.DCSTypes#Vec3 The velocity Vec3 vector +-- @return #nil The POSITIONABLE is not existing or alive. +function POSITIONABLE:GetVelocityVec3() + self:F2( self.PositionableName ) + + local DCSPositionable = self:GetDCSObject() + if DCSPositionable then local PositionableVelocityVec3 = DCSPositionable:getVelocity() self:T3( PositionableVelocityVec3 ) @@ -377,7 +396,7 @@ function POSITIONABLE:GetVelocityKMH() local DCSPositionable = self:GetDCSObject() if DCSPositionable then - local VelocityVec3 = self:GetVelocity() + local VelocityVec3 = self:GetVelocityVec3() local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec local Velocity = Velocity * 3.6 -- now it is in km/h. self:T3( Velocity ) @@ -396,7 +415,7 @@ function POSITIONABLE:GetVelocityMPS() local DCSPositionable = self:GetDCSObject() if DCSPositionable then - local VelocityVec3 = self:GetVelocity() + local VelocityVec3 = self:GetVelocityVec3() local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec self:T3( Velocity ) return Velocity diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 237565886..31eaf98ef 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -761,10 +761,9 @@ function UNIT:IsInZone( Zone ) if self:IsAlive() then local IsInZone = Zone:IsVec3InZone( self:GetVec3() ) - self:T2( { IsInZone } ) + self:E( { Unit = self.UnitName, IsInZone = IsInZone } ) return IsInZone end - return false end diff --git a/Moose Mission Setup/Moose.files b/Moose Mission Setup/Moose.files index 657ffe1bd..4d9f53b43 100644 --- a/Moose Mission Setup/Moose.files +++ b/Moose Mission Setup/Moose.files @@ -14,6 +14,7 @@ Core/Zone.lua Core/Database.lua Core/Set.lua Core/Point.lua +Core/Velocity.lua Core/Message.lua Core/Fsm.lua Core/Radio.lua diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index f5c80b970..e88e8bf87 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20171021_1203' ) +env.info( 'Moose Generation Timestamp: 20171023_1007' ) local base = _G @@ -36,6 +36,7 @@ __Moose.Include( 'Core/Zone.lua' ) __Moose.Include( 'Core/Database.lua' ) __Moose.Include( 'Core/Set.lua' ) __Moose.Include( 'Core/Point.lua' ) +__Moose.Include( 'Core/Velocity.lua' ) __Moose.Include( 'Core/Message.lua' ) __Moose.Include( 'Core/Fsm.lua' ) __Moose.Include( 'Core/Radio.lua' ) diff --git a/Moose Mission Setup/Moose_.lua b/Moose Mission Setup/Moose_.lua index f76d65845..1cdef6beb 100644 --- a/Moose Mission Setup/Moose_.lua +++ b/Moose Mission Setup/Moose_.lua @@ -1,5 +1,5 @@ env.info('*** MOOSE DYNAMIC INCLUDE START *** ') -env.info('Moose Generation Timestamp: 20171021_1203') +env.info('Moose Generation Timestamp: 20171023_1007') local base=_G __Moose={} __Moose.Include=function(IncludeFile) @@ -31,6 +31,7 @@ __Moose.Include('Core/Zone.lua') __Moose.Include('Core/Database.lua') __Moose.Include('Core/Set.lua') __Moose.Include('Core/Point.lua') +__Moose.Include('Core/Velocity.lua') __Moose.Include('Core/Message.lua') __Moose.Include('Core/Fsm.lua') __Moose.Include('Core/Radio.lua')